From b80dd7788492ac15200be72dbaeff8601d2eb54c Mon Sep 17 00:00:00 2001 From: Xiwen Cheng Date: Thu, 26 Sep 2024 23:57:54 +0200 Subject: [PATCH] Refactor table and open document in editor if possible --- MxLintPaneExtension.cs | 7 +- MxLintPaneExtensionWebViewModel.cs | 84 ++- resources/App/App.mpr | Bin 10444800 -> 10452992 bytes .../MendixCLIExtension.deps.json | 197 +++++++ .../MxLintExtension/MendixCLIExtension.dll | Bin 0 -> 25088 bytes .../MxLintExtension/MendixCLIExtension.pdb | Bin 0 -> 16040 bytes .../MxLintExtension/MxLintExtension.dll | Bin 25088 -> 28160 bytes .../MxLintExtension/MxLintExtension.pdb | Bin 16016 -> 16620 bytes .../MxLintExtension/wwwroot/api-sample.json | 0 .../MxLintExtension/wwwroot/index.html | 38 +- .../MxLintExtension/wwwroot/main.js | 190 ++++--- .../MyFirstLogic.Microflows$Microflow.yaml | 56 ++ .../modelsource/Security$ProjectSecurity.yaml | 2 +- resources/App/project-settings.user.json | 2 +- wwwroot/api-sample.json | 493 ++++++++++++++++++ wwwroot/index.html | 36 +- wwwroot/main.js | 190 ++++--- 17 files changed, 1061 insertions(+), 234 deletions(-) create mode 100644 resources/App/extensions/MxLintExtension/MendixCLIExtension.deps.json create mode 100644 resources/App/extensions/MxLintExtension/MendixCLIExtension.dll create mode 100644 resources/App/extensions/MxLintExtension/MendixCLIExtension.pdb rename wwwroot/api => resources/App/extensions/MxLintExtension/wwwroot/api-sample.json (100%) create mode 100644 wwwroot/api-sample.json diff --git a/MxLintPaneExtension.cs b/MxLintPaneExtension.cs index c19a31b..21cafe9 100644 --- a/MxLintPaneExtension.cs +++ b/MxLintPaneExtension.cs @@ -1,5 +1,6 @@ using System.ComponentModel.Composition; using Mendix.StudioPro.ExtensionsAPI.Services; +using Mendix.StudioPro.ExtensionsAPI.UI.Services; using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; namespace com.cinaq.MxLintExtension; @@ -11,15 +12,17 @@ public class MxLintPaneExtension : DockablePaneExtension public const string ID = "com-cinaq-mxlint-extension"; public override string Id => ID; + private readonly IDockingWindowService _dockingWindowService; [ImportingConstructor] - public MxLintPaneExtension(ILogService logService) + public MxLintPaneExtension(IDockingWindowService dockingWindowService, ILogService logService) { _logService = logService; + _dockingWindowService = dockingWindowService; } public override DockablePaneViewModelBase Open() { - return new MxLintPaneExtensionWebViewModel(new Uri(Path.Combine(WebServerBaseUrl.AbsoluteUri,"wwwroot")), () => CurrentApp, _logService) { Title = "MxLint" }; + return new MxLintPaneExtensionWebViewModel(new Uri(Path.Combine(WebServerBaseUrl.AbsoluteUri,"wwwroot")), () => CurrentApp, _logService, _dockingWindowService) { Title = "MxLint" }; } } diff --git a/MxLintPaneExtensionWebViewModel.cs b/MxLintPaneExtensionWebViewModel.cs index a811359..03423b3 100644 --- a/MxLintPaneExtensionWebViewModel.cs +++ b/MxLintPaneExtensionWebViewModel.cs @@ -1,11 +1,11 @@ using Mendix.StudioPro.ExtensionsAPI.Model; +using Mendix.StudioPro.ExtensionsAPI.Model.Projects; using Mendix.StudioPro.ExtensionsAPI.Services; using Mendix.StudioPro.ExtensionsAPI.UI.DockablePane; +using Mendix.StudioPro.ExtensionsAPI.UI.Services; using Mendix.StudioPro.ExtensionsAPI.UI.WebView; -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; +using System.Text.Json.Nodes; + namespace com.cinaq.MxLintExtension; @@ -14,13 +14,16 @@ public class MxLintPaneExtensionWebViewModel : WebViewDockablePaneViewModel private readonly Uri _baseUri; private readonly Func _getCurrentApp; private readonly ILogService _logService; + private readonly IDockingWindowService _dockingWindowService; private DateTime _lastUpdateTime; private IWebView? _webView; // Change 1: Make _webView nullable - public MxLintPaneExtensionWebViewModel(Uri baseUri, Func getCurrentApp, ILogService logService) + + public MxLintPaneExtensionWebViewModel(Uri baseUri, Func getCurrentApp, ILogService logService, IDockingWindowService dockingWindowService) { _baseUri = baseUri; _getCurrentApp = getCurrentApp; + _dockingWindowService = dockingWindowService; _logService = logService; _lastUpdateTime = DateTime.Now.AddYears(-100); // force refresh on first run } @@ -48,6 +51,77 @@ private async void HandleWebViewMessage(object? sender, MessageReceivedEventArgs { _webView?.ShowDevTools(); } + if (args.Message == "openDocument") + { + _webView?.PostMessage("documentOpened"); + await OpenDocument(currentApp, args.Data); + } + } + + + private async Task OpenDocument(IModel currentApp, JsonObject data) + { + var doc = GetUnit(currentApp, data); + if (doc == null) + { + _logService.Error($"Document not found: {data}"); + return false; + } + + _dockingWindowService.TryOpenEditor(doc, null); + return true; + } + + private IAbstractUnit? GetUnit(IModel currentApp, JsonObject data) + { + _logService.Info($"Looking up document: {data}"); + + var documentName = data["document"].ToString(); + if (documentName == "Security$ProjectSecurity") + { + return null; + } + + var moduleName = data["module"].ToString(); + + var module = currentApp.Root.GetModules().Single(m => m.Name == moduleName); + if (module == null) + { + _logService.Error($"Module not found: {moduleName}"); + return null; + } + + + if (documentName == "DomainModels$DomainModel") + { + return module.DomainModel; + } + + + IFolder folder = null; + while (documentName.Contains("/")) + { + var tokens = documentName.Split("/"); + var folderName = tokens[0]; + if (folder == null) + { + folder = module.GetFolders().FirstOrDefault(f => f.Name == folderName); + } + else + { + folder = folder.GetFolders().FirstOrDefault(f => f.Name == folderName); + } + documentName = documentName.Substring(folderName.Length + 1); + } + if (folder == null) + { + return module.GetDocuments().FirstOrDefault(d => d.Name == documentName); + } + else + { + return folder.GetDocuments().FirstOrDefault(d => d.Name == documentName); + } + } private async Task Refresh(IModel currentApp) diff --git a/resources/App/App.mpr b/resources/App/App.mpr index 7baf5d6022f1fe5b42c771709e0f5724c7716569..068fc0681d23207745d4040a367ba11b6233d523 100644 GIT binary patch delta 1967 zcmajgdrVVT90%}wdy6elqyKT6|^mzUe?vMv)C@S#J0d5hkIlAgl95W;t8x?U1I$%sPF1a`=%Nk=wA0%87 zMIr2Wuht*zosaRMHxuInZ(7$?h3~3@9uJ&jakSL5nbsIh$p))aRjI7jtIMrv=489R z(wyvWsZ`gfD{7Ucbxnp^aypGZNYlO6bUy(wAcO#z1R@YaAOt}$NFW3zLnuf=24N5m z5ikWJVJbwyG+;pv(GUZ%5CP=Oj!Km)0e z2I(*lv@jnsU;$*pLRbV@po4740X^h`0rJ2ICdh{ZD1^meh9yu0#ZUsJPzDw#hYDB< z%b*gL!)x$5tbmoU3an5C)ldUASPg4nE!bfl)IuH9LjyRV5t_gW&ESF-XoWUd4{m6O z4bTB^z(&{v9_R!wbU`=tz-H)$KG*_p!dtKvwn0B^haE5gZ^Iz$gm+*F2<(D)VK?l7 zy|B+`P2Yd;x`d|e6t2tEWu{<2HzhdqR-i%=C$@#05s;sv_{5l+JWXTKVvX6QtUnAQkQKk zE6B}E4fQRW#+zu8Kf*o*%!UH%%hro$@qtrG=ZPesM{PNqJ$R>*ckU4S&0hhpTpN**ILk z&cBe3WF6zONxGb`kb31|7Zw$Bu9S9AY3mq)6i`K1Ddn4F5{E#hk; zijpcRhUiu#Mv$Z!-axK&b3$>FN~_9HX|-fv8XHb(PjFKIYEse^#}+c0RPBjaels1< zXs9s3^IdsyJ0!k`ONv!Y=$xrIHL@rk9|0yv&Uwh{Ut9VcX4NE*JgF^ zP?*0ZS_O|7{}Ce#qoQ_9c%nNg=+Zamzsw$)HT-$-wYxW;oY(M#?TmfcS7I?3vqyft zcXZGFnV)r7A&r?yVmBm+8SKV#2Uj! z5v84LU^E#j(v-}y#OUQBqtt4x{~e?RrS^6kB}P>#N@a#xN$xCB#1ey<4fT!0@x@%I zu$y9-Rg~mj@J@W483NCB=ZN6zgolfG=};pb x5Gcu{CLasgF3I%CJd+B8M!BpXXy3Uk{yY;Df)#1x7Ix#mw_WJ#*7F^~{{qhdlluSw delta 992 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 literal 0 HcmV?d00001 diff --git a/resources/App/extensions/MxLintExtension/MendixCLIExtension.pdb b/resources/App/extensions/MxLintExtension/MendixCLIExtension.pdb new file mode 100644 index 0000000000000000000000000000000000000000..b89f13ef32b7dd4fa2c9b619b7eac05d5e14e7f4 GIT binary patch literal 16040 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 F{trnZMH~PC literal 25088 zcmeHv3wT`Bb?!Q6X3jh$jWnZ&ZEWxuTX<|qV+mWv24gHs0t;+Qwj`T}Qh79UY!8g) z$Y(}=FjgYPC4~eiG%4goYC@rbydY_sNAti710-v^R+?J^C#1& z4ln#tnEo*M!lEx|n_pNov^SHFP1x2RJ2@UpC388e5F0aN_GB)W$;Ep52V>(_+Dz2c zgqAq02YQJ%YaZ%;as7MT-d>?gV^vx^(apdZ3L8FzJcfH3H&LCGbtN|=Sbn*lMhZM% z9@=#Wlk)#^?tqNK^~VQ^`nfPo^a(b^>DN)BYT&+gfN1FSy7R#?qCiD`0Pv;?I8iVU z763Qggamb>t0Olcxne}S5_aBB0TS7!QD8+!a93O#FkA`S%vvC1S?MTl=CuoV#kGNG zV+AP1*P%aOESpX~T6ZPU`>rO^1pDt#>f3d(C)!7%6kkXbnwkTtqohyGmE1EmUvlr% zJjs1i7fIeY)g<|0MIHx-mM94nUj#PGiFt2uMABn_hEg2XFmxNF6V|1m(c_DeT1&X- zv6{Jj84{0fE#-2Y@zye=%dgZus<8scK)#%7Z7`UyZ()WlOjqCTp?lmu2MnL>YF z-8&WFOfNEIwV}pJAhAc$HogL-DG0~)oY}<^76VhlK2*YbDzL{&SRZ5U7XVLT9-w%G zhN2%u2rKru0y#LAY<)TE*w!m0Z=C9o{IEjDLCH2{sKTbq1O1nHKn>vmC6G5xK{w=w z6*>+|=E2ZuJfLWa$7+WMcngnC#Y3Ul^2~#w3Le)%4rpBB0ap|`u9Dm{wN7&H6x>sI zu9f_-B8#IX^JJ)kC+h~&m3YD-g=fs+af#xg&~Z>Q4~9x-d`jeipCLc2 z&~Z>Q4~7(v-|SmR}?U zm^w^iOQ5Wp#32R7B;E_0{oAOu|9~Zi zdtLiwUZBcOw$`A&D zedc;@k3`zvAh~C1v(vs$wO7?~^vJ%$P#hGY<$vO_2fQ@V6yE|U4u&nwZSW&j*&abf zgFTH@spEJ^ydR`6Sp#TbY>XHiw7%U(w?ihlK}0Wd-JE;2BFr~8$sm8!8sHALAqh2A zwXEeaHmU>q6qtDfhW~?8Aod1|oW{3TxxoH8)xMt%^WHH5@J=f*0(ac}1GV;=hf zhy~MPCRGrK9BO@l_mt+-PB7(|b))2-DJ+R%4ErMxB#xG$wgra$LgF7!(nw^zY2?3+0$Me*ZzfrFkm zK{mAr4ajIch~fMg%TP3cuyde~??!#K!{t*z1O#&$FTKbd0@fZ7TYHg+bu%cmb;($> zjC#a7bt&hvWJM89;Y%&Y8(EAZSJb15^HYr>ceJ@3#_3jYs?n8VbMm(Vnp%t+GUgUSxzdj-r)AQtylU;uZ=JfQMBH;G0?kDn|gH9)|&2j=v8Y>qV}>fF0)iR3lO~ zwjzKpJ^@ZA<{C{a>QzUr@IC1e!yS5AZxC@@0=5McaBjH-48SD}9(xf+tPn9OT%wFt zEMHA&l7(p*% zdKj|;yr-4{be!u(ARXtL-HBdg*5`397M`68GXrCL8B%sGEY2;**RXs=u3+clyu!J- zUCFuhBFo}9R||@>oQo0Xb}l7=Z>kkkjsS2jYy(=3e;#(wi(J9Z#d(Eui2$>mi#uZ9 zkq%woVL-{J3>ddzJB5hnx!53vbp4Ob#XLAK`5yzW|B1X}s9FA3MxW+?O|T;S-yO(L z_rG@l_gn=YWa57>1NTGR75>K~Q{jIeItp406V{V9R6!&71pKe{8ozxf``Jg?PlE6# znbN#`Knf1jynJvIEFbNhsqH?N59}3MPW0=C`_bvuwo*X zvC0ryoziO0B+YT+aRRI=bn($YLsyJAbm>49bXke4%9wZAWn}ci>uC+{_u~HVxUU7V zpEppaX{HuNW&q7rai$)Ipc%GCEyv%Dc6yO3pd4ov%5f`4IX`c{&S1@9@Z79dyj9ki z(`T@rdtTPxfyQr1>cAPS*Xi@l$NI$Kx4`$rvKlAE{0aOiXcgc?5|0{ethUaIcsYxKY$GyWPYqg6?D7`8FU=W?}swY zYHha;a9J;|L!EU{;nA+*#KJnn*e2_C&gNJ}l^s^u6tZeP|8|A~);o|*tw(F@Y!Z5r zOa4j=Qhobqp$T3C0Kic#3G5!;1`*LoL_5~_Gl zDWOVDXx3N{PF=&9Isw3*at=>uH8r>b+e*$V#)Dgd4@WDY;p_2SjQt4-MK$MO@a z>wOO4juN45qZsiir=J9?(;w5}^lQ|QCqQg zz^&;q+<%071mg&&kh#Ho7tBK0=klDHi?Vz1ocRcFpTHeM*@?GTLMX?dcI)oZ)&?N; z_-{e0sf{QuH?Y9hV{eQxBMb>*F;ifWyOC8Hy_iO_pF;4KBh04ddgBz6==MRF3EqJ~ zZ{+U;!~9*$wTtWK2+5o>?R|gLLQpR=s1>AeiSOjp3gi@^BiC41y4`>eTsmel|BGpf~>GjAF5!R8<15Q zYc=5gdT}$L@|kEH53NQ`u+868eY_t8!T9fjQ@uBWjVgk)H~s*?)dHh|A-OT?Zm{6oh#!U12m@@Eik$ zLKU508G;ThwPKRTnqXK4CpqS2L=E@Rhc`;BvmP;~ws0=THBm!nEnANBMA3^}fvJ9& z^NRSx?Z8RJpVBy0*{jRCRI12+q*R z&;PfRFyA7)~>eJkfUC+r7SmvbJkFOJc7=7LYP#dr^Z0JhqW40GY2 zUk?1*8uj8JFfoojd_-&<|2le7m1Ree!*Tl|KHo+HE<P5!#BM9fG8lj|`Bism&AP<90b^fDBYl8AtAYeTLApZME ztVfY3frES|Zos&5upk{uN3#wV%4m0aF3piYf3Y-2wxQ#S_1R0GhXvphu-Oh&p~D-I z!Dd64AUB~HzXV-Z>&2T<4k(mfyoD=ZMCEv;RZnfunGZt72xsaX3?X9y+hdzvWGq4k zXB95TO`IjqjRe9)n$OybmC=rt?jGduyGuIm1h&gAywnWAAC(S`tU!-F)08CVD0;_HU{5RxbjxE)Lv zZ_HX_OpFU7rR`k!(W+nwz|Xan9O$`s*pgYxYq4vPbN9>(qs;F$=sw&S2U({W<{O^P zXL-^dB2W!(7!udYDy5M@ErIvc8uVRBw*|TUoBrn7dippT8uWAjQ$d5)1ou@NG!x)- zy`;BD`frjx;b;6;g~LMO@P0q@X^7lcWzZ)LPLB!Z_Rze#5WQ?Hi5T=?)!pEHr0UDM zK^dvNTPOz%ZtK-J{g)a}C(+8FZ&ov(ykP1B&9z~ACUi6urV;O&+9>^9ZF6lC{lwc` zW6)1Q(?mxiTpj`cDE+9q88na7ajQ$JyXr5Z+TfAuFcrMJfcZV77tztc5oqzUNb&{x zg&wAbRbBN4UFG9`i{YJ+A*%fX(ys9BtP9hps$TPj=`#kWI?4v^4Kd|Y#Oaf06{g>* z-wO^OfjnV)qn69h66-P|TD(k*KT%a34pTd{2-BKybvQ&njk0|{7VHH5U64Oa^CO=} ztL_k&pEuqK8Gb6gyjIWFd$jt?`X$uv>x91F4069Wd#b~*n~!T_8q0IS`(?d}{yN%R zb1Cid^;AdcYcQTH02n+O$s#8^e15b+z^bes^Rmb6jO#;1$I-k}+^T`9A?D0v|#028!{v=&O zmkRV_ftqQFKpg_L(PaW1l6D;w7ihQCb<%Qy9ujCRwF-1Epf1`#34x-}C`mooO>*z6 z1?t1&HA7f6LAjN#6v!iW1GG+{=Y{7ES`QtTRz2<2=!d~)kj@Qp`ihjpQvQ9NF`q`d zi1vE_%)4#6wfY&PS0L5sS^Wj1C-lE^YIjU;5z4{P-vU!r^Rk22=!?GZgYpmRe+106 z-hXlM25s~I6h~a|uK5|#H;i8(eG|PKv?xOGzsC&j+bf|(8~@qpxc;vvBlhOhQ3tGP|AUp z(tCZibf*J704PGA5{P^MkguM;A<(nrjXdgWr0+PC91j}l`wn!6K>tfo24*~up-x{S za)Qf~XcrOaS$e$c^S&nP7ifmAqWM}A-B_ah3hLhPP=2WXFMac8 zRd=%ouIj{4CCOC$*Fj0~Ejt&Jfovr*$pmy5+Rlfw-=PzT8%>YoX^I zi0fME&P2JcmEP|_T-Qe5XfM~b(RUn(>k{-a>=9g967&TJ;<^>|vz6t#6=bYph%Mox zc8UpfQd{BI@Q!+k1GUz-0{X0if|o^`j1Kx^mombXf8x~rc62?U=bgIW3NJ7&r`k@{ z-@k^3X(i2dAeLn%Em>WbWhJe0Ag)_QKf9t_w~CB446&RVby8TMBltEaWOULJft0P+ z&?esGIODK}Zge2#i3opXxsEF|L`2^GJciVRxQIO*$HfwU4^}mo=C8e{m)lUQQ%3MV z!L~|A1Y(*8G4XVIH@M9%zZ;Y~;pHmus?`woYdTef-s50ChG!qQRR}yax(K!E(&$Ri zYjmCPQ5<$+cdv=$y6EC@)!=^j6Ysez#w@_sUl1<{Tgs-zJzi*D|rb zo7n%Fh&?7&dK0lEg!|jL5ijXE+|OVyVB)=yiE~X8dsS}BV;n*1!!sF|7aJ{ zJ}KX&ZNxmdA9J`BT6`+H7Uiw*_xrU^!Ot5gQhNhEtVL>v=?%KxzYFQ>Q4?tv@%3SP z0Ovc)^fvtf$`1vns6*cr`c0HSt=);#uDS=izQ3t{FUrf%%fs{yuMg?=nhzrGvJ9H` z{pe9ztH(o+QJ22Sc!DC@PxQwqsQu6Ce?~eP{t5Mov^QyU(N|Ktx%wCMxM2G9>#7Xx zm^Om8$AGEB4j$Ifp3>gotD`>s7uEB%FAC+iwI}PBYTwpwt!o4Q!rCjfm$Z-9ZPZSp z+=uiXaKe+?-+TH|?y7kVdn@MilD5EWYHw=S`}ZSdty^J%d$d-uKr1ZpVeKaUx!T9H zjJ_-Kq!!d!qlm7D{#@|i(z^5qeBad$0pF<2(~kuI4wyHpzN_uie^URi+Kis?H0Y~H z3op~x(O-I6Vbw2q)}m}h)?zm{6xpG7=yNlJwE zUlq)&;5JqDJt>n`q(Ak0v?I{nr?KuaU{3iL3#M5x8-Q6I=oU<`V0HmB9!Lr%C8;U+ zX(`W0dPLGY1bvs9Y?AzF%W!9+q` zB$V=ENxLQ8r8BoAYJU_+3Fe5TNA=r)IV$CU6h7oB(i;KIBYaT4A-G7&JxG5P*yRyE zf|>Drh%OHu1xdQ#G^5Yr?jeK^D#X;q9_Bx#qV zx6qUHCHe;a1-(fBfEcz)+o#PS*1xL#So@i#>+jTqo&}yR&mqrmdG7Xn*7Js^A1jfD zwOPl)|2fZ6>=-^6X+ip{NQa^0sn>&=4xD1?n1gMb7awBa}R9ofsH+|tp}2LU`r3I z;^ zUoKO~B(s^@&7J1hplRmZoY5U{ z>nA}>*G}eAyE>?^H#a$M+R3r331}#pzYW=TGnwwsWe*KbBy#|}@`rLMuG(T2_FCzU zlbLMVv{BK!-^>*@C3ESlfK1YLAeku$=pOzN`SVvbw(At2WE9-Of3!WR54BrD=y zPV4Xs60uC@=qR96HUswS6~ICR$I6zQtfX}XbA00F%mh;b+MRa_rNMw}+Zl3~+BQKWUrYoBKr2P8Ts?WizRanHTJ8r&-s4 zPytki!6@#?mb52h4Oyzp-q4lJZah>l^9q9jQ-z)IpKC3<_h80Bl+3u@+&5|F9jwD)2*0LL?O2+= zw3xPg(zY?MTar0gJUz76MsKKEw>~>Mno-lMgcN@r&gGN4&Hh}sH9nCw3#MYzmoLLz z6PTlE$TglUaD6hJ9__Vl%Vq;?H&bS2zk}_cEKE!m&OkfkUn1=*4IcZMV^kW2I%-HO zv3;AZJtc~4x!l)tmeHcK4I%Y8{g(%j&KOde6oZ9iVG@d`P0E*xEDcXPF|WglHWdmJ z+s*t0#@lpp16CeUP+Xl#16Sey)R)_D-DXyX9e6u}q?P9oq@105qmol5coZ;6*wGW1 z>QW1L*)m~EaW_-*d>+aaoD$==CdW+zS*dJIAk49MN(~0c9Lj)X6rs5=Jdt)`G}#P{ zIIa*lf*^zxnEw2;5YsHdqr%DzBy%QlY?s1Db5ABGF2k;iIA)Tdl)sd4P~k58$R4Fd6o4o#S}Bbl94rHU{oH7OPHq+BVcj1Wg+4hb||c20qL zpf_g_BQU4k0Vj-zj6B4ey(|gh`n;8M*C|14wKiJmLuHf`a>_%WP(_ovFLgJy9#)-I5r7_9&>DR&mJ~?8Mk#Zo1Km6%ID4TvFssn zmoniQ_AXcTVL?k`8LPB@xppXN?}3})myhNF%f78#p~NxmO__As%$4z_nO{b^GO@Gn zFl~1AGb{UYyCwdhGMPOm6;`#~nLU%r)yqu!#(A|xXh(g%U{9u?Pq`M$&>$jU;gGBg zWxO)B%$n_HHhEA=`EtE0H&&xGSe7f);$DW`VURT!A-gnYE(h)@nLAXjaORuf3z@M@ zHdCmmWyc=Qn}d7JY_|8Hnd0H^N~gD5TySQrit$jna<%WxrK~jLW>4#cV^67(H`U6U zuy2tCnz}IwF{2B6Np+_tI6w|Rg2&9TviqGfFRy~-4GU@}vjqjiRj?_vu})$eBoygc z5Z9|635||nG7j4r3A$Zl;11E14y00zC5hp}ommzumiU5~vG= zE!k6HmEagarA-!eE$z{0%R$C-EGs#%ySI^64sh16!rf1nf((sI1I4h!ERoJ;Mb*;2 zmxIr+oh6Y-0f_-Tnx_`E64jEwz9CY@_44e2Tg-!(oXH|pAcZHRT!L{;PExny$4gK!eWsr z18Nbp+>L=Gl-iAPjicaJvyk95#f9Nl`)J6*$YJ`) z5&=1RI&Z>CX`;l>A3Ll}de-yDIUY-}a1=&I$J8?lB+oI29&>jRQ?L&ketW`#JD6$J zo7$s*pkf!D8Z3CFPmrAncO|qa3PaL1I+gx zbN`TKF`)KI730bC;wH?GJQp0lR`aApU18sZGg%R6+;a!H91_O3FROK!13uabYlQ4+8Sh_H-d z+rrP-1*`#fzR+(w3UgO#RYgp5OB_ALlokB&M)T0K6ihXip^*{{KfYFU%&?H0a(m*9Vz6U);% zo(ojYJG9f|m0GT8i?!d}YU0meINr?<@fQjNnBeSNZ-EQ}m1CAfR#iY;b&;b8sDfk* zY{hRRSWNZAvDH$i9)jA5ojf8$X{UyrP{B^73PV;I1^1M_6*i_g|67#A`RoMFYcrI> zH&q4X7S3%~;2bwECAy*uH7UFU#zevw4&8z+;5^qtt2UfXX7SIW11R?ci+vyN9BH2A z_{24V6HOCP8n}a?;673~DK<%4G!6{+n?tW{!h>tG_)@||4Ja0uYi+=JnnY~@d7>np zR%Hv8YiVjPY75XNkFUR0;2%p;KyQ;ctxlj{3(~B>UG8@_#PV^UE1-vok_mb8((?*v zz&hrDwSY~7W%V2FV$3YmC2Q?2Gc9s#^0^DY4Q4mAtN$XVR zW|Q&|GpDu-a?++hd99GADJCOlBF~~RcbG>SgVjw4>UJGN9eWp3u?H)$xD|K08{#nq zxBHI|&Y6m9wFlr62gKYKdZC(1Lr^2jkcLbLVHECv98%K!K494|ccNwtZ&@uCB9{%gl@K+I}V?-^q~Tj(t?Fe}KDHibdas+OZP4)3yw7H<{vD=3D+$bgQA zhKGgyG>zZrTn7xIsI=grXA1IjP|6ThRhZ4MCOrjJPnhoY>=f*aE6T=QSjOE79rokS zLoyCa6QHC>xt`?(myUA(L#ss&wB8G71g1&}tlfN~cOGaP8SuQWb!T?LD>o(PC7J+NG55aJel(6cMiZuG`M z4z9F}V*y(;e<4XPu9S2D?bTFbw}c*x$}wrS#0k8{W2sF?smbZZK9K5x#_045T6FptzBsAp#tRtp6~@5)Qdv*F7)7U_^ZK<&%!?^} z$ltDMTHPi1jS6E>xdm@rqce>LcQMllE+OtR#O-G0DeOF7OxMC;L0t@rFy54h!(nFH z&V1Ur)XJq+F0JCyDlVzKco{1Wnnv~a-h zy*M0*Zi1z>@Wrr_21+l!Bnq>D;poiO+`|C(HQ@Ebgs>x0Ms8;0=7`@2T! z@yNdDWHssN;7!2c$gtlj1~9sysVB(Fq@i|Q=P|9jL<@yD7GIq)DNkF0QBQ zm>$`u@R4DReq`JT>F6mYzBSqC_XncY7=?S739`ua{n1Hipod{M=^e;056+uh?RUtk zRIl7%iKrTx=@W0+;*V+3EpQ}2;N?eW)AvV~RM5vA9R8*k`xyGt!XdvGVo2XkZELmP z=c1yM4zk@(UW^2+tJtoJi1^Xzhq#mB=p;nPXzR)=9#XzMj4ZkZwp4A_c<@z%&#wof zbxBO9Xr0Lb((Tro2JhDdDSx6IS8g@YJc z!M-MwX93(0pwnv)o4cNBG2GDw;pk1=38sNT9#*Hy8FM(!;#Hs50Bsx zHr)|6!4M-HWd9a)6|1lSqG1S~eg^_iUNicR$lrvn3_M?~|J4g^$F~JOfBVRed8=Nz ztGaSazUtT6fO-wsOEJEubFGUd0 zpr@;)us}qe{xQ21Sa9G`K7QyZQxo~J1HUi8-@aLS;T9EdOQa!0 zU#(Hcg&US$xJa{=L|15Z`GuR6&-A3-s<+x*>Dv-%hMz24rP1mOcc+@U2c^;$KWXUF z@TJbVUVk{RL-^#T^!lTv{H2t9oWs8*f+OApSGy3ObH03PInT#7_%pL~-G6}dtWR|4 zI*m47h#3x>_^{c;M=Z|Q9PElf@QWNm{#)yvgM8`^=*{!i|ykNmsQdj59NgS1BTVn&0 zV>r9L!NgZTc=2|14sYTi)FTk;q4Tpp{UeH5UsU{;2#McJPCs~IAxi^YexdVUA)Y(v zD%j&^I?nZj=bnC?v@HW1)+cZ;IfxrSGIuXt9qMr}#r;0iy|1E7PIP>5qS`=}qR*edP$=z1#T_Ya&0Awo>}&CcCFajdZT+|TaGX163WIU3KWu-2LK##w5A)eI)5t3R9D|I-lv zW?BBO$MkG9C63@!md?O4-6q^OqGka5X7?KKAh_WyZjeTi_ToF}K^*b)-yA6~-F z4WMUBu|zg{QPn!a6rLtCh+$?Fd`a`?C=?&aNsu>6R?4LSHb79By({<`s;V_ zY-R4x)tez@vA5cK3r5?dPFV2@e2u>n z|JDFXOWFZ!l2#+_gx9TwbvuDif^rPUi))1{g|rj6PGH-C$1V`p8X2=Lj5xms83#Tq iN1f-ELyes@{?AAz)Zgx?lV6bjXLRuYdH&yQf&UG{rK{us diff --git a/resources/App/extensions/MxLintExtension/MxLintExtension.pdb b/resources/App/extensions/MxLintExtension/MxLintExtension.pdb index 0291ed9d1e8aed009071158dfe7a74147b0113e6..e1a2c60ad9e3eae932e72660be25a2b6fd3c6212 100644 GIT binary patch delta 3165 zcmZ`*3s6&M7Ctw*c_AVY2oHr|$>A()s9Xry6q~;s&(Dgx^=tXe>dq;$KJ{JpYxq_a_;}S z=l@%m_AcjIGhEXYL_?MljZqTqCg_t|nj36$98jqjHlEq4CedT)txPYeDjfysRuMIt zzzZAgwczQB&r00|7fLoP9X{rqY?tc$|CrXoOx4<*{ZxKL9CRxX2FwA*0>wl|3W9PJ zkOHg&QGmqDnN14>11tgFNrAxNO+DIBmZ1R901-fcAWVqjCImcB;Ygdxks(z=1*yVv zPA#-@ybEdj>m?Memy%wOV7&6r(68uux&fpccp3v#8e~)rgr%_<_Q)t*)MeD3CZ{t% zemb+uX^|*7+A2y3^?2p#;mj|goQ(fTD`Ye?-0~U}natsPr1W;CubZRsI!~sIJ_L@7 zQ8GFO{Ty)7%ZrWdpE7calIe2lG0N$&$mNt{Iy6gS;DSQo3Be3=;_ML>2|J`(&Mcgi z>Rl!9MN;UGz7H_LjPtO31>X{2Ah_K7-#qX^eYgQU3KQ@1vv)=J;fwq5A<$XZnO}i? zW+Iokuv!?D-WOCLAf^wm1Ro5p^7>habr`gdPezA*oNXcvcr3hr-uRn+_+p~F1SBFLz?)z@Y)O51FZ@2f8TwH8hrvI<8~+h_Y9D?VF+R>ZxYf%8 z5kJiZ!d5FC`<5*bdsYHwOtFs#gJ-|QN4&&GzQl81;(2|z2d^pca*Us7JK!^*4$wTH z2m!&~4z=MAJ{|&JHFzO|&wv&OS@2INv<6rIwH@MKsM3 z$!N4J?X47F_u&TsSuYoO@8elWSQFyur zl9jx0KnF`Kwr4ytBxWIP5dOI_8(GODw*Sg6iEo0{HeE^cVFQj@)Ifv{RJDyrVzSZ8gf7W+J_%|Z3>71?bq zR$GgMtcwoaRqW$p3{RS-|5{y`^2x1&y!%@&Jj-cX^2Od^%er4=6&Ab~MZD0d(#PyI zPx-brc6va_u^WGwzO3qe{kgAE-~r**DLhj z_1v$xw!899L3VT1H(RdXyIMRhYPlm03+oZe$DPg(T=huy!|CqQP4NxuJ)NU3?$p(^ zzV(TouJMJjVb&gMax6Y}*)%I-Ji7Sqa|6KZR?YU#y1l`KA zp0DC-w(WiAKtn+9)J_P@5jqR^uf5ps<15`QfzI0o<%%iS zU!Rja+5C$VY9C%#7#vbluwqYKVA7>OR-OvH;+OX;qdV!Xiotu{$Kph6+=j|K{4np!4_d>~(b= zzw!BnmajMLJ63DdrM7Jdxcfk{<5~TM-E&<7HhwW>*W~yomlr(Rxui>0S~%_>1xIXS zkM}&Z$Zdal*s$?vb(`_b`;nVnkH=3eEBQKcVDNiE>xblM<1e*J{EODDT(@(6_Vx)O z>u>H|nvtENet7Cq!pXOf(ThiauXjEA=7*z;uiOnk|6t;uLT0R+wdLsH6PfRZ?G6v_ zXa07)o3=G4xJ5X zONglEJTo|tlk=?PJR3RBHqLW^^PJ^8mj$QssrFUg_61z~8m`^LwOL*kAVT*qy8&dmsx$ti+YDhLGw2b#pm zYJ~f6q>82Ti*&}JUbr`F5T#~OS{8*+B}Fg|*Akq&5@(8F$a4MC%HqBi2Epg6fwO#>yUVq$a&uf+9BLM0d-jpUtL(lt0kNj(UeN_k~Cl`7dC zji&~8n=sW}pYSsiEUqrsd6SKxv)2?L+%ucFKp`eu#{~;T*@gbPZVcdCK-Z)eHf1~G zVv}()6a%rWyowJyLTs?yhh)dVn`%KnVvS$?DA;BK@xsXwIxa-`azwl|u_9FHpEET& zX(s|d29jQ&2}4v;4H`RBB142#Iq}kA^E5(7&O%oztMflT?0}DC)qN<<3sA+expD88 zkf?CHlkJ6&>O3YizswcIVo`)N7K*+%3d`BW6=#Sa>5^4J>_(zQ5?nrY{Dd$z@iq2-L*l!n>UBbf!Ru~n730kzuV-buz0tWE`S{H(1 zu(i^BhUf^H1RIlR7qpP1)zl$QZA>zej>c3)LZVhEn}pex7SROsE~e)%oo`3E zMNSlw3Z7HfPyxO$#QJ$sgkfImcW(|Hemm^$jgN=lftiZ6eKQnFA`bd;AOu(m%mvbj zQYjG1n}8T#9cTcNhcjCQ5CE_S&?or;vwnM18_HU+0zrTp@C8DJ2<{pG2ZFF z5|KSgFp5-8G#O|R(QJd5Oa=s^yAkML7$h_Xn0N{00t+rHFz}%r2s%o^o~aL%!Qn%2SBKSb!1AkDr)6NWsuTqNVhAw?j&^&0e>R zy38_q)y-ve)jarX5zhtr!ySV8a_+?-$`n2mYdNEEOKfnm)zDDrBOjYUIM&4*8wx&a z8gH4#qoA{{m|uh%*cZ|Q5#VA^Uxnb2)A(X{j2h>IZ45anA5w?q@)&3d>|V~cC=6Ur z3PeD`H#&_k0k?oh!|&tqt8fUsycqs!_+#K#c>J%!7Wardi6#)>HSB>NkAMUOC_NS4 z1AlTFzkwJpX9L{t;WH4w5;_hy@i=b2p8D|`ygUTH zV(@ej{vo)vpu1iOFsSj&3L6^1y~6G1>e% zI$}Vk(+q$mBS*nJJF+Z$1KqrbpNTRUU~}gv$B4ur74S1a2a6ut6^#~R&}t zHi<_osfe;-Ju+}OvoOF|47Nuai%Be&p^#CN@R^U=NfJ5TH`*ZEOg4#v_L^FuCndK+ zpO-w$?8zjR(|EECGAgANvKLZ8hf{`Satbz+kDOjKw?dvb4?~LCQ6S1Kiq6QgmX*~t zta8+?bk1jhWaMZ zbyT&9Xz*Y1L!7Sr-=`v$Lq~|yHE*1uC>0DmWEWE2U=*l*K z(|L0u?_*JS>4j_Cv)B9m5OHpDw{f&AaU|f%mfAlIN@lc*oPm>1T@m+$cl()2es^h; zJ?X<89hZ;QwdYMeZ9j8;clu=8h9|IJE@4sONn2pYo#^j(bjI#_Lu-?6+p7y&rSHt{ z^SSD{^z^4Oqa}msq4(~LiK_Xn+r*X_JPi^mZmqwNB**P!!+k(F;UHvVkU#)Miw@$Y7>f|*g2YUM# z|D!U3`{S7|#r(zPQbl*S?QMH_t|GT!&i%kkbSUJDoCPZGoy7Lj8qL6(6$hf!H9IeN zr~c1*^VWs1*M=6%S>j*#N>xJMc-fz&RcFmt3)JV&77umrT=w(gxlZYql^ZSRPo*EK zIaz04Y3l7gGgrIq%4lKC(tDd*zTdzvsL~rsrjm~Ntn7*&YO=1mesF02?Z}v>bsI*$ z-n;pg>Y}`>J=nuwC1RNjB-s9iL|K(HulaB0^;jNd$54K0|;{WOM!i{f-cIUlS zb@HRNP4Ual8f`xu|I@|s;}4wu;a^VPJ+~;nQfbKJ7mZyS@x3mM@30^1DSa@e64U`Z z;wq}lMoU_9vejH^H5-kNRHJ!y8gI5bEEOqfmDW|2W{26rSC|G*2keVg|AKQlIM)`= z)xo)5=3IT8Ymjq&!nw{1EtW~`vDL1g4C9K&$ zlNFl-EUXau3FT>gc_iohiF#;w;2O(cQ=n8vYKa^-w5XY7ed}u~1#3d7YS!XuUK0X; zF!O6gNMAJ*c!BXIiJIeuB&$|f_>?kt_TxKxID>&}+>u6&0(V5D!iP*EJe;_5<`SOf znZ!w&*(+9ED45e%$Nt1<-AlzCm9q6X8fNhoPNrKpf8o1yJr^iMWaRkjzCs&QfbRJq zp*Evgsh@-EH4o4Ww=*15-3Jh4QQzaYAi~(3!Q-`6OGoM<07~G;9v>RO_#Jg5901hehO8FuQbi z6XM1clRi%2OLzgrh`{fNUh<5JG!i-feHMWlhjU diff --git a/wwwroot/api b/resources/App/extensions/MxLintExtension/wwwroot/api-sample.json similarity index 100% rename from wwwroot/api rename to resources/App/extensions/MxLintExtension/wwwroot/api-sample.json diff --git a/resources/App/extensions/MxLintExtension/wwwroot/index.html b/resources/App/extensions/MxLintExtension/wwwroot/index.html index e9b3b5e..b93a739 100644 --- a/resources/App/extensions/MxLintExtension/wwwroot/index.html +++ b/resources/App/extensions/MxLintExtension/wwwroot/index.html @@ -1,7 +1,7 @@ - Linting + MxLint @@ -48,24 +48,32 @@
Rules
- - - - - - + + + + + + + - + + + + - - + + + + - -
SeverityDocumentRuleCategoryStatusSeverityDocumentModuleTypeRuleCategoryStatus
HIGHMEDIUM + + System Administration.ActiveSessions + + AdministrationForms$Page
- modelsource\Administration\System Administration\ActiveSessions.Forms$Page.yaml + InlineStylePropertyUsed

Inline style property used @@ -74,16 +82,18 @@

InlineStylePropertyUsed Maintainability Failed
MEDIUMHIGHProjectSettings-Security$ProjectSecurity
- modelsource\\Security$ProjectSecurity.yaml + PasswordPolicy

Strong password policy @@ -95,12 +105,10 @@

Error
[HIGH, Security, 4100] Password must require symbols

InlineStylePropertyUsed Security Failed
diff --git a/resources/App/extensions/MxLintExtension/wwwroot/main.js b/resources/App/extensions/MxLintExtension/wwwroot/main.js index 20af38f..d3b1928 100644 --- a/resources/App/extensions/MxLintExtension/wwwroot/main.js +++ b/resources/App/extensions/MxLintExtension/wwwroot/main.js @@ -1,91 +1,4 @@ -let exampleData = { - "testsuites": [ - { - "name": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0001_anonymous_disabled.rego", - "tests": 1, - "failures": 0, - "skipped": 0, - "time": 0.0025816, - "testcases": [ - { - "name": "modelsource\\Security$ProjectSecurity.yaml", - "time": 0.0025816 - } - ] - }, - { - "name": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0002_demo_users_disabled.rego", - "tests": 1, - "failures": 1, - "skipped": 0, - "time": 0.0020738, - "testcases": [ - { - "name": "modelsource\\Security$ProjectSecurity.yaml", - "time": 0.0020738, - "failure": { - "message": "[HIGH, Security, 4098] Business apps should disable demo users", - "type": "AssertionError" - } - } - ] - }, - { - "name": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0003_security_checks.rego", - "tests": 1, - "failures": 0, - "skipped": 0, - "time": 0.0025866, - "testcases": [ - { - "name": "modelsource\\Security$ProjectSecurity.yaml", - "time": 0.0025866 - } - ] - } - ], - "rules": [ - { - "title": "Business apps must always require login", - "description": "No anonymous means every user must have valid login session or credentials", - "category": "Security", - "severity": "HIGH", - "ruleNumber": "001_0001", - "remediation": "Disable anonymous/guest access in Project Security", - "ruleName": "AnonymousDisabled", - "path": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0001_anonymous_disabled.rego", - "skipReason": "", - "pattern": "Security$ProjectSecurity.yaml", - "packageName": "app.mendix.project_settings.anonymous_disabled" - }, - { - "title": "Business apps should disable demo users", - "description": "No demo users", - "category": "Security", - "severity": "HIGH", - "ruleNumber": "001_0002", - "remediation": "Disable demo users in Project Security", - "ruleName": "DemoUsersDisabled", - "path": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0002_demo_users_disabled.rego", - "skipReason": "", - "pattern": "Security$ProjectSecurity.yaml", - "packageName": "app.mendix.project_settings.demo_users_disabled" - }, - { - "title": "Ensure security rules are active", - "description": "Any serious app needs entity access security configured", - "category": "Security", - "severity": "MEDIUM", - "ruleNumber": "001_0003", - "remediation": "Set Security check to production in Project Security", - "ruleName": "SecurityChecks", - "path": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0003_security_checks.rego", - "skipReason": "", - "pattern": "Security$ProjectSecurity.yaml", - "packageName": "app.mendix.project_settings.security_checks" - }, - ] -}; + document.filter = ["HIGH", "MEDIUM", "LOW"]; function postMessage(message, data) { @@ -93,6 +6,7 @@ function postMessage(message, data) { console.log("Missing webview ", message, data); return; } + console.log("PostMessage", message, data); window.chrome.webview.postMessage({ message, data }); } @@ -155,12 +69,33 @@ function createSpan(text, className) { return span; } +function createLink(text, href, obj) { + let a = document.createElement("a"); + a.innerText = text; + a.href = href; + if (obj !== undefined) { + a.addEventListener('click', (e) => { + postMessage("openDocument", { + document: obj.docname, + type: obj.doctype, + module: obj.module + }); + e.preventDefault(); + }); + } + return a; +} + function renderTestCase(testcase) { let tr = document.createElement("tr"); let tdSeverity = document.createElement("td"); tdSeverity.setAttribute("data-label", "Severity"); let tdDocument = document.createElement("td"); tdDocument.setAttribute("data-label", "Document"); + let tdModule = document.createElement("td"); + tdModule.setAttribute("data-label", "Module"); + let tdDocType = document.createElement("td"); + tdDocType.setAttribute("data-label", "Type"); let tdRuleName = document.createElement("td"); tdRuleName.setAttribute("data-label", "Rule"); let tdCategory = document.createElement("td"); @@ -170,7 +105,7 @@ function renderTestCase(testcase) { let details = document.createElement("details"); let summary = document.createElement("summary"); - summary.innerText = testcase.name; + summary.innerText = testcase.rule.ruleName; details.appendChild(summary); let pDescription = document.createElement("p"); @@ -214,15 +149,30 @@ function renderTestCase(testcase) { spanStatus.innerText = testcase.status; spanStatus.classList.add("label"); spanStatus.classList.add(testcase.statusClass); + spanStatus.addEventListener('click', () => { + postMessage("openDocument", { document: testcase.name }); + }); tdSeverity.replaceChildren(createSpan(testcase.rule.severity)); - tdDocument.replaceChildren(details); - tdRuleName.replaceChildren(createSpan(testcase.rule.ruleName)); + + if (testcase.docname === "Metadata" && testcase.doctype === "") { + tdDocument.replaceChildren(createSpan(testcase.docname)); + } else if (testcase.docname === "Security$ProjectSecurity" && testcase.doctype === "") { + tdDocument.replaceChildren(createSpan(testcase.docname)); + } else { + tdDocument.replaceChildren(createLink(testcase.docname, "#", testcase)); + } + + tdRuleName.replaceChildren(details); tdCategory.replaceChildren(createSpan(testcase.rule.category)); + tdDocType.replaceChildren(createSpan(testcase.doctype)); + tdModule.replaceChildren(createSpan(testcase.module)); tdStatus.replaceChildren(spanStatus); tr.appendChild(tdSeverity); tr.appendChild(tdDocument); + tr.appendChild(tdModule); + tr.appendChild(tdDocType); tr.appendChild(tdRuleName); tr.appendChild(tdCategory); tr.appendChild(tdStatus); @@ -261,6 +211,26 @@ function renderData() { } else { ts.severity_code = 3; } + const tokens = ts.name.split("\\"); + //console.log(tokens); + ts.module = ""; + if (tokens.length > 1) { + ts.module = tokens[0]; + const last = tokens.length - 1; + const rest = tokens.slice(1, tokens.length); + //console.log(rest); + if (rest.length > 1) { + ts.docname = rest.join("/").split('.')[0]; + ts.doctype = tokens[last].split('.')[1]; + } else { + ts.docname = tokens[last].split('.')[0] + ts.doctype = ""; + } + } else { + ts.docname = ts.name.split('.')[0]; + ts.doctype = ""; + + } all_testcases.push(ts); } } @@ -291,25 +261,48 @@ function renderData() { document.getElementById("total").innerText = total; document.getElementById("rules").innerText = rules; - - details.replaceChildren(...ruleItems); + if (total === 0) { - details.replaceChildren(createSpan("Whoops! Nothing here yet", "pico-color-gray")); + console.log("No testcases found"); + } else { + details.replaceChildren(...ruleItems); + } +} + + +function djb2(str) { + let hash = 5381; + for (let i = 0; i < str.length; i++) { + hash = (hash * 33) ^ str.charCodeAt(i); } + return hash; } async function refreshData() { - let response = await fetch("./api"); + let response; + if (window.chrome.webview === undefined) { + response = await fetch("./api-sample.json"); + } else { + response = await fetch("./api"); + } document.data = await response.json(); - renderData(); + const newHash = djb2(response.text); + if (document.hash !== newHash) { + // console.log("Data changed"); + renderData(); + } + document.hash = newHash; } function init() { + document.hash = ""; document.data = { "testsuites": [], "rules": [] } - //document.data = exampleData; + if (window.chrome.webview === undefined) { + refreshData(); + } renderData(); } @@ -330,5 +323,6 @@ init(); postMessage("MessageListenerRegistered"); setInterval(async () => { postMessage("refreshData"); - //await refreshData(); + + await refreshData(); }, 1000); \ No newline at end of file diff --git a/resources/App/modelsource/MyFirstModule/MyFirstLogic.Microflows$Microflow.yaml b/resources/App/modelsource/MyFirstModule/MyFirstLogic.Microflows$Microflow.yaml index ccb4733..97744c6 100644 --- a/resources/App/modelsource/MyFirstModule/MyFirstLogic.Microflows$Microflow.yaml +++ b/resources/App/modelsource/MyFirstModule/MyFirstLogic.Microflows$Microflow.yaml @@ -19,6 +19,62 @@ MainFunction: NewCaseValue: $Type: Microflows$NoCase ID: tCZ8lwGiKkSAKHUntgr4dw== +- Attributes: + $Type: Microflows$ActionActivity + Action: + $Type: Microflows$RetrieveAction + ErrorHandlingType: Rollback + ResultVariableName: UserRoles + RetrieveSource: + $Type: Microflows$AssociationRetrieveSource + AssociationId: System.UserRoles + StartVariableName: currentUser + AutoGenerateCaption: true + BackgroundColor: Default + Caption: Activity + Disabled: false + Documentation: "" + ID: ZkMk1r+fwk6sCrj2vasdoQ== +- Attributes: + $Type: Microflows$SequenceFlow + IsErrorHandler: false + NewCaseValue: + $Type: Microflows$NoCase + ID: cD35k6/3SEq/Gr6T2SEBMA== +- Attributes: + $Type: Microflows$LoopedActivity + Documentation: "" + ErrorHandlingType: Rollback + LoopSource: + $Type: Microflows$IterableList + ListVariableName: UserRoles + VariableName: IteratorUserRole + ObjectCollection: + $Type: Microflows$MicroflowObjectCollection + Objects: + - $ID: hC0KyNPDzEKwKKrJC9Tl3A== + $Type: Microflows$ActionActivity + Action: + $ID: uq6IUlVGSEKw7+axm/Enxw== + $Type: Microflows$CommitAction + CommitVariableName: IteratorUserRole + ErrorHandlingType: Rollback + RefreshInClient: false + WithEvents: true + AutoGenerateCaption: true + BackgroundColor: Default + Caption: Activity + Disabled: false + Documentation: "" + RelativeMiddlePoint: 214;100 + Size: 120;60 + ID: VZ7hc92BekaOZ3mFg9x5OQ== +- Attributes: + $Type: Microflows$SequenceFlow + IsErrorHandler: false + NewCaseValue: + $Type: Microflows$NoCase + ID: 2FZrVXv8eUimYpiJfqptag== - Attributes: $Type: Microflows$EndEvent Documentation: "" diff --git a/resources/App/modelsource/Security$ProjectSecurity.yaml b/resources/App/modelsource/Security$ProjectSecurity.yaml index 0fd0316..2f3731e 100644 --- a/resources/App/modelsource/Security$ProjectSecurity.yaml +++ b/resources/App/modelsource/Security$ProjectSecurity.yaml @@ -2,7 +2,7 @@ $Type: Security$ProjectSecurity AdminPassword: "1" AdminUserName: MxAdmin AdminUserRole: Administrator -CheckSecurity: false +CheckSecurity: true DemoUsers: - $Type: Security$DemoUserImpl Entity: Administration.Account diff --git a/resources/App/project-settings.user.json b/resources/App/project-settings.user.json index d5236ec..fcc8ccf 100644 --- a/resources/App/project-settings.user.json +++ b/resources/App/project-settings.user.json @@ -12,7 +12,7 @@ "disabledBestPractices": [], "readRecommendations": [], "suppressedRecommendations": [], - "hasShownRecommendationsNotificationForThisApp": false, + "hasShownRecommendationsNotificationForThisApp": true, "type": "Mendix.Modeler.MxAssist.PerformanceBot.Serialization.Configuration.PerformanceBotConfiguration, Mendix.Modeler.MxAssist.PerformanceBot.Serialization, Version=10.12.2.0, Culture=neutral, PublicKeyToken=null" } ] diff --git a/wwwroot/api-sample.json b/wwwroot/api-sample.json new file mode 100644 index 0000000..d611a37 --- /dev/null +++ b/wwwroot/api-sample.json @@ -0,0 +1,493 @@ +{ + "testsuites": [ + { + "name": "policies\\001_project_settings\\001_0001_anonymous_disabled.rego", + "tests": 1, + "failures": 0, + "skipped": 0, + "time": 0.0030863, + "testcases": [ + { + "name": "modelsource\\Security$ProjectSecurity.yaml", + "time": 0.0030863 + } + ] + }, + { + "name": "policies\\001_project_settings\\001_0002_demo_users_disabled.rego", + "tests": 1, + "failures": 1, + "skipped": 0, + "time": 0.0020127, + "testcases": [ + { + "name": "modelsource\\Security$ProjectSecurity.yaml", + "time": 0.0020127, + "failure": { + "message": "[HIGH, Security, 4098] Business apps should disable demo users", + "type": "AssertionError" + } + } + ] + }, + { + "name": "policies\\001_project_settings\\001_0003_security_checks.rego", + "tests": 1, + "failures": 1, + "skipped": 0, + "time": 0.0039986, + "testcases": [ + { + "name": "modelsource\\Security$ProjectSecurity.yaml", + "time": 0.0039986, + "failure": { + "message": "[HIGH, Security, 4099] Security check is not set to Production in Project Security", + "type": "AssertionError" + } + } + ] + }, + { + "name": "policies\\001_project_settings\\001_0004_strong_password.rego", + "tests": 1, + "failures": 1, + "skipped": 0, + "time": 0.0040127, + "testcases": [ + { + "name": "modelsource\\Security$ProjectSecurity.yaml", + "time": 0.0040127, + "failure": { + "message": "[HIGH, Security, 4100] Password must require symbols", + "type": "AssertionError" + } + } + ] + }, + { + "name": "policies\\002_domain_model\\002_0001_number_of_entities.rego", + "tests": 9, + "failures": 0, + "skipped": 0, + "time": 0.0185173, + "testcases": [ + { + "name": "modelsource\\Administration\\DomainModels$DomainModel.yaml", + "time": 0.0019996 + }, + { + "name": "modelsource\\Atlas_Core\\DomainModels$DomainModel.yaml", + "time": 0.0015077 + }, + { + "name": "modelsource\\Atlas_Web_Content\\DomainModels$DomainModel.yaml", + "time": 0.0025085 + }, + { + "name": "modelsource\\DataWidgets\\DomainModels$DomainModel.yaml", + "time": 0.0019993 + }, + { + "name": "modelsource\\FeedbackModule\\DomainModels$DomainModel.yaml", + "time": 0.0019995 + }, + { + "name": "modelsource\\MendixCLIExtension\\DomainModels$DomainModel.yaml", + "time": 0.001506 + }, + { + "name": "modelsource\\MyFirstModule\\DomainModels$DomainModel.yaml", + "time": 0.0019982 + }, + { + "name": "modelsource\\NanoflowCommons\\DomainModels$DomainModel.yaml", + "time": 0.0030007 + }, + { + "name": "modelsource\\WebActions\\DomainModels$DomainModel.yaml", + "time": 0.0019978 + } + ] + }, + { + "name": "policies\\002_domain_model\\002_0002_number_of_attributes.rego", + "tests": 9, + "failures": 0, + "skipped": 0, + "time": 0.019026400000000002, + "testcases": [ + { + "name": "modelsource\\Administration\\DomainModels$DomainModel.yaml", + "time": 0.0019999 + }, + { + "name": "modelsource\\Atlas_Core\\DomainModels$DomainModel.yaml", + "time": 0.0020001 + }, + { + "name": "modelsource\\Atlas_Web_Content\\DomainModels$DomainModel.yaml", + "time": 0.0030006 + }, + { + "name": "modelsource\\DataWidgets\\DomainModels$DomainModel.yaml", + "time": 0.0009982 + }, + { + "name": "modelsource\\FeedbackModule\\DomainModels$DomainModel.yaml", + "time": 0.0025224 + }, + { + "name": "modelsource\\MendixCLIExtension\\DomainModels$DomainModel.yaml", + "time": 0.0019984 + }, + { + "name": "modelsource\\MyFirstModule\\DomainModels$DomainModel.yaml", + "time": 0.0020009 + }, + { + "name": "modelsource\\NanoflowCommons\\DomainModels$DomainModel.yaml", + "time": 0.0025058 + }, + { + "name": "modelsource\\WebActions\\DomainModels$DomainModel.yaml", + "time": 0.0020001 + } + ] + }, + { + "name": "policies\\002_domain_model\\002_0003_inherit_from_administration_account.rego", + "tests": 9, + "failures": 0, + "skipped": 0, + "time": 0.019846299999999997, + "testcases": [ + { + "name": "modelsource\\Administration\\DomainModels$DomainModel.yaml", + "time": 0.0026109 + }, + { + "name": "modelsource\\Atlas_Core\\DomainModels$DomainModel.yaml", + "time": 0.0020662 + }, + { + "name": "modelsource\\Atlas_Web_Content\\DomainModels$DomainModel.yaml", + "time": 0.0020763 + }, + { + "name": "modelsource\\DataWidgets\\DomainModels$DomainModel.yaml", + "time": 0.0021044 + }, + { + "name": "modelsource\\FeedbackModule\\DomainModels$DomainModel.yaml", + "time": 0.0020769 + }, + { + "name": "modelsource\\MendixCLIExtension\\DomainModels$DomainModel.yaml", + "time": 0.0020977 + }, + { + "name": "modelsource\\MyFirstModule\\DomainModels$DomainModel.yaml", + "time": 0.0021378 + }, + { + "name": "modelsource\\NanoflowCommons\\DomainModels$DomainModel.yaml", + "time": 0.002599 + }, + { + "name": "modelsource\\WebActions\\DomainModels$DomainModel.yaml", + "time": 0.0020771 + } + ] + }, + { + "name": "policies\\002_domain_model\\002_0004_inherit_from_non_system.rego", + "tests": 9, + "failures": 0, + "skipped": 0, + "time": 0.020190299999999998, + "testcases": [ + { + "name": "modelsource\\Administration\\DomainModels$DomainModel.yaml", + "time": 0.0025253 + }, + { + "name": "modelsource\\Atlas_Core\\DomainModels$DomainModel.yaml", + "time": 0.0020008 + }, + { + "name": "modelsource\\Atlas_Web_Content\\DomainModels$DomainModel.yaml", + "time": 0.0023652 + }, + { + "name": "modelsource\\DataWidgets\\DomainModels$DomainModel.yaml", + "time": 0.0019993 + }, + { + "name": "modelsource\\FeedbackModule\\DomainModels$DomainModel.yaml", + "time": 0.0015094 + }, + { + "name": "modelsource\\MendixCLIExtension\\DomainModels$DomainModel.yaml", + "time": 0.0020002 + }, + { + "name": "modelsource\\MyFirstModule\\DomainModels$DomainModel.yaml", + "time": 0.0022322 + }, + { + "name": "modelsource\\NanoflowCommons\\DomainModels$DomainModel.yaml", + "time": 0.0035587 + }, + { + "name": "modelsource\\WebActions\\DomainModels$DomainModel.yaml", + "time": 0.0019992 + } + ] + }, + { + "name": "policies\\002_domain_model\\002_0005_avoid_system_entity_association.rego", + "tests": 9, + "failures": 0, + "skipped": 0, + "time": 0.022041800000000004, + "testcases": [ + { + "name": "modelsource\\Administration\\DomainModels$DomainModel.yaml", + "time": 0.0035096 + }, + { + "name": "modelsource\\Atlas_Core\\DomainModels$DomainModel.yaml", + "time": 0.0019999 + }, + { + "name": "modelsource\\Atlas_Web_Content\\DomainModels$DomainModel.yaml", + "time": 0.0020002 + }, + { + "name": "modelsource\\DataWidgets\\DomainModels$DomainModel.yaml", + "time": 0.0019996 + }, + { + "name": "modelsource\\FeedbackModule\\DomainModels$DomainModel.yaml", + "time": 0.0025083 + }, + { + "name": "modelsource\\MendixCLIExtension\\DomainModels$DomainModel.yaml", + "time": 0.0019993 + }, + { + "name": "modelsource\\MyFirstModule\\DomainModels$DomainModel.yaml", + "time": 0.0020014 + }, + { + "name": "modelsource\\NanoflowCommons\\DomainModels$DomainModel.yaml", + "time": 0.0035162 + }, + { + "name": "modelsource\\WebActions\\DomainModels$DomainModel.yaml", + "time": 0.0025073 + } + ] + }, + { + "name": "policies\\003_modules\\003_0001_number_of_modules.rego", + "tests": 1, + "failures": 0, + "skipped": 0, + "time": 0.0019999, + "testcases": [ + { + "name": "modelsource\\Metadata.yaml", + "time": 0.0019999 + } + ] + }, + { + "name": "policies\\004_pages\\004_0001_inline_style_property_used.rego", + "tests": 3, + "failures": 0, + "skipped": 0, + "time": 2.0918288, + "testcases": [ + { + "name": "modelsource\\Administration\\System Administration\\ActiveSessions.Forms$Page.yaml", + "time": 0.6622029 + }, + { + "name": "modelsource\\Administration\\System Administration\\RuntimeInstances.Forms$Page.yaml", + "time": 0.6224689 + }, + { + "name": "modelsource\\Administration\\System Administration\\ScheduledEvents.Forms$Page.yaml", + "time": 0.807157 + } + ] + }, + { + "name": "policies\\005_microflows\\005_0001_empty_string_check_not_complete.rego", + "tests": 0, + "failures": 0, + "skipped": 0, + "time": 0, + "testcases": [] + } + ], + "rules": [ + { + "title": "Business apps must always require login", + "description": "No anonymous means every user must have valid login session or credentials", + "category": "Security", + "severity": "HIGH", + "ruleNumber": "001_0001", + "remediation": "Disable anonymous/guest access in Project Security", + "ruleName": "AnonymousDisabled", + "path": "policies\\001_project_settings\\001_0001_anonymous_disabled.rego", + "skipReason": "", + "pattern": "Security$ProjectSecurity.yaml", + "packageName": "app.mendix.project_settings.anonymous_disabled\r" + }, + { + "title": "Business apps should disable demo users", + "description": "No demo users", + "category": "Security", + "severity": "HIGH", + "ruleNumber": "001_0002", + "remediation": "Disable demo users in Project Security", + "ruleName": "DemoUsersDisabled", + "path": "policies\\001_project_settings\\001_0002_demo_users_disabled.rego", + "skipReason": "", + "pattern": "Security$ProjectSecurity.yaml", + "packageName": "app.mendix.project_settings.demo_users_disabled\r" + }, + { + "title": "Ensure security rules are active", + "description": "Any serious app needs entity access security configured", + "category": "Security", + "severity": "HIGH", + "ruleNumber": "001_0003", + "remediation": "Set Security check to production in Project Security", + "ruleName": "SecurityChecks", + "path": "policies\\001_project_settings\\001_0003_security_checks.rego", + "skipReason": "", + "pattern": "Security$ProjectSecurity.yaml", + "packageName": "app.mendix.project_settings.security_checks\r" + }, + { + "title": "Strong password policy", + "description": "Bruteforce is quite common. Ensure passwords are very strong.", + "category": "Security", + "severity": "HIGH", + "ruleNumber": "001_0004", + "remediation": "Ensure minimum password length of at least 8 characters and must use all character classes.", + "ruleName": "StrongPasswordPolicy", + "path": "policies\\001_project_settings\\001_0004_strong_password.rego", + "skipReason": "", + "pattern": "Security$ProjectSecurity.yaml", + "packageName": "app.mendix.project_settings.strong_password\r" + }, + { + "title": "No more than 15 persistent entities within one domain model", + "description": "The bigger the domain models, the harder they will be to maintain. It adds complexity to your security model as well. The smaller the modules, the easier to reuse.", + "category": "Maintainability", + "severity": "MEDIUM", + "ruleNumber": "002_0001", + "remediation": "Split domain model into multiple modules.", + "ruleName": "NumberOfEntities", + "path": "policies\\002_domain_model\\002_0001_number_of_entities.rego", + "skipReason": "", + "pattern": "*/DomainModels$DomainModel.yaml", + "packageName": "app.mendix.domain_model.number_of_entities\r" + }, + { + "title": "No more that 35 attributes in an entity", + "description": "The bigger the entities, the slower your application will become when handling the data. This is because Mendix is using SELECT * queries a lot and will retrieve a lot of unnecessary data.", + "category": "Maintainability", + "severity": "MEDIUM", + "ruleNumber": "002_0002", + "remediation": "Normalize your datamodel. Split your object into multiple objects. If the attributes really belong to each other in a one-to-one relation, just draw a one-to-one relation between the objects.", + "ruleName": "NumberOfAttributes", + "path": "policies\\002_domain_model\\002_0002_number_of_attributes.rego", + "skipReason": "", + "pattern": "*/DomainModels$DomainModel.yaml", + "packageName": "app.mendix.domain_model.number_of_attributes\r" + }, + { + "title": "Inherit from Administration.Account", + "description": "There is no need to inherit from administration.account. Administration.account may simply be extended, this is not a system module. Avoid unnecessary inheritance as this has a negative effect on performance.", + "category": "Performance", + "severity": "MEDIUM", + "ruleNumber": "002_0003", + "remediation": "Inherit from system.user instead or adapt Administration.Account so it fits your needs.", + "ruleName": "AvioidInheritanceFromAdministrationAccount", + "path": "policies\\002_domain_model\\002_0003_inherit_from_administration_account.rego", + "skipReason": "", + "pattern": "*/DomainModels$DomainModel.yaml", + "packageName": "app.mendix.domain_model.inherit_from_administration_account\r" + }, + { + "title": "Inherit from non System module is discouraged", + "description": "Inheritance, except from system module, is strongly discouraged because of the negative performance side effects.", + "category": "Performance", + "severity": "MEDIUM", + "ruleNumber": "002_0004", + "remediation": "Instead of inheritance, just use separate objects which are associated to the main object. As an alternative, you can add the child’s attributes to the super entity and add an ObjectType enumeration.", + "ruleName": "AvoidInheritanceFromNonSystem", + "path": "policies\\002_domain_model\\002_0004_inherit_from_non_system.rego", + "skipReason": "", + "pattern": "*/DomainModels$DomainModel.yaml", + "packageName": "app.mendix.domain_model.inherit_from_non_system\r" + }, + { + "title": "Avoid using system storage objects directly", + "description": "Always inherit for filedocuments and images. Never implement direct assocations to the System Domain Model, because of limits on the configuration of security.", + "category": "Security", + "severity": "HIGH", + "ruleNumber": "002_0005", + "remediation": "Remove direct associations with the System Domain Model. Use inheritance instead (i.e. Generalization in the entity properties).", + "ruleName": "AvoidSystemEntityAssociation", + "path": "policies\\002_domain_model\\002_0005_avoid_system_entity_association.rego", + "skipReason": "", + "pattern": "*/DomainModels$DomainModel.yaml", + "packageName": "app.mendix.domain_model.avoid_system_entity_association\r" + }, + { + "title": "More than 20 modules in project", + "description": "The bigger the application, the harder to maintain.", + "category": "Maintainability", + "severity": "MEDIUM", + "ruleNumber": "003_0001", + "remediation": "Consider a multi-app stategy to avoid creating one big (unmaintainable) monstrous application.", + "ruleName": "NumberOfModules", + "path": "policies\\003_modules\\003_0001_number_of_modules.rego", + "skipReason": "", + "pattern": "Metadata.yaml", + "packageName": "app.mendix.modules.number_of_modules\r" + }, + { + "title": "Inline style property used", + "description": "Avoid using the style property, because this will make the life of your UI designer a lot more complicated. It will be harder to overrule styles from CSS file level.", + "category": "Maintainability", + "severity": "MEDIUM", + "ruleNumber": "004_0001", + "remediation": "Use generic classes instead, defined by the theme.", + "ruleName": "InlineStylePropertyUsed", + "path": "policies\\004_pages\\004_0001_inline_style_property_used.rego", + "skipReason": "", + "pattern": "*/**/*$Page.yaml", + "packageName": "app.mendix.pages.inline_style_property_used\r" + }, + { + "title": "Empty String check not complete", + "description": "Technically, there is a difference between empty and \"\". Make sure to check them both.", + "category": "Error", + "severity": "MEDIUM", + "ruleNumber": "005_0001", + "remediation": "Always check a string for empty based on != empty and != \"\". The first one equals database NULL value, the latter one indicates a truncated string.", + "ruleName": "EmptyStringCheckNotComplete", + "path": "policies\\005_microflows\\005_0001_empty_string_check_not_complete.rego", + "skipReason": "", + "pattern": "*/**/*$Microflow.yaml", + "packageName": "app.mendix.microflows.empty_string_check_not_complete\r" + } + ] +} diff --git a/wwwroot/index.html b/wwwroot/index.html index 3520a4d..3abc9b5 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -48,24 +48,32 @@
Rules
- - - - - - + + + + + + + - + + + + - - + + + + - -
SeverityDocumentRuleCategoryStatusSeverityDocumentModuleTypeRuleCategoryStatus
HIGHMEDIUM + + System Administration.ActiveSessions + + AdministrationForms$Page
- modelsource\Administration\System Administration\ActiveSessions.Forms$Page.yaml + InlineStylePropertyUsed

Inline style property used @@ -74,16 +82,18 @@

InlineStylePropertyUsed Maintainability Failed
MEDIUMHIGHProjectSettings-Security$ProjectSecurity
- modelsource\\Security$ProjectSecurity.yaml + PasswordPolicy

Strong password policy @@ -95,12 +105,10 @@

Error
[HIGH, Security, 4100] Password must require symbols

InlineStylePropertyUsed Security Failed
diff --git a/wwwroot/main.js b/wwwroot/main.js index 6e64bf0..f1d1b5b 100644 --- a/wwwroot/main.js +++ b/wwwroot/main.js @@ -1,91 +1,4 @@ -let exampleData = { - "testsuites": [ - { - "name": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0001_anonymous_disabled.rego", - "tests": 1, - "failures": 0, - "skipped": 0, - "time": 0.0025816, - "testcases": [ - { - "name": "modelsource\\Security$ProjectSecurity.yaml", - "time": 0.0025816 - } - ] - }, - { - "name": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0002_demo_users_disabled.rego", - "tests": 1, - "failures": 1, - "skipped": 0, - "time": 0.0020738, - "testcases": [ - { - "name": "modelsource\\Security$ProjectSecurity.yaml", - "time": 0.0020738, - "failure": { - "message": "[HIGH, Security, 4098] Business apps should disable demo users", - "type": "AssertionError" - } - } - ] - }, - { - "name": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0003_security_checks.rego", - "tests": 1, - "failures": 0, - "skipped": 0, - "time": 0.0025866, - "testcases": [ - { - "name": "modelsource\\Security$ProjectSecurity.yaml", - "time": 0.0025866 - } - ] - } - ], - "rules": [ - { - "title": "Business apps must always require login", - "description": "No anonymous means every user must have valid login session or credentials", - "category": "Security", - "severity": "HIGH", - "ruleNumber": "001_0001", - "remediation": "Disable anonymous/guest access in Project Security", - "ruleName": "AnonymousDisabled", - "path": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0001_anonymous_disabled.rego", - "skipReason": "", - "pattern": "Security$ProjectSecurity.yaml", - "packageName": "app.mendix.project_settings.anonymous_disabled" - }, - { - "title": "Business apps should disable demo users", - "description": "No demo users", - "category": "Security", - "severity": "HIGH", - "ruleNumber": "001_0002", - "remediation": "Disable demo users in Project Security", - "ruleName": "DemoUsersDisabled", - "path": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0002_demo_users_disabled.rego", - "skipReason": "", - "pattern": "Security$ProjectSecurity.yaml", - "packageName": "app.mendix.project_settings.demo_users_disabled" - }, - { - "title": "Ensure security rules are active", - "description": "Any serious app needs entity access security configured", - "category": "Security", - "severity": "MEDIUM", - "ruleNumber": "001_0003", - "remediation": "Set Security check to production in Project Security", - "ruleName": "SecurityChecks", - "path": "C:\\Users\\ops\\source\\repos\\cinaq\\mendix-cli-extension\\resources\\App\\.mendix-cache\\policies\\001_project_settings\\001_0003_security_checks.rego", - "skipReason": "", - "pattern": "Security$ProjectSecurity.yaml", - "packageName": "app.mendix.project_settings.security_checks" - }, - ] -}; + document.filter = ["HIGH", "MEDIUM", "LOW"]; function postMessage(message, data) { @@ -93,6 +6,7 @@ function postMessage(message, data) { console.log("Missing webview ", message, data); return; } + console.log("PostMessage", message, data); window.chrome.webview.postMessage({ message, data }); } @@ -155,12 +69,33 @@ function createSpan(text, className) { return span; } +function createLink(text, href, obj) { + let a = document.createElement("a"); + a.innerText = text; + a.href = href; + if (obj !== undefined) { + a.addEventListener('click', (e) => { + postMessage("openDocument", { + document: obj.docname, + type: obj.doctype, + module: obj.module + }); + e.preventDefault(); + }); + } + return a; +} + function renderTestCase(testcase) { let tr = document.createElement("tr"); let tdSeverity = document.createElement("td"); tdSeverity.setAttribute("data-label", "Severity"); let tdDocument = document.createElement("td"); tdDocument.setAttribute("data-label", "Document"); + let tdModule = document.createElement("td"); + tdModule.setAttribute("data-label", "Module"); + let tdDocType = document.createElement("td"); + tdDocType.setAttribute("data-label", "Type"); let tdRuleName = document.createElement("td"); tdRuleName.setAttribute("data-label", "Rule"); let tdCategory = document.createElement("td"); @@ -170,7 +105,7 @@ function renderTestCase(testcase) { let details = document.createElement("details"); let summary = document.createElement("summary"); - summary.innerText = testcase.name; + summary.innerText = testcase.rule.ruleName; details.appendChild(summary); let pDescription = document.createElement("p"); @@ -214,15 +149,30 @@ function renderTestCase(testcase) { spanStatus.innerText = testcase.status; spanStatus.classList.add("label"); spanStatus.classList.add(testcase.statusClass); + spanStatus.addEventListener('click', () => { + postMessage("openDocument", { document: testcase.name }); + }); tdSeverity.replaceChildren(createSpan(testcase.rule.severity)); - tdDocument.replaceChildren(details); - tdRuleName.replaceChildren(createSpan(testcase.rule.ruleName)); + + if (testcase.docname === "Metadata" && testcase.doctype === "") { + tdDocument.replaceChildren(createSpan(testcase.docname)); + } else if (testcase.docname === "Security$ProjectSecurity" && testcase.doctype === "") { + tdDocument.replaceChildren(createSpan(testcase.docname)); + } else { + tdDocument.replaceChildren(createLink(testcase.docname, "#", testcase)); + } + + tdRuleName.replaceChildren(details); tdCategory.replaceChildren(createSpan(testcase.rule.category)); + tdDocType.replaceChildren(createSpan(testcase.doctype)); + tdModule.replaceChildren(createSpan(testcase.module)); tdStatus.replaceChildren(spanStatus); tr.appendChild(tdSeverity); tr.appendChild(tdDocument); + tr.appendChild(tdModule); + tr.appendChild(tdDocType); tr.appendChild(tdRuleName); tr.appendChild(tdCategory); tr.appendChild(tdStatus); @@ -261,6 +211,26 @@ function renderData() { } else { ts.severity_code = 3; } + const tokens = ts.name.split("\\"); + //console.log(tokens); + ts.module = ""; + if (tokens.length > 1) { + ts.module = tokens[0]; + const last = tokens.length - 1; + const rest = tokens.slice(1, tokens.length); + //console.log(rest); + if (rest.length > 1) { + ts.docname = rest.join("/").split('.')[0]; + ts.doctype = tokens[last].split('.')[1]; + } else { + ts.docname = tokens[last].split('.')[0] + ts.doctype = ""; + } + } else { + ts.docname = ts.name.split('.')[0]; + ts.doctype = ""; + + } all_testcases.push(ts); } } @@ -291,25 +261,48 @@ function renderData() { document.getElementById("total").innerText = total; document.getElementById("rules").innerText = rules; - - details.replaceChildren(...ruleItems); + if (total === 0) { - details.replaceChildren(createSpan("Whoops! Nothing here yet", "pico-color-gray")); + console.log("No testcases found"); + } else { + details.replaceChildren(...ruleItems); + } +} + + +function djb2(str) { + let hash = 5381; + for (let i = 0; i < str.length; i++) { + hash = (hash * 33) ^ str.charCodeAt(i); } + return hash; } async function refreshData() { - let response = await fetch("./api"); + let response; + if (window.chrome.webview === undefined) { + response = await fetch("./api-sample.json"); + } else { + response = await fetch("./api"); + } document.data = await response.json(); - renderData(); + const newHash = djb2(response.text); + if (document.hash !== newHash) { + // console.log("Data changed"); + renderData(); + } + document.hash = newHash; } function init() { + document.hash = ""; document.data = { "testsuites": [], "rules": [] } - //document.data = exampleData; + if (window.chrome.webview === undefined) { + refreshData(); + } renderData(); } @@ -330,5 +323,6 @@ init(); postMessage("MessageListenerRegistered"); setInterval(async () => { postMessage("refreshData"); - //await refreshData(); + + await refreshData(); }, 1000); \ No newline at end of file