From 06aa99e0881139768b87f5d50f91823dc9de5e98 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 10 Jun 2019 19:26:46 -0400 Subject: [PATCH 001/544] Prepare 2.2.0.0-RC3 (#497) * ESAPI release notes * Prep for 2.2.0.0-RC3 releases. Update to 5.0.0 of Dependency Check. * Updates to prepare for 2.2.0.0 release. * Updates to prepare for 2.2.0.0 release. * Change 'import org.apache.commons.beanutils.*' to 'import org.apache.commons.beanutils.LazyDynaMap'. * Fix lead-in documentation and add static setCache(boolean) method to allow disabling class and method cache. Also, reduced initial size of case. No way we would ever need it that big. * Add testObjectFactoryCache() test. Fixed testMakeCipher() test so we could actually tell if it failed. * Removed empty initial comment line. * Added larger-than-life warning that this is a TEST VERSION and not to use it. --- CONTRIBUTING-TO-ESAPI.txt | 22 +++++--- documentation/ESAPI-release-steps.odt | Bin 0 -> 165550 bytes .../esapi4java-core-2.2.0.0-release-notes.txt | 51 +++++++++++++++--- pom.xml | 4 +- .../accesscontrol/DynaBeanACRParameter.java | 2 +- .../java/org/owasp/esapi/util/ObjFactory.java | 27 +++++++--- .../org/owasp/esapi/util/ObjFactoryTest.java | 36 ++++++++++++- ...ESAPI-CommaValidatorFileChecker.properties | 32 ++++++++++- .../ESAPI-DualValidatorFileChecker.properties | 32 ++++++++++- ...SAPI-QuotedValidatorFileChecker.properties | 33 +++++++++++- ...SAPI-SingleValidatorFileChecker.properties | 33 +++++++++++- src/test/resources/esapi/ESAPI.properties | 1 - 12 files changed, 240 insertions(+), 33 deletions(-) create mode 100644 documentation/ESAPI-release-steps.odt diff --git a/CONTRIBUTING-TO-ESAPI.txt b/CONTRIBUTING-TO-ESAPI.txt index 5e5ec846e..4154a5916 100644 --- a/CONTRIBUTING-TO-ESAPI.txt +++ b/CONTRIBUTING-TO-ESAPI.txt @@ -41,7 +41,9 @@ Required Software: We use Maven for building. Maven 3.1 or later is required. You also need JDK 7 or later. (We generally use JDK 8, but compile ESAPI only to require JDK 7, which means our code can't yet use any features exclusive to Java 8 - or later.) + or later.) [Note: If you use JDK 9 or later, there will be multiple + failures when you try to run 'mvn test' as well as some general warnings. + See ESAPI GitHub issue #496 for details.] Building ESAPI: https://www.owasp.org/index.php/ESAPI-Building briefly discusses how to @@ -73,10 +75,13 @@ Steps to work with ESAPI: git checkout -b issue-# 4. Work on the GitHub issue on this newly created issue-# branch. 5. Make sure everything builds correctly and all the JUnit tests pass - ('mvn test'). [Note: On occasion, there may be a failure in - AuthenticatorTest.testGetCurrentUser(). If there is, run 'mvn test' - again. It seems to be sporadic and it is hypothesized that it is - related to some sort of race condition in the JUnit tests.] + ('mvn test'). [Note: There are some known issues with test failures if + your are running under Windows and your local ESAPI Git repo located + anywhere other than the C: drive, where the test + ValidatorTest.testIsValidDirectoryPath() fails. Also, if you are using + JDK 7 on Mac-OS, there is one know test failure in + SecurityProviderLoaderTest.testWithBouncyCastle(). That same test works + with JDK 8.] 6. If you have added any dependencies, please also run mvn org.owasp:dependency-check-maven:check to run OWASP Dependency-Check and look at the generated report @@ -98,6 +103,7 @@ Steps to work with ESAPI: $ git merge issue-444 In theory, you can do all this 'git' magic from Eclipse and presumably other -IDEs like NetBeans or IntelliJ). From Eclipse, it is right-click on the -project and then select 'Team' to do the commits, etc. If you choose that -route, you're pretty much on your own because none of us use that. +IDEs like Oracle NetBeans or IntelliJ IDEA). From Eclipse, it is right-click +on the project and then select 'Team' to do the commits, etc. If you choose that +route, you're pretty much on your own because none of us use that for Git +interactions. diff --git a/documentation/ESAPI-release-steps.odt b/documentation/ESAPI-release-steps.odt new file mode 100644 index 0000000000000000000000000000000000000000..e75c8599c67fc20cf6ba60c280f1af0084ef9eaf GIT binary patch literal 165550 zcmaHR19WE3vhNp6j7c)FZQGgHlT2*ewrz7_+qS;gwrv}4{`a2q?s@CI^?I+`t=(0< zy1S~Yd;jF5z`%b108juxKlrGu_8=1+IRF6ohyE4;tjw&89Nlb<^lWV{%?$J$&1|gc zovjV&Z1fz=9O!IpjjRoA44kZttR3kbja(h&{tspm1T)2!GyuRqgz=Y|qN$UWzO|m2 zr31a=zms&f)+S+cvSRQsSTKJ-ftL^${{1%|1OPw)P+)&kTLCZj000<3PFhifmYj^1 zih+rOh?9|qjg_5-LyVP6nCqtmzpR!BFT1P|uZkFth?umHjE1y=)^90Eb$Ll;Wi@G4 z6KO45B~2r314|VnCqq?f0}Uk$9RpK+O&232Gh-7YOBV|VH%AL2M{6f{dkYt5S2LFo zSFa#r<9ch`RyWsn4__}Uzhn=;5Z90tm&jbtxFT=AK3`icUn|`Z7qcKwmv}GpSRV&} z&wv2W_Ar0Xln}SPD7T_`@6eD?|KNnch=joC^oYp#h?umfxai2Zl!VBT_~`hQ_{h|h z)KI_9u$27BtlH#|&eY8OtlW&4{Ho}jnm>6Z33;`#g>4BXotcHTnWgoA%G!!jBl0p* zYqKMAv+^o)V@mS>H0DLu7yZf4&(A9?D=aA}EUhRl%quM_EiWxBFE7t8tSu<7&#!1L zDQGCKtShT*tEj20sB5mPD6XlhX|AoPudlCbY-?(1Y;5glYpiQ&YH4q6Xm4*14jqV0 zn21TAi;WyeP900hnoY0nP0g9htR2kH7%eFnEh$?lYZ@qTTd1p;Z|>-CYFe#s+iUFH z?(FHT>Y1wSnQiVJYUmy7=pE?jAMF|*ZyQ?Zp5E&0+wYp)?XAq{sm=v9mG(B(_P5vf zb+(Ok)D3sG&vaBychvTF_x5+J4)t{`_E#?qweHV$_4oJp4UPhb2Y@5vBf!4l!Qt_d zfw8f%9^iC;-^Re$Z2!c{aR27$cq@AaC#Ovvo=06KR&lQxx6&FvOYUDFgrQA zGTt{kzOXnuw!XeTGv+uvp9CJ zGIp{&bh19ay)?VGHn+36da^ltu(Wr!J-N3%v%j-`aJq56J9oCXetEocd%X5^y}9={ z9G)BWogD6+?q8gr9Gsn<9i87^UY{LaKkeVXp5NS`-@RNsf83t$-&~%)UhZ69 z+&tZ#-QC^YKE6D>Jlwy$zdYW(JU+j_-hY06{#AspudjNJj~xJjl3YUgr=siXMFza% z+M?HSb90x~#o`s&3D!`Rk21~> zosn8HfBd37(;6)y3<(__-6i7FmmBo1%eBqGEjMcHd!J>O-c>os`+e+a0w#2@_huyXxLtXb4zZS)e) z5PsHWJLRN>|Khoky=DAn`0?DH{c)<*%oAMwwLVnB|D1TW%CG*=sGiA*o@4mJN=B38;Dhr<{#HP*bmtqM-8;EScQ1!78-R_fZST`Cg_jtQ1O!1+XU61)x$$}n-x&(RgtQ1XBDOo9U-9=rw>=Ct7@#!thrZ># z32EezxZ6+q6I8`nDKjl{u4MY1T4|A?Ra3i$z|l}ol5ei5$CB0Itg|FpJjy*B0o0TW z!*D4fAt8L3BiLoip8_;WcK7xxIeFU5EfK1hUUiCP(&#Fe?Zv+-^zw-f5^vJ?miXqP z$yK)AqMW{gRp-Ifzc%e>u}UdqQ*3n^Zo>?;bbhXqK6y7kdt~?PsMcLxVq1SW_DgzN ze2^z6bkV7GIgaF#vU_>IaNsf9o}>k1Te~?)yuglKwSh8PuX#^&W~b44UoMjKNjQ0( zS7~%lxBqZ?|2*WUA-$$$rz!uUs_h>e3b{b=++N+WNH*$X=-Ig4p#HwcvG(+yy%(-% ziFu#&BG}D>)i2^~Xu}u`x%L(V*W3_#ThV!vC#048^E;L$olY7fs!mBz@V^xB#!ok~ ziZAlxp(VB7Ac#_v;`-O+>|FQ^sOa^d%rl#&KkyK_GxD!!P&Zj}M ziJM82$&ScM6URW%J`8AD)n18CMJ7pCGB($`h&1Wzuq+rF!!almshuUNlXz_y#Bsy$ z)>^^Rk~2e;lp^u+ecMY`F|73Y?)TINzZ{b)G~PMyuWA3M!uj4y#dizME-RXR9I-rD_e*oqjAbO z#DoXKv4*$``cbY#9+2rg{h`ckU?lM4@&GIfETZqbm^+6{b;!*xF%2U5=H@7un;4JG zSgB2^7zKG}t78*um?xV_TkZx1|aNE9$ZrY~v6Jm%*B{)agmHFBRx2{afI{w|} znF-SroEMLb`bs7EDts5A)q~ea{+Jf?;{}i6O@U7Wy201phl1c50Y@}G~%m+pp{12hZ zqnzCCqDv*ZNCKE+BR>G_(mnKP3!atgV$A8gCgX{V;XG0dpDmFqEPW~jS~aod>i*Pj(4d5dlz>c%(-zT^D%Uo3L| z0S63@>N-^PFvQho{PRw@XH=7;-4s;xWu_c{tfYLah1A0g_VzbHH#@?7T=FV;94EY2VPDhQ7LGzI+?0dn#&xJeP5q5g6{lGY0?jqB^f>p&fMIV%3BfL7b#YC2=LI<~WQf1pF`vVlIHuHkzSQR~gRFZ<| zgE!jN8|C?qVOkiCqk`e^WBDAwj9LlY?J%>bPcgMU_(Xs_LZvuV@|@P-=Ym+`2T+lv z%+1Dvp0faO4Dv6?(Sz(lt>D%!JyMK)qb6{RWoiq3pBbdv$WtKT>IC80y%KOylk|de) zTZ9G4>KiEp1jql#J{;a|Yh^m5|G?19ZF|7`>uEnz1rhv_I z#H28oDmkg#Wx4@)da3l@W|xqno!I(bXVw^v z)kajKHwr+|d`Pvxd5#5cE<c(yvu)7P}M< zFr+ot7tH8+aXmEecU>e5r3L-bVjR#(su$MEOX9#W+WZGYr>|=Y1bgU7A13q26$jNZLfq03vm}<>A1KUWp2id@>Ca}_ z{*Y;%qM#u`aOh>r(d<&cjhd2nGMGj>lUa~~CsIQtau8$BLE(Q|D-%Gpe&L^XkXbs| z(Lr%u*8$ls4+0^M>p2)8Q!Ce+t+x)13BU+_$y9(Ta0=^W%wwEQN}Af}mg=wq=i77S zJum}=5fjwQZwRfwf8hkqSo8+Y*<7FdDQ$8KU2nWmhK%j4!L?DS@E?rn739G~uW5Ixc~Il`SE$(9gU}`+Y5+Aexf2h4cLiQh%CgaUS3^Zom_>HfQEOUE0JibRK!q2vsPS6 zqhGAudaLOZmG%7E7D!neoX-6zp{O%&_!Cyk5&6yXwNwm$?PI=bL(Hea2b5f|? zJag7UITpal7?;qs7wJ$B7W) z#p=U#O84h9v5wn5bvxdQ)1J%cd2V;tjm@VXI%cgF;@8B|*Wnra=gQL8QWpQ)?H1qt zaSHz5A?|v=&gOI6Q*rh>s132{xXJypGyMI-!JL4%=nd#N6{;E?A!$>`o)_mi;lQDH1_;D(0w5#uwPZi$HLpss@o}xoL zB24e?E&2#6R&G)EnsOoq9YI})*z>#eDCs?(V)Oo;=H}goDqf$Gk1t`2kNv}$^@BH) z=n!Sq=xLXUe1?wFzh5>P7S&+NPXD}Bh^=Yv#6K_dxx#8rKH-?o0-+jT1K2AQRs)9c z@(#I1U=skdcN*E#Kbc8R&pt?4;|F_PDN@t%R!w3lf90|{h4a+m&b|J<)=k!a?Kxq3 zZ~K?~QrkD_qdF?UqUZ~*bN4Dng##m6NdGCdGd@nsItFvFJOdX5gW%jsUnHantZ9RP zk!In#;@9d)X)}teBrQ>f7$>p|P;#ViO^IAJwQrR>m@8=hUeUei2ZfGxqzK6AVXpxFCi%@D{`u?;|wDyg>DvQp+H=R+^-$?jTYDP6<$IPoTTJa)}w)Wx>8v> zZc-o(t?$q4!COGP4(DX#! z@-{^272fK1U2KA22Eh8k8yDN9fnSV4CwE2`MJGtVyy*rZi{N&^cMF^an`pK_n%ZA% zlO(!DeFGO1819>cv#01GR*_)w=D2+0FFA;FOyM=kDBtTmw#*AK&ZIm&4?KpNu3{K@ z5={R@mUc#)xS!l0a$I9eVU3k0MPh2#@{(6WxyrG&Lvi zyJh4_D7~jpH}@3BN~XJEuT5(9p+)sLs;I1elBS%BaFK#_@it91-|vJ}$iC_|jB!^^ zp$an(z}f7gw8c4q%`+HM2?%& zD7bAI*W<>Jc8VfSVA6kfa`1B{^_RF8_u2L`*dpZzTyzVI78S%`lGQ(L1^x9CUH#WdWc)>E#yu zMI}-{J#i`ttvHDqZLiqn_{^}meYnQe*w(3*6mCO0{$vo9s;4<@?7%wfvRiDw#DDH# z!6xEEntmcu#}8wQCoG$h*SHY7#LiP6j7@D2E1mD64NT=TJl#5nhQBkS3wPZJi|Lm? zzul+KoO(z2Yq8DWX5`A@7Tl|qJKTDh-R&R>$u8q>$$WTyKvAi*JeoILr~9_{McOdl zufpzvt~J}Wy~9CjWUv&u6ZArTH&-^|AyFjsqN4JrS4@Rv*J4xYNYs0OKRLB*CebKK zEbOF{^un@#-G*&xM+%A6?8px^ad=sg>?+Gr0Bt| z6re-a_8Hh(pIgzC>>}$R(1lU&ofN4^-fWu%W8!JM({TAb6jP8*SrL z7ru|9M$AV>f;lZn4HZtz5@*K?A5pzC0Xj+w>m$mByYJg6XG)i~_RC)`gum+lrkPX8 zx84V?mm*T$%)gs=RTu64>%O2CDzZ0hlG32Ux1VnU^%Q-U_+*D^@$Zqrl%&!iqw1Kn zDrZk0;Y4IZ{8)i2I=VZ7I*Fl#&x%k-ur1djf8pfWHwU)1H|2Fj+sK$j2dd^Qe zxpsN9-OCVG43s|_ijA-?R~7@`1B>aP5oPD=7**PgCpZ?r>sR=1NMO5$X>X501*sX<)#W=f+r<-$u{ zh3G6Qet3zn6?ay{u%SkLoZYjXTOI|TtNy@=L9}Yyc{Sd0mVM~HpucMsb>I^x#&GDN znal~$KJSY`Fl|JYg&Ye>B%YO$4QH-^=J;F&G?Mmf6;El!^fD)N0eC$F#vfg3NPj9F z&A(Pm1tr4l1}{=d98s@G=bb-9>+03)+@zd*9o_S4hts6+`1XfJ5I|9s^SodGW$x}2 z93V2M-urS7;&dU}g`%RVvvFpLUt9598c~GDb6fGf>^WVvc;WWcZdH%JPN&lk9j60; zF;tHbefyJr@Ll<`T_e+_;bV^t_;U@i51SjOV+||^_G(QxDE6Q&WISq}h_L}J$>Ln5 zGvhc%gIYpu7^JgegqNP=6^tSmoP^fS@wYfU+{^cIAgi9IT+eflS2F3ZpS<~4!c*BHbeR>+U$*SPD!>mse8+* zY9A)YQ|Iu}ofQwP@G`FaWcUmjMV~x%FPzcRx0GO$U(Ag;5{}RqZ>t38v!35DVspk{ z!0NCAbYT(L#qa~`t_2-5LF2v;Ms6P5$*zH%BipJ_P}o_A1^kW;@jMeJ+Qmkj{rEjf zE|`4`=^GVaB#2rr6CTUsY88n-*Nm_CXw@^*L;=^9rvZ+M$2(mHU6I#?4Rc*dGK4t% zy+#i+T+^KqBix9%iI zvy?^YS0dJ zzc6E&#WBWqD0~{WI0?{;@B~~r*ewedI&w|g7vk778ycmJY@4nf3@}iQ3wHX57@p zo7jRy1$>yG7+!{?Ncv6OZyGh-_vby#M~MEED3M)m`9f?8YC_L9dui3SB+i;<|%GNMQ)No}P; z|JAJsIT2wZm>Lmz*ivFia%VQOyfRowl&xRF^O^nn%Y@}18!;pmz`A&vM=}q&sm?Vg z`KSmK;@Q-aW)is+wDiGJJmhUMfh%bO6p|aYBO)Tr<|B|p!|6!EDSunoVoMu+n~MOJ zhPJop00Rq1ZJTAr4zfm2e7Pk<8sl1|*|kySY~U5NjQeqHV^pTTCtwvoZ0h0 zH*CwzyJIb>#(2Db$>>4Wrn+2<9K=8poAh_KB#9U*arRg-e0(fsTP~^nOaE|R zS#VrC7K81MO3CR$E;i(u#4~ydQD?yCT)jDOoOA&?3O@U;3i(hqLS#oFwu&m^{m)DO zn@AR7+^eSLHZRRCm%B$YI{iN_k~B$~kr0pqCIgP6j6Y5e1t5?R1BXo$EmgwgGSf{6 zOV8#{T9fxdyOXlJ(izOR*#5ay>Mq4d*nKzLOFBHIgV^iY!0aMF*TblNWyl%0R|>EF z61Q_;SKTC<^&XQ{xirU(qU4k(xUn2LHi1Nh1(kdr>T?;-+c;XoR#B7gbxujS?{|q{gTxCWIjp21kMm&JMMYxSD!@`$Sl%A6pfb!Nik)1O8cRCQnq56VzE%OC(}58^xpHtHJg?_8HTJ{8EV z5!@Auqeu;e4P=HFrZp@z6cY4K5Y1OUed>^>m0?Z=$Ux2t1T4#@h02#Ku$4b}s(RH3 zE~7uwllTF-tKBfm{^f1*Lz@IYNM&6S>~g&>D2%Sq3q6q%<>G}1NX%%mb zPRk~7OA^~g3RGltWg88~t@)Na6j#6)-i%&|(VKFL=Lm@p)utiGptx}m757LQgYG?r z(#${95V}u8u^Uh1|Koy`cj|k&PEzrB9s>(y7)uyVYY$ath@WKc^SK9$d=g|Luk zPJ>{)Fi1wSD>LS#>>V>UT&6s(HF+T7=4daRqs1S$$>`%;u`2O3TZ?*QUO^_d46a=J+Qyse zO1*H!9|#SbxqPi)u~^jDSXrC$e-3?K{KyGqH9c)ui8D?BX`Ji=3M(j5@1BMgfuMpU z5ve=Xph7u=f8TJ#|E9LYJ~ES@Po;cEX4ku>&j2_wYqlYVS=xddS(W|t4cd&z!>MUn zMQqq~4oI{A1n-F^|Gs((=rJb?4t;%OuCgv@%d6~x4Lve@#UoG`wCtHs-mXi>vLX~# z=+Vn{A8`WtUg7S|-2k!>eQbi|SGv$NHKPSw{$rnU>e=|VQf54!z6R9oSZdK0U)JGi z-t?CYW5x%<;-tI%k{QXu3powh0iHtm@`^7YSQKEFriRBJuAbZ%_lIRZhT?*bG)>@x zGpJq|3REg1&kG1HgxZ&)RsLZ^N!-c-5_B^Ng z$>jkJf$nF+rhZzo=tP3C;?oQaY2X?K`QAKP2R`qYH2?b*Vgc`?+ebs|4qYX^xe58oxYVMcZ|rh;Rh05a_qyeWb28h?#Q@cESe{(i`I~C_Ii>&3 zixrUydQ2~bNOD0nj}%=qJTjaqBuzLdv&93mV89%RA_6qtEQ+lDIuxgo-x{)zJXs%w zbDY^>|3qM{UIA|UhX#w((@Y&Ps9@^t@z+krPnju~PgQq1Zt*D;!@86b z?3H?Y1?Sv%lM&$WEglURG|g!PLyfLjsnDm4IopOG8f z)vK6hT+NPg`HU8{WkV_|u)o~gE9mDtU#tz$1MeNs50*=t-PF?W;7`u|yG+oX-{)tF z>34P+jDbFSYYPv5hK$#yjHd)9O1J$lvroj>xQCdp;!OCJ-&kX8-Isr9)tsBTrH6Eb z(|5@+&YxqVR;I_%K+i02mWo@1&2@6@Ih2dR(I@g=+%Fp*)rZ`tQ735U;M9K`nv5?$ z-wmQN4~aBQk#HSSz+KCXF9yyDhBd%BcM&LIn(D%~bewIR=LDdEn|Ki52&QNL5X~hv zy)v$&*gjMPan~qGEe>MLHUghS<4ok$z?aqW<&+qsnI4Py^~V=Fbx~>n1x(>yHfLeQ z5B!nya;;$DL20i3duNcaGduk^Y5mN6>lNgBFbT?VHuH^OydqMej$71IO{HH)QG zlGW)-h{cJD)#35!z{)av1&gor6!9el>H={>sb48vXHUp4;5I;h{3762ot|-a=c7EOcjE?%K3jIM)rq8&Bf;NyKg(uW8l-mdOLt~mvH2T41 zEIkH?`aG19$usRN*iJ|Bm`G$sP8de=4>nJMOGlrS+(xpcV(z-@;LXI)X+*Q4?CJmu z$C~)mx$~&K5V3?eQPZC^<#?BK1L-y8CI@PXB1Z0Pxrx$amP~aCBI6y@MHw&)3~B>A`N+z&B5X6}VnA72 z*UNb{3!h3oNL2~Lro&Sr0{{us=Tw5OWm@uP;l*`C7qUEtlZaJe3GZmdpOM4jno1yv z!tcP;U(qn$m?L!*;3BtKHR<~pLum)B*z!3DEO$<)4spri;>iZ}dBaYlRFU*1O|WsD~O(eX=DgC6x6& zh`KAuNNC1~dsLuJnv$JYR#YW;01uYUm5Yi2&dxsIgL`F$|jt_nj`=vdM-=-!`^9HQjEgvflX@sCn4s zOTHTvXmafF+feO;nkk@l+8k|Js2lz~TM-Ekexm^7a$eH{Om|}!M-mMj*h%w#hNB4V zZO-*hdpag&Mfjob6P}#Aq2KY=4eRu>;4jmI1~6MOI(Tqj>SDBIwFry=Rn>~Y;H~+g z088%5<6dO0QYODbwr%kK#DFrdoO5bTWbI@+eNUU~5&ddg%lEEHdn4*(*n^|$laEN==WKLoFWrQ)f{H=Qym-W8<81`Tlm*;A)FEw*eCu4PXZ)FWQD5)b^ZSvp$r8D^o3`~s-40R1DmIr%!zW0sw^bkTBGW2oC%EWB4@z#0fnT> z0L(as86k#2A&fozNWw8Fqh$s`r$GQ_6~hS?!H^8rFxV)-4+|Tt-_98V0+SOW*dC-L z*iC@-P4Dp%)ZWt<2OuW}31#&M?b770__n|Fe?q96b879l4nP3^5bHm+W(JN<_C^l$ zj12z~*jWBM!^|eg_ET7dk@crAD<_8tE9Xz)|2MuGLf|i?+5!snFUL6E{=B~tguaxR zFyQN-C#SPG0RSKX{1?hJ<9x$SQ&H&+aD6msZ<6{Aqv9GcyIi^Z&FKSu`H`bI{Pv!|yVxQLisseu)GF=#oh`M0{ZaBF5%`}gzs~8aB)SuGd zFTA>Z?pvQGd5?IG#^a_;vL<=a*{oJck|aS01HTJG{nsm(JW`QrqrnX2`Q_zey%ohl z^}oyisXT_>KRSX0Y+qh(&cqS??CicZcy z$?-o7r@vZI#2(c;tXcg?6QR~qjCyNvJv%7*$R#7viyY{X6Ve@L6BkVcs zl;3%FfD;vDHTULT)aCyn`WHc`tcJ4lQc30UVV>LK+BU(VJ!jyeKw??Rd-!gJg?Uy~ zKt#kJRsA<`G@EgAP$>d5Kn)EnfCljMJ3!z&cyC?BOWO;xKZB2G>eVwOvg_3QX~K;E z*jS3?m(+x_BTEUUUe9hNJ{*Bfb$0!&|0z;ePE#4=zh}Xn>A1mM#;P=m(#KcDssw*! zE@4l9=*Si$DY;WYws=dPe5-7Pu2O#CdjULUW4%vaIJ+LJpNJVe?}z*S3HIxFs7YrK zoY2PR>&~l!%K~7U*YnT>He|WfLBZP0{_@BBgUaf}xMi^38hZ%P!NX{7_2kmYr0q7N zK;iA{O~agYBiafSFir5o!S{!~qkCsMX+c-M=u=53uXk%lC3e?R?|Y>U-ACuVCjZLb z#KTqx1(@&ng9AT@%H6z?vMU0w*e^VGqWP)0Xh=~f3E9(9-uB1hveYJXUp6KHAW}Ym zshO%@eAy9`#cD9sOAb;Rer>+Y*6nctE3--}>6A%ZQ9Y7|DMtm4T1gwI{= zR9DI#I@vf2jCD2Z zKC~+i0AQe`hN?p5@`%Y~6-NG)02GsyzE~(wyG(Yoxgvp6S0_lc&B)IDeEjRMuG}m+ z2D4cgcLe~nh=ert_Y&a%*W8Hk32D{#fuOm<&yIfw<~9_l(}hL@3Q*RlWP!QR`T*VM z@we>9T@3U@002Vh(O^y>5o2=3j!~(}o^3%)Wg69WuT~9AOUCHQg9N);@%*XHmHuOJ zDj4{u3MesF=dOFfH6YUh$3S$|{I^jm;-nNbqvM7^#3&ifpknmlMqYvn5A?Gyy?=?{ z9Hx`iU<8Bs4i69a>gBRq8{%=N&7U=@TyQx^)OHTr^t5aOWxBzcLuf4n;UT-Fgu=zV~s_Rjk^v1e;n!DxP-seQN z4xg*v4+03tF6t?%t4({!jyHIz`z-6avzsWY!imq=jUF}f-P^-@6g52ec6Vg>2hcIG ziC9xH0CGU@PFDr83%1vKd}9xfAV<{q_-bQ6YS;`8m)Ag$OZ`5%bZ6_KL!I3S${J|( zHV+9^U@g3zb%OTk*=V%{|MSPF)>0yz?S+TPR3@9XfQd$QF3m?@*Vm^f*RLsi+HXCu z>TM1Z7-^la3)OXRQ<_};uz=tXk1+-Y5}H5lyXl+uN^C!+T`dEI&CdbCaSkRTV^Qw7=`i5?)Mx;^UG3$$m*tzK9%unX5Ij2V9`JZokLe1`mdj z+!khX574eO@w-Cfw0nHM?x|wJ?o*9((|an~sD%k=1x~MLvv0zs^R%VFn$bwST1~VO zV`8CIpn?dld+qlj=J#{mP|d|$NuSqVPvCKCY?aeTRg@D)ZEglc3P9J-aS=j)bXHT*=B}xvTcUImPu{V zs``|sg3gYt{ZiCa%{Q@%gaa@{3O!fYkOjLm<%U<|sJ4HK2M0GU-fCq9TmyD=eX_vy%A@Jh7pn1%QDhhq{CA4O#~Uq^>J&m!K|)AyHcbG4 zPKN-A3w+|8qpexa=SWh&u%%w-X_BV~2?pCazgj0}pb-g*G*WH=3@|us`?po~T;LRc z)?yNEmosDUHm7S3td#Qi(HMPeq`!*a5|z%+Mb1bW1rK}9y7LhfgzuF^g!lDAKGz0X za(QTm)8Q^Mp>r_cS;JXaUkDfu{>Dd?R^MX^N|1XiIy-T?3WuvhamLd7TM6CqFT3ig zv+{ro5`g8-#T>eaaOW!L^^3M_smelAXUH3J&nB0H&{zu8OBA;_sutZT+qjQy&R8P} z{E~rq33cL-k-&iN(JQJdPL7icPJz6y^0ud$r?BR4e-|Y2E8nx zZJL++E|P`5IPhrspb%Nv$Z>ZKx#kW9oi-|nQXDUo^(R!)J-V3pfXX&N zHlsU{v~jYVVWqT|p94=GF$DGP>FHU|=uNaf_Fhj%ew4qCb>Tavu7jj{ zc>B_(EHCsc8b33lXw z)A-ujx*&rpW-O=t)-FGK3dNW~&3PUkyqZd$-K7~pzXOGUb6!s=Vo0gx?9vAw!X=Ci zPq|FqT%yb<%6G! zYI|#cV21|?Ji4y@R&qX)#lQq?ucpy`TyHMIx|yR;@2#hDap#z{AZkt?W1S9tQ#d^l z4pN@l-1>=^WXET-eAWfV-5bPWmJL!R=ea*MIt}IfckSkI+`bfSsU;J%#CJ)#crI^s znvfrUS(}a9nbUhdtSnKgxA$gWs?E=}hG3VDR2k8t%MU{auYk>Gd>KyqRG(yuR=*xz z;DH-3FiiGqU`>f;=4p7&M<=#(feK7SQMZ~8I;-G$Y0+N4gB ztI3VkqLr&#n?WPuG}53~oFe{+FUdv&ayl~ADi+vB&=xNIj5^w^XX@@)qlx*x%&LBE z0A^*lCm+&gW}gsN@2I;~?@~&K&L7Ui#sq3@Zpg*LN%W|F+}VelBCvlNaqtt!{@b-Z zmj8ja4sBm`aWaAwAGj)9A6Y8aeeA#PIA!g{wNkG*>72)tDFjFX1NV(J+`thaQ$*ii zj}Jdj|FJX^*(>b&luc9TV6hHvRU%J?Y_Y%|SWp|ZtDe4yMPN8wPQ=a5^(Rlt6mkWy z)H8*VU-Ue?TQ=G*_287X*;9A4bAfph|=m5vU&)+v>I3pufIm9i3^(y>dP9 zyHt@XZdrnJ<3lS*Q1JY4Zp3+F37?DOJ3zlRK~=S2t_XNOdCI+}Lig=adAw`Py-ThS za^{1&Mwkb12Sf8yW^yHP6>sN~m~V~mUUs)O`!TCcdBN19{O|07ny|Y708Z~d%ICwm z-;l9AZ1BYZ$1KJN$&3#!5eKsRdP#0pvP#ypViY^rl0R6tt&V0JEf8%m0hQw2V@Oay zMQd~DKu0@RM+2XR9|C){$pSs>)fn|iFr?rBy(_C)Di<@JF%SR+PtBJ-;l#n3ZLjT} zYZ+tCubw+E9&mYY9tLgRluDZp|B>~dbiX{fUl62EnJfO}mz~v2%DF$gv-%LQocjUe zz&kj+7DG+iR@p4s2hrDHg4`W4jM%AE{e%Hrg=hjq-@`0}uom@^u;1wLegj0~iU`03 zv9%tf?2mW)kf3n!7_y$Yl)BroPVR2G>)#|f0gekvS8Q1{44{BmB0flf>UKmBXjNNH z3C?+DrZB+sH$^RyIvy8d>*B2wdMD5nySk^Y7RyauYe)hK9pFpwte;ymvT5t2<7j8! z<(C*cc>A06c^;miTVlu!@pW^IiTWqO&Ph27fXT{F(17^IHH(ru09uE}%QoZR`}%Y~ z!*Sy!0-1{#g3upJk%4PJUSt$R2>7hs-9z|Q&Fyes0D&rj?-&RI$m8!m;AEmoA(~{M zOn7}mw2%cLolcLCgC|VGRYoapQf^Vuj_x>v6oRf76fk5$H)MhK-4meeXC&>8z%ZfF zJbX)bX=Y~D`yq&Xfje3bPX?iyyjadUoA-m1IhG_-2fEIvRWm2by)jFumP8^PYT+O- zvVTZ6Oqhr-o1!~+w%m-s?RV`9Bqt$~gIS|CA^;8u0Q9EMyLY0sMEW8gqN=)ES-p?8 z2K8%aaTr<3rPQP(yFqXMdAhp@CZ&Av(9L`{-RaHxT4l?(ur)N%*<&QnVBecVz=4AR;H(&G00Io6o$A$)(Y-wYfJI(IxFIaa`jb6Z zz)Xj}bn#L-JitkYy;`ywv;EDWaw*9?iP}R-aYt+3BtG!ePq*I2Y|Lb|v!_LfPY-=NJ#b%O}qs@skDj8YaiZE?kQlHf=v6C$f^W zN6h?iR7rpsHzN9C?Bc#sfRj!`m%16AfI&atbc77oXZuOE)ZEdXRjj%gq^Drvbiutd zjHwpTgV7iaIT-fujh7cDQ<<7bMxSuXRNp+YEMm#}(aSD;db=$3<=3f}yMA1RSli{ea>PPfD} z@%dh(4d32sarIGd-|TBK)YXL_usn3j!|}d2A9B*2nC59s5njPb40^+m$z!2%UXd17 z;JWZPLVvZ+pFOsm6a&q=Tnit54M1C2LEU^6OdF@`!;?#ET2%r>P#c(lSylKE#U3kS zPSIaaDtR9n(1WtF3ia2(F(kd9mG6e){^`bwq2ox&f$^eqeP#x%j@>j15G1mEQjO#8 z$AT_2c9#`Jg}d&df>0RHQ*)AJ`?jH~5*=A*k^%<*kjId0Epoesj=UQ573k!~kfLmInVYNxL@y%_V7#H<6Y4{QYY{5yi=5JlqUAJ@w4_i^#ma?zU(AhB^ z=&5tsi>D&liR-$$HKqLohCvvrdyrKz+psc|&fLfTS!tv-Tqq7_wc2zvb?lyC^dFmNu4i2?f&>gtDg=p8KXtQsaJoW0|I=_0s3P=& z)bFr5mYYpImVswfCcX|@PL#;xf>Dw8;|na@7eu3`m$1N9x#^V2TSI{qCJ-elX88 z)iu?-cCEeE-c|PR2vOllhN>g$$2Gl13z;g4rs6FHnr}0Dp%ab55+dRZYu9IzWyUU= z$85evXQ?I!Vf~2Cmn#k4NMM&UJon%Sxc#3Wi&XqSh@-lmZke4jVj%Ge@jd4`r#ZbS z2=Sm1ag@YhC3YE+z!-Q9ESA-jL}SFOCkVXw6e^qv)1C$h7Z7C(LnszGFQUTqVSjX& zKJWPSS2L*y(2l%%*qmgeZswzdJBv&_WH9g{Ri-(Z>A}skM`uj25PE**U}b2_{Jy!* z0*vGCBv@W2hx894{|J~NxWeJO^?TVJS3p2X;W)T!UFLEie(5;Ma=5{NJgrE@s-C9Q zs7S9q{O8U&_UKIO0l@}#%J{ncB*2wFsWd}#?1RotC6_nK^M;Uq!!+9RlJvMi1doc; zjh;0HkUs%Urluy6h1O~d-~Pg~i)k`2W>DVj^;=mDDXO>ryyX0#Idhc%LbAtYGYyzZ zjLFxSS;D zZ12>YfNakacNqC=tu=$VwSB5wDWbnT=TSsOi>XcOW1AFUhyp(i(|h?WU$-?Hd#WJR zVBBakm$DinUsP(G8?6k0miA}0mjBbB_0VidZ9)dF`3*9y)HDhM` zm;lxGddh1i+W3yYzQdhK=}||-6>m1(M33Lw)nyO*Nk0pf0|!pJmVWvJ5sE}+B!KdB zR+xjiJO{sSw7Xg{XSX=w5=D(5zAiG_5C|~mVTYk1)P5^Zpj8+6&07G*$4bnDI9-z* zefG!pc(XE=92iB!ZsTZTi>I}CRMlqlzcJ19&J+@HbXab5x^tF!PJ<>&JN&6XHhAqN zcy?^&=8j}f!jE%0*X6&o)=2?XFDe@6NzpRXYj&nnaYG^Ey1r{-R=cy*&XyzORlfVf zQ)%BouUN3*cvt_l4SS|YMdN$~hR9^gVVJak8~Kf>*mariW{od`8HwXKEtRI7?etpL@o48)ovjcOB;%49T7q=tXsY^RXc*mFOoJ9oxrZqO# z$`l>++26zoEEx1m>SlIT%Jb8QYMU9Zih2S%Q;cPEps+>xhQ2)CDijsBNag$BtFTJY zDN@9_8y@fwuox#;jbb+TRM35u0|t#^_rV=(-&@M$LW0S zUJ24+)9Vl_$TC!xzTbYnKffr}$jLyxbaDFx+nY(x+~~?vm%#8E>DS=67J)KUpG@s7 zYF%CbX|QSkQjLe}c4T7c&27EAn{n+nkH3D|hSZnep<01(GeI3J_%;lcu%aWM|BnrE z21ZqC4;3j^Mb|TqRO;r)XFwt-6JPUvayma!bH@s0`GjgBi}Y3{OnYqR0Filsjh2BB zZ&syrrN5eS3tM$%M4iamGTA5(+{z_q!jS#_k1)He#+gpt0UWv1$|#%FJPQ8yG=%~@ zw1ZKWl2S;W=OONERLS@6-wSx(z=E-;Fo4hL!*9nrfr1KB+pr>zO}c1_ed~@uK$cZ< zmR5)n6gMnb+jic_kCv!&i_}Z|MQ;4hvh45vqv;AizxI4J#Kar%IoM(CI`c@C?TNga z`fYwFmQ<=&*?zNr(OqX1TpgztGWzc==5g#0`po7hahU!}0#ioL_N!OlcVZ4oVo)|N zxP>t~Dabi8js`XnCl7aO^z%9Ez&xeu`+*NNy8R{?+~ zGeRyWIeB1hRfEg<_L&rrX{ES7SFBxMh-r<2iSFQVS#dzJ;X_Msw}l>}X&gg1T>3E0g;wh4esbVqrqI2(^94%ZB!tDQ zeGEjrg(DFUe%lXZb{+Y~q?(mt{CX3Ve5Gg&_h(hl(5?IJ$p~Y zysikiQ#kY3W`)yL?t^o!n4A@vlnrY?;x~AYY^+oL5GR1D-?Mb|{M$IV!E*a``?DSriXwQu0 z{G5$#3ZBC>)fX;@72D4u|7#ROlq5Gd*K~EvJNog4D%8+`xU&WiCI%`9C@#$`&R#Nv ztqUwGG?IVRN>S=Wiz0kF@xEMMUMAB_HPebiM4=g1%>45U!d@>BKKW@d|41Kj&!35bjes5Ykg<@rJ`arPQ4Wo+1t>mN%!!U z>hFVnl~oWTU+{2PQBq|g{{q>KZxQdo76OPu zw6x>x?d|=2I@}29@3u9-Yik>9@uF$#GIj6s4RHelgC^aTiP!%dy1jM&PtYZejg8gS z&Sz`Cj$fmS+C{&SM)=PQ9X+PDmOa5tPfw4J_wE0FjKG&r@9g5*+S=;sYK;p0V{BB> zWBPaRDzme*OG`_0U_}1cF)@#?u()!Q^YimR+e8)W^xM45%*|O*CMN>_r-K&Okx~Y# zDJrgQZ%_MWkzaXuczWVsV}JUD{kIhi>#|TO!|9os@DJHH$Y7eI*c(jnliUCQ3M+QP zun&L!(}q-@a=gs+gcA7_ClWF;A1~~`eYbW;kL}EU8~$eCKMj3;BONy|^`FbIk;ElK z1OD|Z$I-L((|W@O8F~ETY_9WA32nErkhw>L5Ga6Vd>cZs60yh$d|5z}H~%{(dx#Ug zh^48?$e2ZJLo2t)t3HG87>7Wy3v!7`G>nO|&{oNLRx_r?WwW_+LJm{iV5Ye1(VwD2zg*UexRTdxzfmbCp|@TR`6X__ zo>eEmHT_#wz2jrUmTku63bXj-ChZWFdyQS)zVP{$K-WW~Kw5>BoPvUatnA_5+kc0? zkovd|x+L)PR8`5bM~D$YNLfc9N0MevY~Fr?|78S)O<#TYU_kNDzqtT?5bs&D?m?5j zXp+Smt9tj7dbQ9+Dg^RNWaRHKz>nUiR<5!U_3cIvDP?0Kv&tXmPQlK2PC9BYx!P66 zJ?*{^ga-2s4y)lf3=752^(n(6@u?9IYPI>tJBI=8j1OqU2lGdF0~h_vwmNqFipCmb z==)SZRvVo**SkWA40wF*&U&MW$lf#lYY6uimYV{VA8FS92xHvp`B+RzUb>TRpQ-Ms zgg{-Q`-PZmmkyhBV|zZuxZEB(cETJ++nbH~U#tK&hz2IuM*)p8CFUf`-Jlj`$$^pwz4u#@o_esn1~-WJKPq${UPxU zPF}CoBe$UmuZnrIzmz*zIcNPrPOp`FaPlH8Q+~PQVDZUHrd`tT(~r9k+GqKBuXDF! zoA54M1%`6ol%;X|s|X8s1|#DEh8J4{K&8WKJGip^`i#&Zy=}*vly!BLd@E0zSPZ%N zwjY6etzrwI-|<58;XnRLZt1c5+r+i+X~$vT-EYSV60tEQi&Pil{3@f)QeB83kGsuC z?xS-N;T`J>VuWDIT8`k$cIt9@hJD61I^g)^6ZGan)`dQQ{pTQ0o0;s#plqZ!&#@Kl zTwH%}l4mC$WyHD@1gjNk-;^e~$v;yjc}gqgO?>lrDRO5!~x2wE7j&2LBIQN5&cb`m!!rXo^ds! zE!34ec&aCf(9A!jv^>wr-ugT=6`|8xR4GJZDk~O4oBr1#2sq- zhLe?plNAy`0^ZM=vCTfhCN6BkI!Yo(sk1<*H1a#_Sb8j{!{SQpq5f_AZB6{(Qj>87 zjn{JeTdn=`ZP^*J8-2WEg7>G_cb+NelD;I#7spQLhliA=7&ckG-D!(Ilv4aiXc1hC zM5$_K9IXb!xbsfA9fs_n0%(}T+w7Ok9lj4P)jL9Vx=$0~ckOlBHP|)}1ztPT-;NjO z4c&T!S!On6JX&+Do>p`zW(Ky=H?Wz?TwDx9$B3D&t(W6pJr8eS2j2|Nm4uq8Oc}k# z3|ML$Qa$4fyrsR~5~F-fm|e~K8lxdOd^yUmpV9fB*Y~(r>kD<4<1LD9vtcjJ{}}~=@)m|HGks} z>r?Oaofa^~f3fD4=dNfoGK&UUlYkg)%nw1#wm8N2`mNHzJN zVjyVgZ6gj;pRwqj^5vI1ywFQL9VE6;=@q5I*t|TcMUM5J^T-`$VJ1&6VK`sLFi!If zU4`%l|K(sagxk7xh%U_E!3_F_8T<;m<@)l=J;pET*Sm4`1I z@Ms>aNk>zgwA^L1KVkOVlUbzK?}g+_J$f;P0CoP=sa=c?34f}GZ)3(FZi zQd0C*UCEOhT%5#_pVahMpHIhRNW50LELDFejx;A3HBH=Gd-F*R#^~w9eK$fu+JM2< z8)J3#ZemkNF^djomVUXUIp%s>8^N@9&4}2!S3G3H&8ne86b;FX#}#;ayyQ(|=HQt2 zNls2C1T&}}FUK0}>;GrTx;m$UaHRv?*6fi3uz=&k$?;4295gV7&|z3u_OLFkMDsb2 zHEBnjk3{%k~5~%SmE1_-#^%?f&Ogs4rakx>-plY$D+&Z z(%gKhCU93iAAY4Q_^>6F6~-=Qw~bAu9Em7GAs3hZ{=A4BQ}qRCaskThR%w9zsaIlP za-)WvO)!|4l|xh>TB@~A9D!le*oACLS3uSb3J@mdyuLPU`tnAE6n8s~AR{}Z`B6<( zadM$MOUW;(Dls0&{2KMSkb*Qy@P`KiWIzh|!!nmxp=5m!(#1eI7y}FZnVd~A&@Qg^ zu`o9dHn zrcLY-l1Ht^v~tAbScr}Y~vHa8i>-+rf+DaDQP}5LkE=J7WUu6nWMs(Wu>SSwP}<+bOw&js#He;iXH+(TeBq- z9re!Qh_f|1csitj!rLtu0Hsb_{Y~{crZ9$_`qi?-QlnE~VBllLTOqDnZCk&;+gNjR_+J3I11FqSe*n?7_n zB<4p^sM}Et%M=ihuS#`e^On;7J~AVik1k5tg@r%qql73j)g<>$UmgYBxU<`uQQf|2 zJJ(cM|Iu=b7G8(DhL#kEw_pt&m3Hiij|BqJ1#l-wX z>Z&}AysGhqS)Kw+Q%grwm72RxP_rufLY0SxL87R@0cKQp!9X85a?I7LtDp$Mz6;$i ze8G-}?Pv|UlHy|Jj#Rf%pNUuH&#o^3RO$6=(77#LuLvZ2-*R*J_ve3}X-%0Sv{Pkj zyh3+-KB!rzdBm}sI)p?AN%)$5qzQRnNKX(bOpcA@*_!a&040I}D^kh0 zSRV&5syY(u_yPP2&==HFXe%{Cj1K%SV2~MQRaqXa&haYByx!ZYin|8KwP6iav&+@k z+T6C?5OcB6yY1HxP3&Z(=W5bi;-B~I%%<44r+RxhB;VGwC-L3vz;pU-ftinG6H|7M z%*ccd?<`YUUHUGz4^7%SNUbOqDam-K?#}Mpio|x{v}_`j6X*~9q!@6_azE^l#|``l zzMg%XCrTG5tvyTpEn$dLtA-LW0%^gMpIST?+6eiHQ5DA2QFX2U$;pY7uDZ~Yhy6|E zR9K2e>No%M3|BwQfFEjDx7zf!Px8u)SgVKSy@gqs!4B(HY^UBhn3&MOjs@b#gONb< z&ClU7Q8hKS>9O+fn97WCfbc2erMJS*^s2)l7OwQ0XX{U5G~#JK`^^@2x>|82$~$X{ zYT5>41qsDD&jfv8_#)bu3(5 z=%~IRVIyCg?kyo{@}XEQd=Id1RdVZ+hSFBM)cUU(=v=mIfD;xl^h+3TB~wq)sk2tw zRJo)q1%rb+Nu7Vg$tjwJyVs(Z**GCana`3!a+6xree~A!K*C;r?9>{$6teA}n{0I~3V}J?03vyS* ze{&M0rUoM)(*zJ9&J#7UbF7N68)4OHsZd#e7T3K~K?ZbZpa!fi-LAcx5blJ$S) zoS>$xJ!ktN5fy^j(*FkdL3!g*T&J(oHp#*$r8leRx|6lJ6I;v68Z!+7WTJFpTaN@V zxc_XY?D_C<2Ocx5@xU#*dHy}{b|aeREtGK5nQSVnWv0-NEz_U0-z~VB0X{hZ%IoAx4LIWuWs7IvHxqDEEJO2=RU!&Po`Catp;` zZzFs!mqe1$JmTT zJ6v@1)2_tsmdYXsNzh@d-M=0-`711DzLR+j^>2KuE2<|{>AU$pnDEZ0>E%mPmmbtq z9>FY|u4G9Zi`gA|szG8$vpb6LeaHQN$s(7}&4C+-ln>?VooOq&;w%eCQ2`|WJ~=Qu z*%ub~pjHZ3(CQHqbxBL|t-l%7->mV)3Ry4u$5r%E0gspOU3Q2}vTJL34^zg-g0gt5 z@z@!cVA3gl)&V8#ta`WDO-jpKljB~VILnhf1V%=Yt6Sv6Dz~5&hJBMzwvs)U444JFvqu8!ogvUWo2U0wW;-7k`f<3?Xk*m zDWa~9d$Jk*FY^F6I;6z^{P{Coz#AG!w1F_4IypJLe*KzBqk+?nT^t!dB#HPYW5OsW z`VH|S&PzFjrxa&W{`zPG0U)#YytO`IjvMIS#8_>QopST_w8hkr<0rm*nx!gh>#1y0 zq&r+$ID`a4T>VKt5VUNSbys;0SG@P}68IIzV>FVR^Q_ZB_|#}IyD3R+_mgPzUhByl zj!djCzCb7`j@|3FU|r$%$c_@!9vB&ql&m}dN8Bn9Cg($Sw)TW;=~Q%PAL5n~D?D#I z??7bKUaOp5E<3oo9dy0>sBT0luEZ|K2HRn$^J0;M-FI8y5qT7#S1=Vm%o^6v5D5#q z46&vasyLC zW0k*NXU{nFoqxUXTN0M&Zk>?@hBbBS@E+Ob1R2G@l4KUGT#Jw>{yrDm9|!ZrnJ~rB z&Vk6kGJ^mGcUkM0;k$2dPDnmKQ#9%_i!1%VEE~la?Yuff#yV}LSWS<3eo+OgBGqE` z5%f>FF!~ zhhE+LMVi_vBP;6y8ncs=C&~o*)m;C!Gc7Icu+d*CQdB`Oy{82Jvq+h`P2IxMk}lZh zuzr^R8x0K&v`{QEveWHtYRs@)Wx952tiS){1+67z*wfP!`-U!7Dpz^`8->`}fXEg4 z&c?>Z)|RTGqT}s}UY)j_61Kd@UzW7c1eK7zZ~zAxvGnWLuYH&(q6)BRp_AZdf6>v< ze;XEcwM@2m&Z~3y`)L08qEO5b8u@=$yb_pr7a08a=}>%0w@g?S{hF>19Vl!`wGrpT zoY=SR>?G^__r3}o31(Kw`BqqFnJ8XqzYO>GzA7Sg!p;bVYEs58EI2nUMWey~ZQYs; zR^-fspEQOR<5gaLJ-b|qwRJ6_t7Km=DZ@v@C#<=zAL{Gf_ys#gtWC8#^Zw$BtFfC; z&sUqyO{@qAMXK5=DrK|#yu8F$Ki5b#6$FT4rFPcl^5+>m2w%SZy^|L=ER?Ne->F&tLK<8ITO)t^=QQ4^SHnrsHUWJz8*@3 z6#TnPr=i|%1a&k+NI_{TQ{?jpyJnfn`udyBK$r&m<-wsLwxo~C&8~+lZ4YIR~(PXkzAOorH?{YbTc1mo_fyk!I)U6$M8y~V>y~Pn6p{f z5{!8c1^e-DXd9MUT(Gp!u54W`7 z8v(6UzwsL#Gdv(~UA)(JTT8@Qi8;`vzT#D&kg-x}B-T}wmbPqD(S}P%nEdnS-RjG; zM#U@}%4HY2&^wgNKe8-!(h=augolMCaXW2*7tYY&B-hQ;oS@D#SXk_G*v!jo71` zMl<1`VFyQd%Neo&x~WpF&4UB4t^U|3A}&sSu{keMA|kZV#laMRA?MQO{QG53m(?_` z2Jo-5SQG!8o}F3^_flTx*@gX_NsEj-dkfvF_{oCcgYF=!Ra1excG|9|&$i9J&b!a- zFu+#EyeE0rxbrW8bDWs*YS#VPw1*(3kl&+k8+j0W zJ*&RNu3<2CRT9UY7`NY3@IigBE4ypEZM>dmSaVDrO|2r}ir(DXN=}zYBeiWGmQ%p% zxFuD3_-50m%_Jn`KbbFwS^ynr*cE~b+H))TfRI^Q-`OqHhQzoX%{e{X9CKP-?M>tW zjz^k{Z^#I1<)BMIVUWVR&h1#6mbQSiXofOYirsQNvwqMBBx$LsGv#`RCntUfQ$?( z3Bu!p)T#jB|Kxg{hq$S??X1!CxQ)130e&`0sp3VP}w&kz8cSj)(A?ZuWLI!z1GV>s!44TT8t*v@b9Va|yPcJVIFE21M+|NS#zSU({(?t=$@%zCy ztAgz({#**G&2tbd$=n%v8ImR%=tYSQwbm-ppCTk{Z3%XJQ7_+DXs`?{I$*&<@sonY zM1}%8QoPG%h~<2BXjw-*=m4fY)U$EBgxdV38@0!Gp4M*ck7|L_BI--U}pO*2A7R1 zuOvbeWbT(p{`9`K0WNS?T)Ntj-u9_wsXx*6_~lRJxnF&z%#+pAXot02<#QYN8bt1= zxr_N&+hkG6PGg2a&9?m={=Pw9#Y)-qxo%Llelq@qI4qDk6!XAD!`W7gd#9ZAz-D3* zd0P|w2iZH?yrtu8LGiYkps30BvYg$}@k!U($GV>nGLp>HLAy%?Yu_$r!K{aUObb^y z^61(VPKH$-jsh*&&{-9c&>=KFaM9CSx@6cC)=HMM+8?3+Mxf!^)LMzDG5Eu3iDG=S z{|J|NTupd5N>KTe{>yA}(vz*tm>;|S`G5=(&Gqx^{w(BG#swc+!{PPYwqd&xc}D!E zpvR4))JH1^QwbJl-ll6)_1EHF!DCMw23Wp_G~pDdTL&y$sUMkGrF;?O^=dzvckH~h zmP$N~E<;vt_?m352N(;iTKs)i@l=bv)qNnF^QGEWvw@Sis3Y1p-crLiVF~aAz(<@O z`l1SyPE{>ko7FOVc!KbXo|o9~8s%_-QU^=EYyA}R!L$?RP?6HHLKXIEqKGCUoi>99SC|llf4e$6nZkJ;J=Jy zKo|BjS~XIu2n^C0;MuN&$8fzMV|A9=Z4UiiHZ$J6Ja^ouy3R&>mqPpF;Gm*o=_)!M zm(oV{C4*;M8)mHC#5M|D30||)W_JZ=sR?5oJL2MSh^Dq5U^r>dP39FOS@de z>2xdr{h63_WA*KSotr}K<0GG^CuC>y4mr`I)jR16YD#;}>oDA5|2{S57w`BQMx7G% z64knc1?~9adKoR(&EAoLijSt6JF^D!Oxwr)NS|il#vNfOq>P##?2>-m`usB%+q#RAb{}rEYP*exmOoMGwtWs`?TyI z<&FRZwD&&94*}K{=^xYIS*$0Xa279yHrOshO1dQ3pzYR9<^?2+gLZtb%J~#Dv>kPJ zjgf)zs|LT3vSDM|j&-yV1sfJ7e+w(?{*HQL!aei;h0E;?t%w1G4kDe0%?(s>v5qjv zXkzJzf~<-s50r3zUPP2u`!5-+6F?F25b~1JVRk!DUPR$?x>%E7_ll?8=Nq_J0m-sx zzVCas{-KF)&DT$GL-k)$HHq16ue1E81s`%wPr6PYpgWfZES?_@Pm@l^Zef5zIqoDB zm=4mn0X~KNb=$Z8cJsb73fi%?>rYJ%6;6D$KHsoAzt!HS;UtY!+`IZ_lAlgpz#$8M z?|sBS_EXlR)c%_bU_hq*wVu-G`g^ILipKG|tMdVpqPJU55UQtG;;9xHWm#kG_7H~! zgRdB4R1>}HYPu{nJ0Lt-&(sG6kb4g$sHEkrps@(VXI>Fp4i1g-f3B__H)Mg2Jw=W~ zUC1csmHYWV*FL~AcdBOW+dFsK*Lq&(w{Znnt>&olF!<5E?jlWgf|z zeq27K6^R{IW7(q-@~tyz1QtCWJU>~Cwsdd~hTdXu)JD#j84QHuW07n#j$`Zd-#vs0 z<;F=u*tLw!`-R-^RIPR``p}N)v%@Dh<;esGQusOhala&Z7mS1pBo$tH33`==W^xLx zACckI)@<+mH^}#uooIl-R?LuxYwvdoP((uZ_#JLeeMS1!B z)$&aE1rk6mtxO+(cjpoMg}-b3DxP5gG0cBvs_-KX7QoHJ*YjYAZ(F9%4 zXNFy__$P?=#vTI*`WJ!LFt&1`Hrs}#fz$zT36`~O5393`r!>TDAs9%m;Ih5vs^ju> ze@&ATgoA_gbhjBZS)j0c?hzau%;$Dwsr-h6gM*Q=^hwkUJ0`i4)uI9{18Z#!UT)$S z5O`VfMjXo!TJ>;njR0q*vGGRDnBW7~0SgloT!8&@Ge?u4%}lAJk1RP*s@ud$O$`TJ zTwJ_Ks5gz1$@BE|1ipa*hV%9y7(v%p@652w(lgW9ZHwF3LQBef^roQ$fn5mAv>6>` zurKO%e@-#D`Ts1#1GX(uKJVY{hcYDfcDDQE48R(iP~pslSGsOj5`6LUiYq*QcCFbwhY1B$oe2DZl4D4^d$lP`yigx$nJ2dH%LxP(5fZafWyh={=zyLc=7{>gNS z09H3BUM;=%&QFsm{2Fba8frtQUHseX;3UuFz+NM5!5>%u&U^yd4;pBwlHJwiZOBxr zZkr%qgqQQZ#!ncv?sSRn8CW;R=5Aw0N1m3eR+;9cr80%3E2V z>7zmViZn3rlv4HI(CsR>sJz_m*70(0;my-sa1{~ZK683ktOLOo z8gJPH&DNOm^_qoFtVGcSj}}~zQ1ZcGb9$PCi>pCp5)3-QDaT^G_g*z3HsLtuomH&} zFmf=qJasLB6)pW>Luio;w-rtX<#^PJ*YV>@Y&c)2w{heu>AhKGZ~{d-diipBjWZFK z=8&=3Gf8Dej~ZG+mGdX|B1WcoUW9VwS2{Jrv~~+YWTcfAitmy0z8C>8rSZ9@o`&F{t@Bt2u^?QuDf-V)0rO;mcvIap zYqwI;592wWMd?WyhF^>AtR1!OVEIp^iCs$WHET(@H82gEuwqGKA?LTDj_dl4z)hFo zK8|Q`dVzS&&$F(lKT9>>;sy^qS9BTNcjbj?JJg1%ueIML&Css+8ikT>jF06lBi;~@ zsinQjS_%gU>*^uUL?2T_x$eDE36Ca#rpxd8krpbN1+cp-iyp^k_;YJaLjzaupBZ-N ztXS0ZeCn%xihC5X-uh@Fm@uj0IMv~Q?0tI(eN&nBqZwht9G`x=+5LJWx~ghEAN!*r z#j7B4@I8hg0juQJVCy|xJ{T?otT{3^=}Ho%#4-eV(F*)BeJ}Lv==%7AIcjRkDfLEn z^^w-IXywlzueG%-F#h6q8{S*H4Z=qeV7qi9fsO>QM=1+iNE$BAqFNS^w!qu)9dpo7bS!`fq9y-RJOw-BR1C7Q}y6} zir20B+iy-_7y_@i6Afsace+v0p;LMY2l@$w@bW8>wVSMo(PbDcN|N?QF+-{cT;#zNw^%0Y(=hvAsllOO9U zj;{3X4J}9J)Zc`{Q=>f1`ui^(TOujqAKoV1AfTgN>%W5twZ!0r_^EmxK>KPBn{LYw zq)V~x$H5USTTS1-M^+V7qtSzWas8^E?~o%y#kIR1QN%}>8U%P^h zNXR0|4jBOAv#wv&*M8DHx=FnZIU(us2%tHB4f~JejFCG8X6r%=&o8q&SKaND?ahIw zC<*ys-{@bap~W?-982na=yT6Og8y9QrRMnPj_CjF=^5Lxnkmu?fPyeM-7oIWqko;k z6RU z%bRtx`g|T|YXK?U%YXhjgHiq#)hnT=tH}aR)CB9BV%oSyg(TLz+S-3Y|7J|hhuhP! zkr5JBQzvG@RKSo&a{qP4J{r%741WL(&IrX zlV8bqHo^2An_`zd`h%bq`dUh+X5H?e^~H8OuKQhsp(I>Fo>shezqXiG(+5-T&oGz0 zQ&hZ@s`-NRl*tjkPUCH>vGxb*tINANmR5d$-OT}|hfi7-pF@&L&lnF4;TN$oQ!;zV zM~nJM!saio>9Su%R?gFYnM}3Be-E7K@nGpF6?byZHD6ydt2T zd0dhW@tcPk?1!_P%;uV%t`nl2`eKLFa1xf1mZS;Qxk0+~nXEdz^5H_!PYrex&$Fb#~BJ1GZf{PT)+HKqq5^D&ktTb_E z^*OeoC(?f5$im0dQ{#x4vi;Q+`F(r}`BQ_a_vmXSRF?B=t2k4m~T1 zii*Ix5+Cg2`2n&8S2JKS)lDAf=0DvBN&6FMC*v(pUt!i>6oz3Wba2J-SQ9xXwHS z@KeZNFiK)*V1UQ%=;zK%P*4zvA1C*(R!DAUT(+FA*Vz?Fii3MHn{_noXElp%V;TN*#8(@t%&5QzxZRg#red2PTr+A*p5xs8-9bpe^e&pui?4(@ zoABjw_9C|T?oGk#Vy2*?+dmov1@*p8#g)zF&Fp+`PYv;-L(s%o>{Q#P`c|%OwdKuk zPvzUf;&EVVE52lZ2_R@!O3cd?8&?aq#}_LLk+~dAttc%iVdjVQoYoOaR?^eb{+X8G)5o7JYuMOe;Cv)x{^f6mAM zBPJ~!mY28vOH9ZiX139$okdyJ@(SzyUlL9QsudK=rnSiqo`fSQWp8 zOgouyG+kiPN;{{#oSBwZvV%&ea&6#H=tXsTYJB`#Y-|>Tx0xX%_xF3(PrEf7GLwy@w|AlSkgy|F$_G7RZ~PN7QkX z;4e*_2<7(b=(sfDf(I=15M6=lo1&6hMBd`@&zBFeL*X+ON$#4w-A*$Phn!-TGK;tk zY8h>W_dN$%12h#9`S#w^*!YvUpkdzQQyEq)#@$ZRBEY>0;~`6eYl~U-II234O1&o_r2dQ0I>j{ z`-y4Iyu7@;_3z~tcXyDe-yJ0hB3G}_UuKBEnRs)ekiH2n@4pbSXoS&ebSsLBDR;XF z-y3(kulmmNL0O-HCeFplc_VWpBP}mK3JOx}WbXZ@tYV^~V7%_F%y*7Vc8ax(d8qwW7C*ws)yx*K{{#0|NH9ygv=p$~pJb>qhFnLerxsQ_m*IT^0xLvcU+ zjL}$rI@EjdzFX!JTY1WS%(=yOPTHQBTxNJ&xF+)6=pbYBx=MNR@%_2|4!{U2Rz@SE zYxlIdemHe%y0?#j0-AL7U0>4Ir^`MzrRhD67pfI228d>nzY@(-#I`*HeF7Z7##RHl z+)YVN_Io&P06NWU5*f8F{*j`xJXlM-EepmlRNumjOZ@pW2?az(!o$IiFE44+)6=hd zQ1S9=TPYPyfn(T^l_kOv2nVdMuWQSKe&*`pg4IXC|LJ-b)R^l$11%Mvm8ctA8YgOIB8v4to$3yFB<;hLhN=*qIat4lyMpBtWz9 zGG}39Js6EjMRj$>RWNI=vZp$|;h!Jlm?kI)FKrc?y#M*TAnZ!sh}xuI@pJF_HR1@z z?Lh!fBZBl?dSbx8UR+A!r-03lD*EkWgDhAkn53KYZHWH)+Q`ONPsPayUD7bDXQOl5 zp{cnwQ@3kfGsNf2?iD{@POtdH#vnxj%iauoiOV{k4m%Tog964m{XhZU z?dAEYM7@j>FD)&NMYl0CxsCNzkfm-%dZuDw$0KxESrh^~Cn^6c;H;OZI01wvWd4s? zHpVCEmxBxyeC~tW7edb<{Ur3Ph!q(b2@L~-$E-W2!+|y~y)<1}Tf5Za?r@q= z6q3G+c>{}u6(^XyH#j}5Mvk$E*Q{h~vT2g^kuw0nh~hx%@ZgYvt^Rn_9YwqEI0m>t zdn&Np3o05_r%_C*va^8qwVv7%9OkfbJ{^R3)0iBGq@de_dIiM;+T3u>$pT&BF7>}7 z_UaE7JSuBCyVdVpX)GYXdUxEXAh)g^4cy2}`Ixv_@8ZfzrLJ5<&T3zjInjOpSp>+O z*yCGCsqq?{kKhuQC}Tnj7FF;YTv}TC{rmU)JY=QKyW{Cb2d|joYv0SQf8=W_%iS@t z2j9fv@%>LkL@6M)zt)3B23p(NXg~gR1ZO&!R3hN_n41L-3E0Yt--8{nifva(jk$dr3tIpSMzi(sUcNWG{eS07~Xouat&--Ysv^ZsjT0pB&C}6%Q&h`25=SXP`e#X4Oyn3iN$?A2@S~`Cpid0lUw{X#708N&x_G zv60TZn1$q`$pa+b->)8Mg$#6q4(jFpuwr{CQAbmgpVjcg&x4da26ZWo((&eh;uKKh z-WRn3^qTz@5ktEmdpR+n8`IO&)KpSZGB9wkvEc)2E3o38TUZJr451GRB$yM0yfg(& z_bQ*!K41;;WZ0!mu8N?%J4&Q}Y5KT$pZP=RbGkfiyV8SChKw9<<)`G9kG{Q~1$xkc zcW?C?DQ@7uPp!XG)q`AVa2h6071Uk5{@LkOZ)I)kbGH`QGoIDPgC?8B+a&ZK5cH38yLh}{Gyv$a z!zIy6QV5A2nzO-&(y-!nn3t&Ub+#rl8)D$v%eCqQ1LWuDBjYme9Ue{Bpn=IEo?u}^ zNy!J>2#P7vF|Jj3s7ys*CF$#5JG=0?WHh+v7}|~@RlnVzEBSwz`^&H@qqS`mo|JS- zw@Pmga?R2_=DrzFn*QzUFHp?vha0RwhqpV`*!9 ze8w*LuaFl+D8~NTiZm&ysd*FmcfffqF1}B|i3`0GEVcpxLpoKotgP(P^0Jq5M16gI zh9>xppP%1T`N;`!Mf9fSRf2S$vW1`Izc&M{+9+wRWHc!lk*YV#W;*nZ)u`vfg>jUC z8yfjv=J_AzjbIF*hyK6$2gQ3n{{XnRuF3`S2IfCiJ^{ff({AkCIsbe9C8wt$`mD~o z5zKA78yg$z>l3Tl0*m@xf(Q1xx?_}j7mJmGpN%0oivBqt|5U_Lad%mA2KHA|zT;+l z^Cpn|j*snMxrPH{Q&Ue5k4rb~_X>L!(1T3o1h?z*oo(`X@Z6!syik$ zs4f3!`o2>L8dtgTKOSmz5{_LdsQoy8I#&2W|KWf%Vrh~gi%z1ME6O8VO2q2%9n0=6 z?RgGgkR>HBb&n+GLStPDDlkHw%&%2K)5-o3FhdOjjL~0c%{$Or$aJA$Bhda~&j8WK zS$w7d&lBbVw=?cn!Wy1W+saG*$Wj6y^_}>gP1(-_8QB`ptZP}ZIarc8XXxT z{0-~Om(ESn5dmZ+&(@*av&-jS4jpMGv%L}$q~a<0E-g`C<9dM_e!4Ni=cgugzLm%r zGFz^Y#$f>%qX_vJW8kn&db?U5v+u+sqc%>=wGfAsX%z7@X8Q(yk0 zHmuX*j!>)`U5GZo)OPq%{Y<0+Pk`Ri$6J+0y{&=Y@w^4CRVOVerQ!K|Rz=H;Z`;K1 zx*hOqf?UFG-|&b#OydKqybx4GXCeY$zDrwNp&yJ?AG%(cbv?ZQtZO;<1dVR4r-)C6 z|1M(gSMim zBem>+-9%wNQmWJhq6(2s&j2LrM`WUxjbh_B--p2Ps3`9gm7pVx#^Za$`^~iZ>hc$I zXX}YQPIW!Bsnr2wl?=T*iot5D87m)o|EW{^`~ydK2DcoiIeB<^AS{3ZlPU2#`>V0V z_T4Af3yuKF+pXC;HFQ$vra8orhX@WU!e?VC;fi+P=myE_xS4m zEG~}=99MA_yDr*XpJb}y3}?!cjy_#gLnXhbsJpVyf)NgzE&jxIO+6^I>zZlGcgFA% zPMnR5lth>U<5O}>rV6w+JOl$5XMh-{d8w|m=GzTcfs3A`W3r=?RCqA^X|)<092~G} zQ2t7;t^nj!Q(dhE`jY|T4I=zH7KN1m8~G>jpIIcNku;u-cJuM?Ql6nx$)k^StYVzQ zg#{ut*9L=)dX%Bu?}AvUX@T!K{EA6`n9!i#RO*{&)f;L$eu-V|2ayY2CL)TQvral* zcnD-p8~rS>JZw3);KH)&3eR1bPdJ&|_cZi5*F$!^>EqSGN%ULaaZOzVdk>qaH&z(i z=ke<^WjMA`Xov0@Rv5oZ6-*hU(NvFX<%E|I^^zmCG0tbhklnu!!d)M#vaY5I75aKBuWR5d841&{!k^*4(<+pELEb zDHGaBZ{_N>1rMZys8Zh=&B!#c7Wu0ltG*B{Ev?}-r`BU1y)l}buDbedSnBnT`^{!A z1>)xVLqY&8b6cm^snw!Ybd12Oqj#vkcpShJk~$aiikx+CNYNoQJCY&ak&7ufWORS0 za0+rkfJigZ{V{Az+x|^TjJs|!xwhWd)1N$o_Z1S2jCu>5eXbpi6(Eeis}F-J?(q># z@QX9#mC#)(c7P%SeBI;(r`5Ja{`d#X2r=sd3^O!jh;>>*?(3S+SM+PW-IRI^m@x6p z#^4U1$)J+xj7S+Nj4|F ztmPiI7$tPAvb;tZBXA6wF@=ZFtl6PSjh4r;ka&yEz@9gGlFXN&F5+70J}lsmFU?X2 zX=gSwcSfL9r4^cJiOFE)JaR)q90DrQhscy3YivIoN%Y)eyX3LM@W;c~jAqFa+j*Lk z8CWi8>W;r=P+OEv=gxQ7g?Fy(AlCv>>$s-u>gm; zhZbxGcZm&HnV(8^Ez_grsiQx$(Cchs9%lP0@2*v{pg^ZUMKWR^`=WM94o>%|p|Ytq z$XODz=G<0ve2{?bCL_qq^2GBCBt``EUuu)$u6INAg=-GfU*zJMAa!FQMm2xs@3%_J zp`_D8)kZ%T?OzwL>f@r5(k6I?CV%l8-qKgi^~@xjsC2bOVXW@WLc1#AnR1Sy@@H@! zsD;dUT1!v6E#$rGM!Fbq|5*egcWmURrxyX>XmEcfQMYFYk{ zlRo3{yy#PGgpKYbZIJS**QU9Mm=@cbZ~ zt@bPBmiZ5Z%$W>CI5AVDuPzbcj0jkGJ%o5u+3DKw!RkFjvzdWvFJZ5t->A=$-%_Mi zzD&6f{m^TXJu2_%(gG?sxM=Tp)bniV`W~7tzp0Y0)P0p~+RJ*%VK$1_!fF{xRPUw2 zJlgi(s$O5HLa9IIAY(U zP(BbROZuaSp*a(gk=Ztq>)9-DHN70o)>fP&9~`BvT;r*3z z5z}ihe!rdbXyC#LQAZ&#G%?8=BoOp&=#F_Mt?~9F^|L^GRIN8FP__a-_{H*1UTvAH z>j_%&olAY3g1l^msBtf-noGxj?P1E`F#}>32lvk_RwHyziQUqPKJn#lfYSF$ux*3^ zqR(@GS8>ja>#}Y&R)Q)6?6QrNQT#j#5j7L`HZqj7~A6yRXy^`9s`vx9F*sptxsBqdqv&tmy#nxLb?}s zyohRLxEWtT!k|LnwV_Cegs9TdTtt?=&PrI&fR?d}rW?W<6G-xO*o82_w?i_xKIgCCMCDjE=ZHH@shZ1DffBX!jM%XjxPL1myWbC+05k^5DAcx zGQs=fb)ng+w#$206>M^5$}-?aoWErrrcfIQ*qDM)78O%gq`A+82j+;XVy5DCyl9H& zd&@*${yn?mr$Z4Zgs0qDTD1`tLh%}L(w}yDe#Cce+!vMd!7gXUE}*=A#K;Qy9TjFo za_Dk`#E!ZXZ+#OgPh*wXYYQ72PPU1RJR5Koma7dkS)>{lz!0K#j?aeGCS-+MjvCzm z2tSX(rV6Fz?7M93U3$YH&qLkLK<#PrMNq4juX5g6<6RP&#&{|!J^cb1ddHbX^W|7T zFHQkoxJRcq6>~>Ac`naZ%( zlaCs$n~e0Gx!Gwcn5g23vVDS25F80&uZ2(CS9yPI8qV`FA9<`$@W zl&ab80R=H$a1)aOjcV$hs=jY3T>Ri)|3m%oOFF-Lr9aj!SaGQ>SfaNoFma_K*L(6d zioOa|Edypq+0tj~n0bBki$e-jt;@G9JySaFY<|k0!SO6lZ*4^#MSzH_@yJPkFB3&3 z&86zshMreo|Gk89V1xaWcx4hg5yaRqw&Cb)y}G)%S)~T~WF1z=JoD1Lg7&ut4=0pW zgM${*ZAr8+Xrbf&90i0HQmZkd(sU9F2c8hNmH1SBB<5`xx5g1KEn?{FGz1O~Cf@kr zx(p%$yg7qN(OxWWZKjCPoyniHzh3-RPh3)XlOn$^oxj6yZ2q$0=?`DR95#VM$iby1 z(&Vfl_73#94G0vS=VvZ8_%tso)u_HhsT?GB_SN;93E3|6C1RnXp5 zP*jNt2}L8C8MgdD82n~@ISlpJ!h9_5@TlGIKk5)?+|&_6H(ueW5#y?a(epTqcEJxe zS1+XQaek;X^Tl#1p(B@DHM`Z;Ja<+r%COPr*vt1ilQr@2&Gox-j(ZZSeWS7bJlCu|iNtm1 z@M}C|61{)Y-%JsyBqk*?U4I^HNm3rg6SK?Ba*6AJhSn_lEH9x?t0Z7nHE&&cc0uzJ zx!alxS$Hr;1V(B~W!^Ab{l(Esqwo+^g}A7H zoE-l$8_Aj;S+lBSh0n3?FSc#}nTpDIc|i#)ebK5k+ubHORer)y^Y~%%?jBV*$5(vD z!sLZ17RP6kYu&Q^f_e-=^WGS7olk7Hq8ZWfVRD@g*A7f;JD>WZwls=(3?Udhhoe}0 z9=w)%v~;xCgAjk4;jJ+f0qJ-S&?(+RihMkn@en&t?-=j93tF08Rb@YTW)wmRQ!#By zcAq9g$?&5Lv9ry~Iv9i>_lGwKlAisb5KLyX!9Wz(`~aVkt8TA!IK}k}WwAe@su^&ykjoGKQDjxv&G+v68Ot9U~_`8 zOFV~X526H+zF=?P>=`5^mE4@$*ANVo9`QWv0SMt4R_^+um+)JAkF^lhko9c!mgDo$ z^D^CO?6dQ;r)nFb+Xavd_k)2rn^Tmc=G3DTy-2TjE{Y=el<_tVZzzT1eCeC+f{vv# z%tXIcfcOhSj{E&jxBRhATV8=0QDS%9rh5pi*3PeAJK$bO(&xo3yu;Z?U-%#WGTJWK z>T8ha$}&??<70>Xb^6f--%!Y{QbPInyEWGehQt7Of5z<@4zjoQMu$;@im6{pyPYSZ z)wofu7fOOb*Z!NN$LmHRx=&6Mj&n{voa~RS_6#Wb2}q^bIhFnIvu!>lh(bk`N-(A` zixxhn?fu?>;;|{{jk|b+u1iX~@|zwTFX3~>;k9Ebr{nzdy5XJ%dHhE6aC5Np=U{{n zE3K5UugX?l&p9QjpmutmfJbH`$v7$dY**tedP+$Y2x>^t{?4=D!Sp}9p~vvHU|~D$ z!;+l8ChBd$2w9G$nsK`RX}4OhA@Fi0&oR{p_7>v>W}rI0t=HWy*hMv$H5NuNw0*+c zy`cEnwSLRpOKFO@0|yyPcjtQel*hW|NHuFFx9F6_p*nD;yly0)DB~|tz4;>e=EC@9 zx|`w~0TM*Pv|V}G7{cWntF`y%&#E`R9urhP=j`%Zwuw{frbYJ`r#Gt%TEr)Rev&FM zF77RD9VGVpRSVL{Gvu}lK+18+efx(A_#Z_EJ&F}RUDe=!&c2*SIhkftX4ll@CYq_Tx3t*rIzpbYZ;3NsNHa*nk_%mH{Tc&VB$b9)_lD2gLr0hDbkdPa13}jn=AJ}KSR7?EYf8PNG=?L9{ zm7-pRKbhN|6Fadqd*M!nZcgSc@t)AAE#E^n8-%Hj=9x^ADUQKIPJ1FVf``dzU-u~> zQ5Ji@HwjvdijuY)!GaKPzii{7T9(?%)C+$2DrzhIZM#vc@B~>DMyIz^I5H9k^-^pG zZU$enX!52f0V|;t)4#-=fUH%&j-~`0}ymoSu0GnuABWTyGK-xJRm40Eo z7IpNyt>wrem_m-zo)yve?n7e^OHD*{*}Dv;#d=d9lIX&3rE=Y$!G_1w;jkwN4>yh#6$3;&Rmz??GqCwje<39bj} zi$&HWxNzkzp%!kC_7H3=I@wd!kRQA?I!(WKPC-h)7OMcjaq<>>lAs>Be7&lW z$~FA!SFkoHP7NBeZLxvAo^)QEcm^FfqO03?DUM>vdHXMX=~alm-z;TD828%K&!_PW zZ@st`u%N-#?mi%o{v<=l#xt+dtN>>*BH@E$Gnfk&u9^uOw{ z&OJ32lU3E#e8UEf4GsM1?ig~O9$42479^%NYFfm7*rB-2_`%c3O9em4TSb@Nu8K)Y zw%_&;$x^Yi(pt<_7(h;M{ur_KpKZrc{-jsCa*G_Ln5+0icaKI{eRot@-WnN;X<%85 z|F*&6*Xedkb5Bx85K=LIDT+Rds=KCuE5VHUhi#Za#25lECFeXMoa9n!$TC0f9{l?Q zqysb|x!3{}roQjrOE5%q?T#S4~MNN4D}xmA?*qJ ziqH|oc%;!`|LyhANIn!GPtFN(L;R0d{^Qq>9jP%{eT(E@>EsBd1WcgWwxxw%M@#8{ zea_!!XS=~+y?IPc0FV~IPMiH-@3(Uj^D90Fv=x|&36kNSaR&eUEB(FAv4}vr^yrS) z5%2!<3;px93f>fs_apoOP!!!}S2+bXl>c_~aM+$oV`8fa3{~9!Wo6*7U$kN1&N8?Q zMd!K@m2B0sS1niH3%!2_JDk8PsUJlq^mKQ5@c-`)QHWptiLk=+FPxym{H32Of^K)| z7ZZw*a!?!HK`7+=0D6a=6GHymii=*uRwFWcUB5p`x5mo+n(^4uos7288{Ph@sm`Xk z=E6Dt##zOp%6btS4l*~ApeJR8m1$==WG+aUg2yHSI^9Ya<5HPytyh{QH9ec8(q1oe zz7tXXqa4G3l&cz=h@sgoW1S)E^;N>QR%ywiICoheLD%|i-9t6NjiAMeRl zY>fwNIE@7%!K4{)JJyp-FzTEuTtOQBuC9$k>&eP7{sufr?6zvnXC+P(OC9EPEa$)M zC_j4VY2rJ+6dv!CLG6t-84C}iEg>IMzm1+tlgt5V;}5HiJzYKo&ku>Ob6_A-y1Cw0 zh#O=j+QZKxx2GwF=g+m=^?2--U)T2qQ|!neTb)iNg8y*DKH*cf5xpOd!&-!iny-#J zYUhY^MR~_9RcW!n3Gu)Gb#-(>+lF$kfl{vXOD1z*wF&~M;L2tFaolPjb+#hi6A0UR z&OZOM`*e`0X%_1)dh9IoT1#(tBXPEtb+zs6%YDrn~HG z(oW~ITZr0%H%N2T4aFi8{2qNvHta(PNQUG-;Yna<%k-WtlyIt<{|uuAZAB_)D{9y$ zIetzg5b|3NO#a@gBA>TpnO0NQ@Uw0@o^yY9%+uc4nSqXuO`C;?qi6V4V3y)@OKtYm zpHkbe|79byPL~jJt#Nwl&Qyv(^GV)BN~%>VsnW3jTz&deXTh1xD%S~I&MvP>uj@iv z)l7rH@Hy$svlZXQ5@Sw}xT{fa9bMTn24@FN1lmEK--PK4r6?$g`DmsA<|4SuUomVb zbO|#{-Z;YJ7kzyw*iM46q-lm1-L7|%vrz-aj758n)kf}kl!^|F(uiwa7SQHtY~NTRo84GroFY_cD-RED&zMXA-{prdlK{7+6JJI}V{CD$a8!j2ri8vGK+}{PmIf#vExxe zUDOW2z3s*i0Epo)f+4neLsuo+IX+(5(C|&ag9aI6nHnf+=ml&Z2Ni^ko+!SIk0|%CaO!7^9@?u&@!#4mr5E!< zkg}^vWJiC-{pj7a`sdD3EKobl8UwZhz@!n1fCLv=ul>{{v;8af1Ueyy1v&&15@PR+ zkfMLxy0MFs6?=7HiSysi@n}v=ro`i<>eGhg8~lWBl*G>dJW6ko2m+c!UmCr&2}_x< z1xYat7!C}s*)uUWB5dYiTLQCedq<0v>Z;^iIkS3dT-^g{zN68P-bC zA`+jBOPL#MQpR!Wa6dnASO&Bj9&HlIGbkbk(X^r0v8R}!sLg$`xOGg$jGlpQ8m~?T zUk~!TYCVLs?mJcxmO3C|X!SVHe-)U}e<2<};y!0*$`Tq=L=Ojtjjf0u(D`2Et!evi zmBDez1E%WAavAi0QSECQIL9h)kE)o6bsYD1HzXyACIw^pV8&s|<7(UPPx7_Fx0cMK zH&>-^YoSh1n(-4aNoE?Bvyr{85O+>jp45GjDvv^{;d$EIGY~YP5UL`07devTf*TZB zWrW_%P*D`O>P}bTlGA_VZ=YlR`G*nyuQrm4jf?H6cTs9}N+h-%crq@>VaamTNB{a} zbP8uq02H6bVgLf^~4So9y+k6*jZEg;M04_3joYAlft63(ylRAd% z)>eVroSK@i61gP2&NQ!HiIhU!Q@+RcRO=fZ1%Mj7h;A+}uFxcaT@D1OK$s3KyH*#_ zt>L?h{GUdU_oc)OI~rF(e)nV6qxuHaNOCUajs^vhCpHF67p2?d5_)8T=kA&&2`fMN+;XF6klpwM7E-u{R8 z^mGgjC5(}>-GNjEAGj@@{-&aD-Pr~XlF@Y#kFzRAgxQ~^RfAf}21I|Uy#3qNY+6LO zRq2o64>BXnKDV4be6Zae(E)6bQam8GSYni^+NG&Zc9 zi^1wc6RsPbM8x_JQ4AqPmatki9iD9Pv-gLa9}K*LO$5NWr**)2rTbkh6`e5GQ%yhW z$Hn3B$9CL@>MMuJGOT?oJCnyNiIKhE`8`$CDlIDhCOk7o!(=Go+4=cYtgNxarT~K9 zI!S(M&1wHOQ)caOz8Vn`aV(=9|C`8F&Tr6l1{NKlAQv@5`ug5`dENV>SJekkO??0? zfoDve@-nB_*FXx)Ml*Z{ME;l0BANuti1-6g^zNmsX?;|b6Y9r~lj&sk?f2R`?s9%7 zo%L?K9NznkS!^JMPd3uzJ<8M56Ie3Q?9&7_-EpS`+uynmHgJwjws?3+0mR14NC4cc z*4}%H4NI5cxjjSxQIuaRFMtK?T3%jWCRd+_Owoc{|F^r$Swm5AywX}yzzvYxTmd=c z1y#!UKSyub%a)_aD#3pDt#fa{I zL=Y%qKLL+wp8Q>K0LJDylk*w2=$gQyJ<#tyDp-U#Q z3O`tCXlMY~9ymCu!z;B`Nl$bazkZ*bw7Z?GfJW21yF2jM>UABb0hz`EINXDSgTaA; z59}tEAB{~+re|mE7HXtEHUOR&AD`AI{QZLi7oL;WYh7(^VgfA=|Y}ATVkIeP8tDEpdRs3Rpd>p84re<*39ToxgrQHD<>t;uZgbpD*G+ z>gb?CKrbGw$oKsGxR3)L_3Z3yYp#3Hdl@ZMsG!#A|rsH~&GX-6=630b%ih-MwkbXgG zuEb%zuZn};NS!<{Z|7x3ZhI-)-jYIwQbtO38veA@wAw>6`EdG;`_gV4WtO#U{aMW{ z!&lHvz`tzLc!~cueG#MPPm9Ui?S*)bjEO$`!kOb{4omj!MMZFF#86%b24&cb}sz|Zb)^u3-!un^ovk}_rp6sd~MRs({lGB&WsYp*tsa5yqYSpchSGVw!XCCf5YWf zVpI8*y=-rPOJwPuY;@%ty6O2#d4{{&)=M?Jm?L3~i3O+b+)g#_%rBU&q{rUZ%8VGW ztgmhg$ka4vxd>9FMs3{L55Fv!w zaPcRNs@}E=(g{dVs!V;Y-H0;mdwPp;1V3bb*Rw2A`*8k|-GA3T>YUuiXl!_4auv&_ z?80#QZH#O{wApXjg99`ngD zF`#Mg#a2j7M8qIaXhOG}@zrLd#qw5<iNK;z8d;9x)O4EVX0>uXh4VTfc~ z_wusOVx5g+6rFj^`^>qye@V@O6TbOM32WA@pix<^t-Q6dJ(I*$l&jXzS=qTlNnR1K$n! zoBEaN1GyHqwrv)Z`B_<6fDIJ1f7OO6i@Jt_7^6(D{Rs?;GFG|2zXxWue|j2_CPi7& z3Rzhl0I2f^Rn_7v-sye$4F0|2wU3rYJKg$ zlK6toM?i*8tyb*fUfNvbq2BC6DE2=>>5%q_EJYRz3F_G+@5hcFkFa+*kSs+Mtr{1X z$)xJ-^d!v}Bs>+KR|(G{<=plci_3aj8ejOH{-o|$_T1L!9nnC94NThg1JsV*R9{(5)dOwHs zEB9Q?u^XYc^BmRB{8@TltallAvs-~PA9!|L>GYSb>bFNa%inWc@5qdm^{Vw`btE?{@-HIW&df{FL{oP}=h0Hx?#?j?& ze5=|IRp@&8Ud3%n`lt1H0K3R!TZ@qG!9;U*^-2o`N%P)?{5j^EyqE7jrrJ)N_(6aA zQtrXNmjB^Wy=FPtY=fhW_T==DTFv)+2mZ>8I}&8^wo<8l%jed&N@Hq*AZx585(wpI zF1!>mw=hjD~Yx%jB4sMy(6Y~I##I zjDYj>`0xPv%N|%<8LNRZjD=!N!zuDQHl|C$(9m$IZr?Hu&>Y6WjMZ>)YJjGmIlyfP zDleOkjm7qt`yaswGd8_Ce^G%z^Tv^W8#+q{^R z6zT0!(4!8#j_IqwAM!S4{rkb9Yrn!@_U}(2AR{9qA<@y$eB*N~TktY6G6JG>vcE6A z@?3HmlzVsz^hUG8MyO+}Wtf_krD$$fd{`}Yfyn>u;vuMZQJnQo6h9Kh@nrfv#j!## zdcN4^a*vQvkZ{}U?^t#DQA{q|^@>iUJR^3s_k z-jbp02aB7pz*u#@phSY3uH;m%S-Ursr*A7OY?Gi!Gs%zJGeVDbeM5|{lgc>Bt*%f8AC>;R z)WNy8P3t)|j z|5$_yc@+Z89AGMHXlepsfr^WjvhoDb_cqqnAE=u&+q`)^&dCYhQ8!yu2%EnC5a#bv z3;Wm!691DE6Ng7fFJHba*KW+A^8V9}mo?5!G0;Qs);4Wh75JQIQJO}I0?Hr|v4rON z%t%EQN-UVGDk>+3>M^(iis${Zza}kc^0IIFQ`nLT=iiXq*m{7r z2_-zh+dOC{d8+TjA#epbPo!Y|=Ya2D#jg_*pScV2OIAHuo?K%n%10Gfwn_i9SQE0N z+Mp)a)|Onc*0R`>mkSZrO!HH7-fIt#N%TIG>Pr((WtIw9tMQB{65_SH%V(i}A*@n5 zwKRkUk8SFJ*bu|`uWg!YX?f#WTlwnl@|fHmmRss+ zU8ZPgRVqUuMn({GFc$ZZgd#oE(9#ljPRo}`uIZ;V?|z#{w?V6T*DWgxF#59=bf?NC zqSw1GEy3y&Vb>Bq5pnp9=ZtNT3=psyHZ(|~LE^iN^zeLYjh}KTiEds)iVBM@rnt5I zF>|RDU&sJ`-ybJ_p7={Uj!s5V05`HS!tjeV@wLVofsegz3xN28yA#6 zn^&ISTU5M0f?7`Mm}3td;AeP0KVFyXw(jrm7rQLvgC?tmxw%5lFLclW5Dr2~DIf`; z%f-1{I3EV0zffaQe2~PfCr^s$I73(J6$_MV{;Nc0y(er!>5q!Y3RZ~4YkOEz6ZGGO zO;;qOQa4F0=P<5X|Ta3m=Ax6P0j*5BjOsO78bOSx%qh)O}PH& zYPTH+rG$I3j&_u;oOf)W*pLPYKhgAMW#QA%Q02Rd{LpR^N^ty{*ZHGD)`7OD%y2uD zGKI2H<}(svpePg*Ni$B=DZNAfvEXuC*Aao-9|HZahf`75&4R>o2JAU3%mlj#T{FRFz#%OJ23%#& z#nx^6vX%rY3w`?hC6W$Fi)_Qpm(?QPeJT{p$lH}_mHX5O&{o?ufH3}DNel8@o94p( zoQSRVOUKv6BcDlMe#E|#5rr*%r0Dm0r}_6( zj9^=yEdZLY)N$p!|0f|{vzsdTEsrF{E|nnj!}6T-+VO{{p_-bY)mDeCb4s&LEFg(! zBb*v|tpyKHxnDD-1ee=3@`44LG9{VXzj@Z>Z$zagh>1~*Ea~jC3JJb?*_%ilsTg=%~$69Hw>|rT$qrf9IzQtjGZi5ed@znU@i_UST zWl4C+OxQfq!!|X@Cgqi)v!7VLeP|y{Y~7F*+azrhTq_}`k4{EJ92bc~cRX3BmFZq? z3MJE~vYq3fT@3zY-DP-!=B_%+%CO z29V55Z1rGy^Cp)v$l8Pa&6_tI9L@t@Y4_ps;SbiYjusdC0oMcIAW>)~&b7=|$P{#b z-ZY23I$0G06ycZsN=ccS<$f^*^67?TyJb)M)|c!#J1o~ni`3XiK0ZDl?*%)= zu>gyZuviHi836$UDTKF#DTFlyCtOhao~JKEcu-!|$u8>!tE z5wRM=TF8}4AOn8LgNZyDm$t!-C6ILCs^#Nz0Y2g6Ez{!3oAyl%VF(oLzaD;jp!HS1 zeyo~{nudRqRyvmyVllpkxbCHAgvaAdfr)7%es_zBsU*4?%u%UHZdth93no0-=U&Vr zT!)(dRy7lkKJ2X%Fxb5d1?2axXmNNpDQu3d#itabpV7`E+VE&T<2Ol`&Ihx&LJD%i zuC$mjK6M-B^u@wYV_lu`SP_2q}+Q>21okEmjrbM4mqC+0V$>QtlQLE;_aw87NJW! zndY}BACcuS5nvaHQndx9t(}I;IxPGBe;KlNz3PcENcF71{ZL;Z4(cT?ZxfO3e^Xp4 z>=EizWR`ur4lIJ7TZ|1BeRY3ieK;J%NNhX^6M@TUnB+;q=NhSldETM(i|cblL_}1S ziQ0T{XlRuS?h!5rl_XylD-U5xv3HRsxBYL31>?Cv()8xe&Vi6@-2szMQ(+zd-!r6$ zs^Eug-LbNwYbM;PT}TR&MPuqs;@6qTN4!QVMvCoc^Zs|D)Gmi-^a3A=1j33)$Ii>1 za6jP9b!9Su5{NP%AFtM!hd?z*t`>$PXLLU)SgYW90}p$n;r}`ekN`6-eYRIiTvqXS zNq+qHktT)ufE^^K8(KU=51h?vDQf2Z=; z2g8e`p3dt+$ILvtQnsS);lZ;ZZ#;KO;}&XA12bGVb_x?_kO`|0dc82d3f;xB5CCj{ z)wP7r=}&l&^Vo;FT&v74_u9lS?y2npZU$vqru=2 zzySc%Au%cG(*kH)yjrjVB+6h%CVXXGtf-|auv)KIyZ~SS-}!~!-rl~xZJt?M@wtNA zBT*ruM=%zaF$?{PdMx|*b>2A_(J?8_hc3E~j3g)0f3hJ-kMi}5NP|xa3JL;&Vh{gcQR#XE z812QxKu)H?>zWC3IGwM)Xk)rM{X&;zVYyVhG0sP(l`qO;%vN$o%tE^cV4%=lf0op& zmlOZny#L$KqvMS^+-E-J$O4@`C%jQb8_G)YGXBd#fT!uWV;;{MK$+)YFGmAx(ZqR~*lUd@XA3|bKaYM2 z7-#@F->mH#6crt?D1w3+pYmmsbJ(+3(Rp`vUiFUA-D#PxiE%Ctz9SW{*5Q~A6nkxe zhXH1HR6q%4l3`#dDl6ywfiOH8>DxI?s;lKW;2HeA+by*P^eDpvjhUI5eH~D(1vI_W zD=WT$77lPukO97bFe66rZZ}sv0(Z|Ffl%NVe_@l8v-1a+owc>fLM0hZ_X__Jz2|(U z8(h!LAhG|S{{Q=v`%jo*Z{+M3GBSgHPZl>LGFRIobq^elVye*@>?o*m(=c~hzwPC$ zVOB05ALd%aemZe9kM)m@RTKe>%5X214U&@iu4e7|r6EC69lq!KV8|VaWx!xTu)~W} zZ|VVa$7M}<0df@N-klj#mWqq%!1(nixrpI9VL37?tyxf~oni&QOtF7D(`Btt zVq)T9w5MsDjfj`tWyd@DucX$|pt>~^Us+RQ471Q>)sH_n`BN}D?e7A2t_aPY;TDE3 zE}fm9&#!(0X@$BFFp3_0P~B8Xl#XUM5-O@rl`&#-Pn6EoRbM&0i?tizO8)zX)uHma zaIT%x?dcUCjm8>&Ypn!kxM}gd__u}p_gZP5^|#`{{Q$X7)ej#EeT}&zi?58$CsuUu zIr%AuFDO?NYsl@%#{E`a?ZhacE}nlt52A({W^y(p)0+GWN(5=2MBlxe@BLumC*10^ zgT^V?+e8^wWP7I$_&Kev57A4eFo)5+WTM|!0(;tco8A4x_vb2e zGu*;=!SoF|RE1XL^e&Zgp-=z?N%;B|q*yAht1SFZ*1Cw{7Uk{j?Zw5#&CLtJs*F%e zfC`zs5i;L5AjH*b-7wndrAQUaJ|NtXODIzZI(HO7C7mQ)r%*4E5b6^7DV7-SKZPxt zzhgqF!_c|1qN1>{P;oUbk-bb)b4t-`9mAn8JvnN0jMF#Qt!-+&wg}f9I;m~a*CDg! z3$;D@NNXy5-diTzUU75h?B&1=%&LIMNR%tpDo|1hHJC#$+z$X}{-u(zurM;ZiU|n9 zfD#9&#=%5JBYe}f&JapU%ELGH&UGEGM|*qoAMlEqGyWcyAS|SiJNP2Y?D_CJP*7#U$=VL1hfb)ck14J>OqRyI_D2tOW!%vyNg>}--n+A}z1 zmi6|JcP_81>g&~PjIGLnmX(VVc^uQ^p60za0i4=JO_Ne))raGic)BVt^E8(VNECHW zdEtxXaLo5>>aOtM|Bu7=uj7~=fQwU6qoJ-2a?O_3*lNaP(nPM(7s)^Batv@1USWpu z&flOU+pBXQ5^cDWOluX!vbKsE>4wYh?ehB*s@cW9qJ1i*lU8?k=+-i6>@Ot=8UeF+ zkj25!Q5sNYm_h>+NR$3?V$ELERv-&zjxyEV9afKiAOieO@5KPW(MQHVigp1$Lot@L zF|OqN{0|0b=dWN6HD5)QyBkphG!3hC-WgPrB1!Csu)h9pprGdNXQyCN-`bg;+^gsz zk*x?_cL#sUuaUI>LuzX30irjcW8yZ0l!pGDA@|hsV^sEJRoaUo zp!xV*u}-~OS$A_+n=i?F?Flo}Ox{Zu)5|P=72Tq2|4v^(JIy&Mc zP5y3%a)_!5+NMiW;02(r=vO#Cvw^phl9DR6)Uj&Tj|k<3Ddtiu(=Nh{c~4P%&pKb6 zBLO_ajO8SO(!1VM$jff|5Q!osoH5c231N`EM^omrzdbX#s!IER==utXD zLrN5+rAy&Rhje#$cdMj?v`7g^OLuq2p}UkmbRL>Je)|36ckkVgj|yj>*|YbaS+i!X zcfBv4tBtZU-khN&^HnxNK3h0gW4l)7PAghu`++8mQ>x4a^He4=&0NY5^FzRqK_cYS zFyzgw+ca>C?gRLYcprd3G_h+{zTyo>a7c)|QnNbPJ#*IMGX0AM_+QVMKiV(Q1X&G$ zrG|ZI(7LCU1m4NpH!hEtRp@>wj=PyWX#7ZOz-=^L)VMvI+sYn!LVPC`xh#h!Gsndb zPZ2>4vPlv6TJO}c#FtRXMRgWPH;jXV95nBZ4)VM z7+67zsJ6#{q5Nm%l+G7!JS+0Tht4ogHxN{nFCXJf6OKbpnL7fTagh z-%5QWN-oxA3qt)(kpHZUR~fu0UQII>)N!@L3NUK82jZ)TmHY&zta>H9FXC?Trw;-A zQr0kfMkHRGW~V_Q|4rEf&|R{0;?i&X2%^>M6Dd`;O&6jx*g$^91XOwu6X+Njt%5CJ zwe?)KDUiT`>%4i>3h+CH$L~$=i22(b{r}h70y9DiVHQ=X807eWuCI$i0P(phhwIgu z^{zK}SGHt;wCOiRF5SpfQ;_ocbxz zGro8seq=FLg5u^o#n&Oik!hEA?*t~-oq1>oEk5!0YTz*$d& z`U3txj=e?$a9_Ug1y>*w@_TXq;(e+!-Kl~d^6}>I7%HS&>#zn~SW;3_H~v)E^WQwY zPFo6`P^M%o6f(+fw;N*MnZ|C4es%Ae@@wzuo%S!&*FcC0h?u=v*KMo^ zRTDlS;0s0$xXjkp)(#~G5!**=dO^WkAcZjISl4{D$5+)ug&i_&N|JY`U21?>-?ZgV zC&@uMdkg}-X>oDJ`ueSBmd3`)N=n}s-dnhV;DhP!DAP}KxyrMUVRxqSc2GqCvaJ>q z`-prbz#iNH5K#d;kQVc&6bTH5PzebMrT7DT2Es^yQyw^Uc?iU&V{L81fVC|^7>GCl z_EbQv*FH2PdzBysRkAdybJ@)&*E3aXtOthVY-=oNH{!;5k|zeHx&vYYGu{s#JRl<_ z1-2yzjDk&mlze<3S2cu^dSm%cL!<9jf80H6mOZZ=<^S?*@VFgTA7j-503|1Cy%j%8@7U&=-Yx&LvPF3hbMfU?3Ud+!R*`HUyx1_~byRt_r>5F@_4VFXV4Z;Jf! z4kT61gY_iay88NZXQryk1>vWN+U%rlz8L zCY#*jLWvy$p&ALrW0)30FQxtdTQLc26;*e!$;pR&&P;^lKS45p2^|1%pM|V7oGhPZ zUOLG}SUTBbG&C|0b$<8UNK6Od5S8dTo{5>df(!w8wfTntWbr*Gr&z5t0~|ABWVpB; zDrnPUk`gr0U~F}Dcb9h;vG%CQ;PSPB&Gshghj-6^TS+~=Zvg>_e9J&d&a8VHB^$gu z-k=;_HFUd6 zy|%Isld*)kj&S5uMzE#Z&lZBwqh<=%fRixa-){*>Cv$ZKj#_|` z&OMYcwRZXZKVz}KH*1?1o6q&OY>Pn07%{|cZ&KhZ^%!FaVA~7_Hoh55`49*;9^Qlr z(#6iBX#^F3pwuL%`A-k@^7k$pwSCh(!uuQr;jtM~MR77GcTW|*9>r@Lef?6sR)n)! zDQmmWP@AVLET3*E>k%pVF~AGEt4uVamvrg?YiBwL?7|DR#E-!E-g zu3KdO>U29Ng`I;VuGh$eSa|s@dPy;0wVgY9I;Z5vSpP8~0BZz8=$ZupEkM^zsSbd4 z<8RXiiA_uM-5RBJ!6Ug^hy_Iz0JaW56w`ohlSBylKPUJ&*rEDRuDjv{ui9sjw?>g=zTXf=AW<8%N!D0o8O-P`_4pI4`29RIbRV?EF z*)sXlZ?fIUKY%^TrvunTkVsB_2JX`z>2bs9q$U`$K-={HF>)TJdM$%`tMMJ=m#1_@ ziC?~KG6zzE$BuzN4VqGFoyE_@+#;*R>HqOMp8e;4%xgl)!S0@pU4&s`+uF) zfA&(r^WgnxQBjPo>wp#(&+Guiuo z+Qmv{rB?l~ZvWTZTc&&f)*8BnW&|m*`%*X7_x>J!ba^EupUKzph5!3EsE9BB{lRSP zT?@j$r*ekRS-rnm7xps&qI&sv!3==0?4LtJIP?3r!V3g^j+@4h_vm*B9-3`V7S6>` zJo$S9zsPS9JFD!sh(AEqDTo}kA(${wzHw+^z$=v6Kl}XZ%F?Pe(ITk=iu=zuT0UjI zMSPj1a8c8JA4;oy9|(Uj09R-W{gKmbwJq?B(qLlAc~x7$RRK;f%oO>bZ!HImW+230 z@)G;S8x|*n8dd@D9dL@AC-*lIGqX)FMs(cQ@Te|t;7bDh{HbTbuaN*R0~J8f8vNIV zA0fVU3sX`Z0vv?gq+V;rnob~ksKBQP%1O_?eKkM{XC)T^6_zmr+&wZfvYpGHOLu-m zz~6MaJTbrV2bZa8;9yFy)YCh#qkiB0zJKDH+D|huEfm0=lnW)b0dff#vyh(Q;e!y2 zKVAP;*uq}&8VM}lUgvf4YtM#MBB54{S-Xn?_ahq-**iHbj06I-T zzzNV48*?qz*td_4j!sQ+frX;GJ7^)e{J}qcVt)3c1wjUkph1=5V6*dspI}~re1HdA zK0}cHmuwY1GHtfEulV=cZ@VB)`ePNjnF1M{T-O!)8V!SG#iE)E%^yxSqkA$~NlZ^{ zMn+z$$9UJtt*Ojh3WWP}5x^=Qf(_ry!yL;p9eR>xk{{s1F0=HQ`@(Ds^da zbzZ2dIXT`zAJ3MZIV~X`mWMT`m9orvw?d-|YZlOggTYWeL8?&QGgEBr|7rJC!+#BC zQwg_v(Or!+GU50Zzme<{gn*}M526aiBZ8yvWmG2l-zWNfAKyuUY6ntfhN!ZY9iU69 zaNc~g$fs=)aYx!L2(z49VErJ8VJ*o&*WbJrfR}nO=zbjLNhI+4#k!6_?8v1A9Q|_9 zs*c1Z4QUqc&x$$*^lE2kHK-)T#r3!VC{8`IEf@j+pEum_g?tmgtzfQ6BTi*bXlLP2 zWi(l!`MSm`d=Q?7=5{cPv%nu;A_Q%FbHM_quiB?Ld|OJ(!{udi$2sS;s)x!9s{ zWyhw_h3_&4C_}my0*AbOts;YJ{L;J`HT+MZDa345&d0|nJH|wtCZ!CjtuZOL9=AhD z5H2CqSl>i^Yfm$GC$06RIHSoRHU2wW903h}1hQl|9Mrvd!TmogCNe zeZKMwk?T5wLLWoX?A>6UxY|VHP$oggCav4ADGevXp_?@oY-MDyC06)U67{d@LAxfd zS5;ry(GM5py4NvoDh5m;-OcRK38#}}ZstqtpHh-lkF%8M*SHi`n&!RE=VbzB`bBaE zu2?!|KGL(@!ME=}>&pBqys`PUVk`Xp) z%>w1$`=&F3mVB2yyO;XT+p^8mR%cP|qgK~+3M-F1OmD9#7jo%cZ;npIKG9T)sxY;( zuQwYypClF1LTHy{&nsSj^IF115#Rj$Akt8Z}mOQHs?RzBL?9?pmK; z%QslwmYpuBLVt~`NIXPQ@%{B&;FXY<@5RvsYfx-i6 zIi}m#X+l^U&g)uiNnpo{gc{biR0-7z-gOI5>B?A`lD964L0AryBrD6Lava}*MX@+(7? z&pK@>%G~W(_`0F99@nQlGn*)Fy3C}LU$n&8Ct)QBf}B=cpu@#i=g?YoKF+c~)z9fw zyQ|ebm6>r(@7isI)i}W4ih$qF!j|~spYk2~%EO#AALJ)b^a&qTWB#$D{KM92jbe9c zpIz5;9;clpEL#6dV2~gys)(p} zAciY8_vx1T8`r)0sFcCRT+D|N7{_}!+|CWv^6l;E#6(1_1C27M?JOOH9IwU&SY8h2 zd8U<&c!$wLLnY&+MJk>%mWMUad1K?B_igs%`8V_pRFwDaWeTbDRzg$eburQ!-#Fz6 z7rn&G3nbC*&Kg!pxQKcEvDoQ=8k8YgHItEY*44$BM#=!~K+~vk+V})Gm_c6{v5eLxeLu z7{nD+u(_+%zowx-AzYxWs%kr56BV2-lazh<741t>lH#W{e_+9r>HS_s1Pcp^RX8tYRCDHq~2@zNP2vbUPXC8Bx?F5;)zj zjLF{Z#av!s&2Phmk(l4RQ_? zQVP!bjr9;Ij)tos3nQx^g5z{u3k%w(X#nZS{PLxdZUGsFiXNab08Bh6us1f8FXir- zm=?|_kq$7ObDLEn zx6)?teWv#|Vfn!l{7W~xTU`)HkvCaqQY7IO6E0?yrO;GCkODKB&sa#&&h>T7F(t`H z8Q&9rE%UX*W($wAS2z?Q7d6X+#@=Krijg5m=s3@rva&<9mo{Z9Wn9eHVU*Keo53|p z;`eVVdDui*H?DZdnSu7+%TspRgnnlexA;KcJNM;+$nD}swWAZ%V#S|3Ew$Yp@=e9< zsW+;xneg2=^211T!blc#>^k9)-R)HbIoD6R5i~z!*|uD14>$2LY@8ixGa@JzCVST$ z-U^5)=`tkr=l!HTE*qZMgl8uCI{1XUT1fdYK7rxFR!Vo*EMikDkIE+~n^e}iU2Y^A z-q;}2UWZ1DCzDn$?#kqhJ)yH;d{qBLDclvlH@BP6!PsBz_&!X|nvcGccxOXNrAAbG z;f~$BqMDm2JwCTCTR->8m-pgmIX}f5O(aez&p;k9`h#GdoU1Fm$$Q^>>EYESNKJxd zZH@)@-wmQ4!Po~}5b@Cs_*3m{%g@KoCE zs3w;_jck2iS`I;5b+7bI{F$)i1C3@)&9g~ti8V=DXisqNwY2Qd{^FA|BJLkk>&kRT zz3xY^EwdDQef0S1xdaP8z!#{urowiD8s{rbnJp!yXQM70USCudr3n9=`$AR4hu@hl${rp?A==n8!C*0y$aeOn$*IU-5ZcXI%md+P$oU4IoF`$7 z!LVL8bqLamOKnji+X#GoJuZgM$?+6Z?Q@Nq6~BYzIEktXjZgKFS95ETN z^~S)9;E*f#x24-9O|nkP`X*j?a}=W5+NBpgt>HPA;V((ObEMN2iz)Ti=xgeDo|X2s z8TM_AT@j6Cb$NTpsuO0NGx^MgS_LF-FV~`MoUn85-I@%S?sLkeeBNB8EXgn^RnzCU z17FEkvC+hIA8;-ypf;}d!KRP}2xNeoceYWz!}=Q{aARGlFR%oV9DsKocYE*&pfG%b z(8>0eq~xP-Z{YQ^T20Ogv#wR}CyBhL%1DOM@z+&rR~<{ti)^qRd=~dfTnYyIS^nel zFrBnpZYgovBvT)+j*HBX;y}3Ar~D9Z*-^beq3&>0b}Y6ecB#czv)OOGfjgwwtbUyz zcLFcYDe7}gt#dgnz=t?l&s|Z_96fq8T#GZ+(`9;uRIXD>^|sg6a%z{?UERWHmx#?$ zB^PD?iS#5&MsDu!Eit&)%C-9arX+ULh%QV2*Po-Kfu_Sdo(8ao&&kR0-~$`Y9qT}< zC^Im4?ryL4ad_{Ug#&Q{G9CxWb6o5waT)*-XmsA5q_(^%_U?C0*`Icuek#_tNLpS1 ztK?V;;@g^g;P8h6bMbMUoc@3vI&N2_bWhAV0k_qpGD zJrKPt{WqZDuAr@5D6gP*hv9RIJn_IAh0=cak=RQT(sV3yQv%%5m(PX0$NFO?cfNEq zRakmSG-2sp@yl`cA`B1f z$@~>Ede^R2R9)?4PaB8X5blymfhsfpHT|D;;?q_u`LEmaSW*pmvti|AQ%o0zRk6pk zV>{tN?XE-_$m{2i9vV4K)bD)4PmKqm6fyRfZsX+pd8{tT+BKXN9G*_3+&A0)W12IA zEdf^;)eb<0;rHPdUr~vml14=0lW6aoGr6LA%|?!bkxD8i)*Txg`?Ea=xIyZ2FKKBz z0ryA1l5jABN>XPk@wJBlgI)GNEi8AQ=v8C3ISwr@3ewVM%}AFM1j5=~w6#+%1ww{h z#wRAQ`lz7j$uIs~kF~NAPi@6J8Weiv2s|`H1pjJQ+X(E+ z``J|6ECBS0=WOOb=iPHV0aap)R*xcS|2yth_?Dq$)!|}=9Em?cLqP=b&zbjzi$OdM zWPk+r>|4tBK(JlT^YP!04!qjHjT(+Od3ypHsNbw^yZ(PadYv?uE)oPJ(m?X3dWHt{ z{om%Y)BXN!`kpoM|NOu{_3YoX?%g|SMgIT92hsk2fkW_tSrV+n9SPl(;8Vd`N4dih zIX?gJkhGYzN{P3zf=o+=E0T+aL;**_Xbkee2a(2eViH9ZNrvx6226H`6dY}tII6A+ zX;N1zJ2iWp=97<;@b|t^U91E-6XCKPFZg>=MJG>}F;y1jQCqAX<`>kLcy^{D7rSO- z;N({AXqIjid$7S%Us--Z8>wu!^u<+<(TA2`h~t=yFR`v6-+ zTB|XIlW2ydrEjGyw(tl^cMPeS`=(R8WX}!?`$EggKQZjtin}oJuwH#l&-Ask`$kfn z!D23#t>&sABo*26)R*fxh+;KHRa@f{1M5c7qwvxkKJ3dgqGb zPvWxoW&0Qk;z^EvXd!e5dK_%uzTp1s_C5FgOKq)zKIDRh#YDj$*-r&d>ypBQ1}nW8 z&SLp$lT>lzBzs1!JEli$yrO@%!7Hy~XtJ%(h;w5+8C@y$>!e1;y&=`5?3WatmK5j^ z_565(c5l&5l+*cYmIKk$=n%EpI13$ne!YuzBBjSs{X|S z9HH+>Fh|=V+Eh8$d9YSj{K$LGR6t~=p`xX4U~aBKslV|y+4bCRy>?rnsJ2pbIal8% zrAEndOWeR=k%MSyXrw`wBMe7EA~As;CS9m?Ef#r(ttas(Say%!;kjNbj`QKKw>S4q zO--q&_;h`{f35CV7nI8k6z*y@c-^E7h&xcRmijdaKdT+zprw2EY;J&fd%7l#+fh!T z)V*aIi*sT|qBlg$$|Rg~P+FQk7LMxEpD7y~>p`=ywyVFWB9!{W730ZaldIvk#H3~x z+v?}`E4oN-DlQQ=!oCFebvXz4idkR07;2!wJ8#0Hdd^qI$9KH?8e@O-d%Lg1?)$FG zuXtoUPic4SQ5ia%Zw;3Y8K+Bkjk0^X2be;GSTYG-_i#E=fxfI~e0g7bqH~K*diIn`We#fO*QZ8tVsO~4vyqTBoHG7d4(=^? zjvur($GvNLPgiKHYC%_QU#?Yzm%>n|n}9$3iQf&Q|LCLSAgC__GgJHQ~a5r6ng4BCMK0T*rXuiL|Cy{J#(QsHL9_Jl`sD1-qwte%k^r zALYI!$t_n>Tl(^=YiL+YoEkBCc}_ki$&cyn8IP4J3+>W|1^>8Sa(C6s>4ICxV#+r* zq=F6XFOACd*WV0(Dr%cw?hPP!>l=DM;&n%j#k0EO%&XZXo>`Cz9^EUcLbw>xW$ zu`b2Gi{U{JSfi|?h$89xAQE%i^!!#ezZ^I6LrfV;Drha+e`O5d*fh=WbgWu(>X&zM z;(tBobXj*&h*j5Hk&j9JLg}2u?fo$&h1QtB=$6th^F1|DR(z&O_?kn=_o+`zAp*${ zvmuZUj*Z?T; z9(5_z7Xdp8+VC~=5~qrF<1{{Z?#QXkOQSa&QCXMu*$5<3^bF#&gLtz?9UDjVJz8dl zUHB!rJF7qW9johNHuBLB7bmso4dFj$>t+&ZQurLEyQYeSoyz8id3!pglj-A+wY(lQ z4nXmGgL9kob35sfJ8suEWVI}RExz^7EDYIXt_`WoM1%9z_jYwgsj9`=b4Mn_kl83DaS{9MEW+eRE=+^sJA~Hbf%V#oQt(4Iy#&-X)XrrxWA9(#BcKW zKtHA(a5jKSw*q;E=PzG=zhY3T3BdeTc|IA@fRpErgj~Lj=B^?j_ON!dPzNijWhfrG zJaPwfg5>L$xr;|voSKdQ`lfieCHFMKmkc{r$*DC;A53+b?l`5XoKZbazBSilfd8>j z_uR-UbNy)JBq`wBf4TwwcCwJa-#c1bl3I6==Jhs`GMg@YD2q0Rarar(f78cIepPw()O)+$vRW^i2yt=8kFG(sIwdCN1MweUg})d?Rd}6Y zvA3_AeQsV=EhCsE@gC`V%!>Gltsu(-s0T_rpyOq3zGmBl{o_^oZ4e@>G1mcsS^xsl z@_h!jH-MN6rfGM-LMD)OXp9_z9}y&twTkwH)=}Gg){X!~r;Ll{>z6MTmQzFH$HYhi5}DlaX-!RmAQH?X?S0pCi#F8^-e=DvrGNQV~wDSEpt&XJ3n5QuaBW z9KIu^2*ui+7ZCclf90wZYLPcL2zHmpPnwvR*up6J#5ZpG3-L>wDK@eiG$!bp5fm+4 zZlw9<7Zd=RAdgP+m$h$C zQK)#yt|l{tyYDwJptGlmaA$Nv1Q@369^1qDF~QV#<@B79!A%%xg=%-&hT)R(FXFJF z`NgVtZx({i_9l1jN&|$4Sh|!0Gz|)TBYUKm+ znQX(9O>2hFZ9$({SAp}h8TM9ce&@}qvcXL0B=dbErRL{vgf+hRKM2c0M7{u z6?_GW+#9df?oJixiKwWuyr;$VnIN34$4!J&7voW59Ox^}=O!cDDym~uWS5$GFQu+8 zw=2GqvlB3SREsIYx)^;=Z<|?=sFIf5_c6A9z-H!c=gorh2a2h?0s^7xo?%;S8-`Hj z#UZuwohka_VCH7`r8J4~Z~-?Fmlu4M(`GGKOJ3|_pN_CyQtAeM4j5|t!hD2&X$u`w z>v}idg|?qZ;t!rEtX|iwsRcw+W)!GeTsB{g)a!({=~f%7no*pWM+l^!!cj0VrlvEF z8-${_iw9XN%Q%E;cfZvfnjW@V2Ru`ebA-?bD)XT-_*gwNY^mK(yFO1&2>&5^>m+69 z+1~t~qRoW1mnZO1v&=63iO7rY;bj+CkWW=go%_ILp$eQyrm}_g)$;u8-4KZ`!DeXO zzK`k7R}@C`&o?>5&>L#p?YZsA!~Ko0osH4LBT(&tR}J{K$jSyLwkyG&CU6t>dvp3wS_8L_|_FUp)V>fq}60pCF{O0AgwY?CfsioAA$aw0vSV!O0aVV<53=O%HeRD*9e!*VyEx@~R;Ony-L+eoQaHyT6%61?mNwQ<_Kew~ zv$K@KUDJV$jr9~+89qfFDJ8|sA2nZ9h0*W48Qx8Yf3ysYVU4N?l%uJ%FXh@;+-~+R zb~UI*h4v5Pt%W$S`>G|o`rOXCi@KYBuNN5 zdoH$RLqntyeXvSdakQl+92~TboZ31qqLupxf`%e>@l3>ijlabjLF(^OTHbp(HWE_1 zM7aM$0I3tF9TB2bNB2gMZGhIF(M*DE>aA!ufeKFI?4!4xU14R^(aBl_M0mQ-xj0;1 zIOA~Yv46aJLC^NAGFjUA*$_^qA@$7#O$5bOqp8t)Yobe`fK~S7Yx~4wTYt(0TI2Ae zbz)lEdxsu4V%?!E4?mz4M^@#uDo4vkmm)%zei0JdAtach$n2t_ zZbHfUHg9%Fc-wdvEd9l!x#5SyF_BNtg+{Z&vNNS%BrqF zAknMA>54;lrT~{hS>#0}aT^#sJY`aaV~wuQjHTWM7y26bP>R!rA0LZk*uLY$J}vOh z$?BrTFHoYP!O(EeZ%dQ(tHoWO9${BY%QpbZvTZm70eP~Zc2MvKoLyHCf1|+1&sL(Z z)!GBA7QorQQh@*obTDvhwPYb6N5M}X3Eh|)AOG^@i__jb;3JiN7Sp?S1aLng5P)Dy z01;<_ERjE}h*_T4p?x7e7mu9e=={XkGg@r|DwZH2kAzG{k5ma(viqU8Itf6XW@ z07=JEDk>_axwK!?uVUkppwi;v)Ae+dd>m}N%F6!H#-cJ>Vo^wP08(lp=jOcgaZ{Qo zjyz1@d9Wma-|;%LbqW3*U2t_kM~O7Y$jD83j%rTiShMg z0LMJ`^BH!>b@I6{7OpGgEi#QDX((ypo9NcC%R(Nuv58ke?K;mEmsx+*D06z^s94m*>5fR*Ll z72P6H!cShN^$(wu?3c(DOGq547raF1&Uw|~$tN=v7eo_8pB_-3sZv~xyIv2CqL7GC zE6{ALWIyytv3z%CxoX)Y%$zk3Bt3cR)0;R`=y1QMyIz zx2HDt<~?DQyQticel4v>5C$)=PaiIJ(cy{`OfIBe4s)_dJivy@Mw@qG4hW$iPD zFeB<-T1(DF>{p`xM9IBTM|12Qb&bynTx|MiAO$-nz`&dVjoxMaugv)>$paSm+v zD~s4qQ8es4#o0e`@DgN*SW#c}9Y>@Qu%otPiiD@t5C-bgLv^3$RbmG=t(j66M`L5P zA9%XYj#Um6UO76P`PMJTASOA`6EP9VyXy~aF>r)?lQ`SxI#RlB-!;v9aPJ=Moz6kW ziD$T7O?Cu4D-+gtXM8WO4^Jtilh4&P)&O*bMijk$$zviWFJHkkOx2KYD3wVxdS17R z9LjUuek3P&q5DrCeE&*g9FHXaG`RiB|}{XFdpwkmkO-t43w*oGUN3eP$zGmBv3b<~VP3wTh<6gcG&BN|Z{Z89k%oz-bi~o`iMqG-t!!puamV zE)Ed9PuDuqX}15yLKUd;x$ZUCuZS8O(~>CaioHnapD0iTXMt@h7YE1gcwUSaQ!~gA zN>AF6|Hd&t`p26z>nCqz{MfBHH0eT4beV73E{sZ?zCK=;GZ8y){#i zyw)*=aw*Tv^nF$^-y`Ea+WZ;|T^cIGn$?Ut$*&RK;$(9;o8S<-!*zVm%h$~7sp02d zf28^`1)6-mMMuH~ttOiu;%y|pkhthd8^h>G$>sPp2W*DXSwTwdxcTfnisi4fF37}D zif)PkUrX}cQIep$kS~{aXt>n=kQY5O7K5(&j&>dmo3FFW>I6qpjB3~T(XMVH8PBN3 zMLoP7?0X>LP~&1IyreHl<(-I$`DL^*o3ph)1}@|LBHE&cS*7-;@~Xcp(yS@p7!sGzw+m9c_XqB793QhY^`Em5r9gY55Ia6PQBo;R zVMHH?h{sP)&s|*iYa{H01osF&{Yb%P(*pR?!Lhm7Ri{AU={a+D%#~M~57B-vvSWzt zXmrLyq%9ciu<$vV`j|iMIm+U)B$osd^fRWMB9+6b(;8qofaG_6Wy|QorW;v9YkiR6 zU<6@v2p{SVaWH0ZkKvle8L8<_9Vt**8&Ko;)?Y((pS7S$mz*n8>L{>-iJgr+)bRDTNAu6iqxGbCgShuTV+Pp&~W1rH!@prKf6kDofL}|Q^%Afr@B%UDUE%7Fd zDjqZQ2JMF=_t2@ou~Lc}NXcCiZ$6qnkHNwBJnocuVs3CW$@3^OME{k(PgtIm(W~ZC z6d1v(c16U3s!A+on(-X-=+MGUBvo~mWW$H^=z)(FRLIn2$0%jtI@Ps$M&E<-XxMq5 zYms_896xW8*3z)P)%#R}3ZoBZ&BfW*hv9aZi-kJ;;(#j$MXxH)G52pikh{VF33R18 z3+1jbwEGaFK3gA5E}U+(;OLKDEId!$M1!{nS9c-#ZBMbVdTmmh&0+Z($KMw-w)}FF z`&S%5IZ1T2FU#`XO#NL=b`iht^**LJQ^?0p;@|LS`~1XP!$aE!{9@<}w_dxdZB;0B zsdreDSar8K&2`PpN@%)DZ@YY(w3xjJbs~H2f|Nj1*@>@B?^XS-(xjvC@x-G_AjlPV^a52xQQANg#T?IWs5ru4nH@K+f-ix zUNn>P3s4;Xpq3Ep$wP$wu_u4Rfugk|6~?UR!Aqy}GY)O#pZbsUUbzNpF(p8Ii}gb3 zL)jZ+tiLQT7lqH{9Vp-Y%BU23(yQv`aW%pGpEG>SV7Yf#3oNLh8FgtN3NgQ zx;aY@vdmWh-Mq-$cRRA!9!W@gXm49*RNgvQpM!%eTZ*1yC0{B(NLJHulOCl{4gLG69I*bQ!#P5p+D|R_odsQcps@^KI@p*2FCKC*F;t#CNKN2W*msg8knI>0esD zvWFOLu}l*6g^~8N80EdlTXo1s5`3&EL$0W;U`}PA6yUC;lEKWL&!N6^YV-4fNa|Iu z{ruKg&fWEfP#UNvT&c0-$5Wz;3Wj8~`Z@xdZV_Q&wRkkZB=wkS?}QoiF@y>W;ZNe9 z5Jz|N%Kvrv}OPwrFT za=K!d9NCO@cej6m^)l-B(AJcOyttGs6wM4BV?)h+M}o@p(y;E8u(k?*lIoQ33;S4w zT-7!6So;o^4*FAq>glraai`qCJ@%}3rlqskSsD_Md|Z_|R+{EI(SQWQ31jDpM~zMI zvaGjCFbm%0A5}Qh@`OgP$;f=nmcP-S%p0{(jHL|}4Hu+kVrCYWpH8djYHRzjY$vfA5WWR7rqU!M3UfZ~6JJY0;T%T{hsfeofw@q#H>E=ER1K4{Wl zhYXs03ccLR^AL+5m8WM$p$HSrgirxiNl>~>WM*Y)`6(o%wo+k^%gCt8B8eHLWSLJi z(|)nRU9i3}i&suNM%9#15S1-1mm@4)G_$5%Ib0pow1Ty$@$eSxyb6npIVbm?)67~D^k2Anh| z)pG!|J~%k&ZNGwYPc^iMzM!cuF}bXdPqQDR6&@A><^UM> z?JW480yfY{m4R)_l8DPrpH!8!*|C0?^u4+epS%Y_uaBtpb>CQG0OfO+^Sy?5l}?fkpR(2-SrD#RuKXB>p>>K?8&nn13+7;QuYTfxr9q=Md$1 z_+RKCmi&bi#mB`)uZWxPp4tW3le#M2C@Lt}DAN9W_7o?=eS9pLuf*(BD=jJZM{b%P z9+%S``3=# z_(7QC08Wv(|8MbO3nO8oUK_^!Nwt*}?Y9sFpi_(s_!{;=4hdA3ue+y$-K~VAf&*E{(p2CCs@u1*vZ0mYk_wEuv-2punNUzjt6=i2<|3lCNs_($+ zCCBh5fpX8Uo($nf{rGFYw-7*5oRzf()?`5Sv;~4ehzPAh*X=@JT^XO60_c)Qi25Jc z|31HiEUeiFQhxU=QMY$?oVZCr=x7flt#)_u9zVQ)|307_2W;hJWMpiK15=29^HeR` z)HUcmMLKy5Ej&n(_D7@0{Bh0VX4{yu5b-;WOw^< z!-CBQcSLZ9fjyvzZ)f>Z|F?dW`tkiq?fm9uT@WCEWbEzj0X7VKQUW3(cTm{c)g|ix z8WR+kf6~3CdiJc#r2G^Pzp7Is%(CK~1JtXG_$bih2acBzswnAafPIADMedK;^ox3b zZ<`6^F0P>_(i%C}%IWu;~O3-yL zdBVb(o7AS-^V`Bty90ixee&2Ah+v%R2mE<{2Os=FyR^8-Kt|+lQq$Ji#RUum!Lw;Y z^q>#Hpp+SmU~W>`9LIxshWk}KBrE*n&?ss#KqTV37R#Idj@#$@9N5RDbGaFla%Wdp z@cYFrr^|~A#7B<+bs7~=SXpsT-MF~D^@;4J#ts2COY}VD&$U|?kiesoe2Pgb;Bf-p zjnK`OobS!HZoL*${emF(EMb}b)h2RD&1D-;;+z5J&Rjf+Pq@vlGNOlUJu4UNl3D{j zZu>N=a**x^DeKP=6lABmd7_mrJT1OZeK7xF7|%W4R^39%RZb^%Rz+jPW6*~VMk1?btH*D%w9j77sLv@s}#g-xKjbH zCWX9MV|8HcLBIo4q65kgOw891*_^R@fk|sG)jyBX&h@pQbAkgmDe!Utu?lH77$4v( zF7*PcZ!#HLVDl=om@$P6J)86k@1X8;XXY{ZxYg)_>##}&5XiP2(A8L!olhTq9|q zq2_qO0sdruJHZr#fTpsD^72psgC{$3pJwe(joeCZ=c8Zw{kA^ccBI|9Lk6Xrr9( z)Pc9bWa|Xfd^VhTi=%YHvo}M7gEbf=;dCaZkSsxn>);ze!!zG(Qn+!;p+Qv>bPR++pvh`l{z2rBUx>Sr*Z1}s%n_o%L?F=yJr)oUc%Tk+foc!6Q2)HaWeZHbg)A_$ zT6oJW{|{+z85P&oZHvZ9f(4f#p>QX-vl9vl?k>UI-9v&CAh<(-;O_1eF2UX1-R;i3 z$^Oo{uiew$eeKncw9pdPsx{Y?G5YAe52#`qebdreWRwG4=fF$yxX^g6YJ2|C@@N!P z6qbF{m;DsN$mEdM0~$&FY$Hewe#E#hBWdJCb;u->xSdMWl77vy?HJ}GBIc9ZW9osm zoS~1oyR#%&X2)p52nyP|x-_&j<}Wqs$UI)CPkw}&I?7dr9OVW&CehOYjp$l0NZ8}z zfueD(HgnOlPE$HNym_55aXE?nK8cBPOV9llVbb=W*y%HygxYSLXIIy;P1cn8 z4ma6hzLpptHy1PT^J5n!@9;uHD|fp8I(9XSSY8gRu8}e07cf7=pdFvGyp!12-`50# z4R#@ge3ezRSaz!=G)Zo&(q%u`647-Dcmc=j1Mz)pz#AID$ua$W)9c~8KAMNq|J%fFRc8Y|=mg zGT4Xox7Qubex8qn#7l1yxcGU(DZGC6Bvjbz-+-o}F*Me*9AMNaIB45C@_^;GLxJGfcVen1s`7{P;EK`PozqQ;Qxz6Lgw`0L~K$QWw0y* zS_NQ+1rZSw!<%U}WR?KA-+~@6t_MCGz>x}{8by%kpWqx51B2za^-3p7x(r8E$e>|_ zFbRJHEr9kxgEfZ&sVZHNX#j(VVwbPrn z&IT&tN!+o1;7(?h(>w$r;NS(mfx5c7Qc}M=8~%{auxowVp3FHmn^z+Hqu154B1>cy z!C^M3d+JSsc1>48d{-zaJm8S!?{^1n^yVn#*o3JfLFLSun*bb6Zh2ZRE?g*o?{Z1N zi3S}L)92(5=SuL|6PhozAHm}R(*55>Vpp16E+?VFWZ|Hvum<>3>Ga<-6C0<~XMuUXCa1k3e!b zV_n|n6PdK%KhxG*S?z-}!5!OWH@X>X7JagemYS@>dj9yb)19%P-r~+FpWEg->=iL5 zO;ZpqfSy5X@E3d#O9l`eIC`AFC5aRvMg63t6!X8wOZ)_~f5P_zx9_%J^tcqP*qMo& zkD3c?0y~72hGjJsi_+;`VEl!!7oqts`%za|8e8=0$*jj2g#9lVoSoJMgD#4mczyvq zq{G36lYgHJd-2isND^^71O8+KO|queJWHr74D<8U@5*TQ`&?<;o>p7VZIE4d;sq>V)vga+Hl zTX<9ybhVTo?RdH0b;qgJUV2qihqc0>=n&;PNWMYhW`eh{1dmX>;@j`Z?%Qulk9zfT zqQk!VWvv??Zw;;0&!3$BVBplc5*;8V7gbL#aB3986@^m`=`x6JEI{BgRV&YS+OI@< zPd%WK;mphN!|y^?NTJ?v>M)3(^}RLB2EK&2*d6W9Hn?ylXdgRJjql>TPAlg{DyG>M z$I3QYpU-nz3vd7GSmS>ETRc`TlLNjLYN*H2ZTLmT*ta<(%?j48Og+JxvnY2N!B?yC z(Qc=U%Z1{b8nx-Cd6oRzMk*22xAmmc(ch;{F0>23UC_UMT`@77>~gZ;+eqcVt8u!v zsG~cXH(xKFLvw#z(!R7(lrif)O$#j`Ywa-U8+^+~e{Fhtk~CI89T=PpDl8YrH+JIk z;+Y|nY9xujlw;4nBy_r-cq<0+z!To=w0g`nKu$VqLdM+rp^4jb+?K4T%>%q@XQOLy z>#@j%`NfVMMw~m2H|0Y2M>8je$Aeg7%)_n7ZfA=nyFFid)fE->$Wrf?eQ9NnOW(2? z@X_z9s5>JJSUr^U9zCi;A;$fT&r{OJO6igjWBBpxV}+5>HYEh&zJH(KuNISlzbTpe zYEl$6@-Y;;#KW~fquqr3EmqKjC^ zhL}8>-WtVnI!V<1#-+fyUm=Z)h_zF(v2>@mB=t6d@|Alx6JdUSz_@+xeqTd z(R86&U3_%vx64j4kW_!8(rAfrzx17_$EzcH99&_9Jbxxp6Y|*00&Sb!S*x9@^!v$l zG6-aAzkHgHJ;&8uWnP)-x%V@T9a2IED+8LrdYrD4rTc`n)u^1blt6bhDAYCD$jsF{JAEhDR8)p}~RFfXM`*RLH?DAKi}xMtnWnUD!Tfx2xJ`S79;0nd$kN34C) z#?Io?TxxN_cm`{cjc1~K9!C5ZMMZ(9Co%ZwsMz+!%6J?CVk?{+{^oSwS|j+8x$JVFej?mn^5?s z(CF4ydq32u*uwkG;HKRbeLvk5%m7*P4O(LUQT1L_1W^u;Z%nTT!G| zb-E|H+oh*kO{LM_#}jGsne5KO>>sE|tP^=|07~vIg0GTbs&9If@zms;VlIZSV7-Ta%zV2q zUQ&zSG96LQYO2OiqiOf1AwpI(U*boy{rMZ$`Ir?0ut2*p=axHlu-2hF_DfOal-rFg`Z!n zy0+lvrQlHd+>P$Yb2VJ=)ls`3WPouKXpY8&uG7>`LpX`V@MEK9NWEO6Z(INt&o#a92CR5bU_3qEiL_&7ND{?VY9eF zg8op(mFc0eF-){?*f6;7g3ElpajV-@58K9Pxr^&)%`4p(V?UId-qKI(o)N*A+1Q3x zUyZ&rUqX767Wdl%n#}np*5RW8$l1D6?QFjl9*RF~34l31S~r5th2bxY9_?YKEo$OD zToya=zo938qiq0#7A6Vn(gni*=q}v`+I@v5G|@dQOLk2YU-z4o{u4v{=&{BRByt)2 z1FcT)5*RwL<8cqz;DMA5Q1aZA7HRvNj?OzqXb&)YQmpG6$|^3sB8kbktv+~4M00&R zwUg&1+qe$kz9smy#B%lSkGa?v>PgcI0Q`V71fa+uz6D?0ANmmr>~FXSj=2G(wSVXc zsKQcGKMDh&NY8cPEBDW110BdW*>-3WQTl(RmmBN3+w+c^_XO44!T3IZJXYAMtR}g) z%IAIZObH6js9d9Gzd9;Lq|(HCz|Iz2%qyn>5R|WA;Y=DTykgDK%J9VVXJKI>;N@3V zj;~suo}DFeIY>}NUKyK%7!q5SGxzTdzmT>o__RS^r!u&v-9sX z(nDsW{T-Y&_ zANU53P!72*%2k|_$RVIu94Vq4Rd2cY732f(@HW+IcpZn_`?j34n4(Z96nIlgZq-FT z+K#tqz`5zMKKiPZ^oxSB-CgREs?U6RNjJjg`Wjr4f+J22ZQB#UflH=n8C?P?w)bDuicP;of4&hJWzaRQt z-Q-_i*XGO&B|D|sDcYZ*AwXFZ27gl0811~%hRAvQgw?UP%Y}u>r+pT;Bh&k&6I&t} zo@r6W@Et=L-XEH7%NkYD#)4C4qNWEn_5uUPl%{q zR0-#RQmpofNm(RWFgrX}sJx7FYVg8$pSq9y@Ir0H-5`bEEm?MY zDx-S2AH_$#an;4)-s7P~XR;Z}n+gT7N-F9sc76<~L;a?J@gZX?P|pf8U>Li*j51F; zx7S3PiXA=_h8ilX{hI=3_&Q|3VlWmVXZlq30h-3<+OfZ0a4SrZFaVkXfG(=hVSyP8 zUJMY4|APqH*ShsSQvypLRBal%=|7+N1sn;qwW1)3(U!>3Cd!t@lP|G5-Syu(L#(u& zjoDEK@21?sOUHY){QdmZ6OzmHb@*Kdjwk3886?WO0}!`bU_m2l zb+(IAFk;SvbAL@s>clCbI-DB(t{@$nKh}n0M_Xg;J!dHOUM)>S)@(;ATSepqpChuJ z`*~{i^P8k~xJ<=4IF#VGZ00Rq zueb-!PfjfH)_JxM9bW|R*Qv&OuEwuC`SL&EDd&Yhl(=1w+K?~lt!5m2=kb-zh59OE zlbJ4(q4UN(cg-tXHma&K@8dtct2<+9-{NG=mBIf2MGuwD8Z$Ol5#~ov=@!xQ>y%S8 zlinVb_)^k;C_e@L_m3OrEE-= z+b+Bn5$Ld!m21u%TV|Xd1t1`8T=9TB8dhp@*e`|LQR74jj1TD2GMQPqluU9J*g(_`QDbiX z<-_46>|%cn)h?J=TgF;0BMy8p^%Nu&M1;h}_4LvhMPaCliZNlS`Na&c?Sd<$V2n^G z3P&U@?Pv{6db&h1Mzs8x)up;GnLi#%+R!*oJt$$A*ou$|OdA?Aqvt5e>S>9HXc3P~ zC!%GJja!)}sH6>3_y#g^;4*(hemE9EZQ}5U+axG?fXc$(=pZOgfy#)dz2{#GZk#ES z@0z|Ad&eGk2j@VX>UmxIb*o*ix3tx1!!?U-I(@cH?X8UT<5_tCiq7I8l-FRpijX=1 zV59;TBY>1#$~W84(NgGs{Z`AWpP50HUbq+?y)}%zWL$291M}Uxaj9i_5fsv1b)jW#uX&ED7u79embLi~vXw=bn}L3+7x-*>gsP%nYW4$EO;k@gtunGShLZ*_ z^kaHJ7E?qlJEUX;hl)#%iu66DG9|6Edg8~#7OK&||E8`M6-R2EoxLqgNkiG&BLlM= zVYatylNQpGP*Y&Z?H=4ftf8q$Pygn}E-S8_=~~ld_(<4&x}l2;6*5pbC8IC+uH)z5lgq4I zj6&%wpCpJQgutICW6`ONVa2JZp`j-7O!Su&6-GNI8VY|KBdtp0kZ#J$$cB>NwQ7Ix zu{uyTv$SOBumUj=l;q7zP}ozBB26tQ*pIq+_Uu^^4K-$0AfcrFgBv0g`fEf^!`%fQC5EC%B<_8|&+t;sT zVlfQ5#fyoy!;hob>H`2H0TI>|GYH55^(iJx|8fC<`tE6GQEJq_>6sm3+7Ad_f0i`N z3V|OzG?l)~|G-Y*Vfk9t3jaS!{|7#NbkRMv)(jLBKU+=YLOr3d_w|3dQ4c`-$4FyUUjU_@AMjr{i1 zoU{qls^=(>_EZ3I;Nf%A^zzvgAFVp%BV033lHZ+gyr04k3k&lp`_DBWIUs=I@~r1m zI7~V<$6a2~l0J^dKSDuqVIzH!u03`u>T_XSa*qfgZY>r4T(y7z)Z@ai8Sf zS|ikHe=YH9x5sw~F0Rp6CqCJA@@A!nXdmNtJX_T4F_Emn{ZZ_^J}l_EAx$%*w8n7L zWjTP?b@y?{9rE~iu#3p`@xiu+?c%U}HEb-pn8#(l8JC3Qsh!Uy@2!3OHa_+UVGuT+p9z(Y-CGZxvmq5i|a zl9r+9jAjZdDla*DO?x^B3GFm29k~f?Y?Q4IJNneYziG_DOAf$WxUVZxBa5jsl;<9P zQO2!Y*u+Xo!2hVrE2VIgIkhIsx)}kkU2}kCwTwq@5;#v`a?}2CsI$#C$(;zqFW&qf zZtIdmEp5lOfsG`oe5-uR6BlRn{3>@>{ap8>7eV6au@}G51e{9wk)Eh9?DN^y5pv0 zUfIDvQl~YU3OZG-(p??hJS9CVXENNzS*w18M>6^8E;T=0r zaNAm#nQ$`=ZtFZ_VBgSiMc=Us+8WFrm3>*`yS)pNtgXJq*dO7Naj#sI^tiC2N#wT9 z>)5S-<)quy*q`)~S^BncpdWd=fcow>xdA~~`L5QWbhYEY-Tm%Il$OUH@zL!=F$Tb; z1t=3w11WYgkJ0AXT$NomV(RYVzZk_;H=A3y3#{y!T^?l(fAUbhu5Cm?2|eEEim5K0 znCDzo?zYOj=>#nvd@pxR*Qfp?>z3{48O?Rh>wX$9JZHHQaKj9JAA)Z10wti2eoO#sHien8STO|d+>83 zeQ2)x(5Oz<_X;RG1%>xRWQz2|Gkih9TIR0S!*fsZIiMto)eKB5%1~XCLyghFnVbCK zq0>9UIDfw<8yAPiv5Irr=r8!b8GHs6oHv7@LRk!K{fK5?jPp{>!>I)h8lKqw;q)*B ze9x~BvmkA%kr6(+F^V5!kv9)>NBdC(;aDjNM}ky4pStv9gtqDvXUjp6M89UaOQs)3 zxQ+!}3XSh(&&&x7zt$HKAnFMiCm-22GUmKlJ+pI(YoK&D0Mt2k*)>Ttb-cjDFa{HM zYvJ)qdei9({n3b}9ZD#4Xe2WJ-Y#lm=)gDLF99k3n=lk9Gu)3(gRUm8;7jMu_L%2HvdJDGPE&QrQ8Ng&0GlSHBpVpPVk^a}9mm#fsn zspQ-LEdKfPuflPc5YS`CO$zgS*(X^YSM@)K!}r(ZA(fXkUhzy|TL-Ml?rXFsbG(id zs0_!&)p>www->f@=txq`l=%!VukgxoueI#9&jFd-aNdRPvZ4BXkg)N>r~Y2i#=v=k zma8GSN_2zgvxmwM-gUzl$Ii4Orx=kj=4aX05` zVD`L`Th=LAcOY8cgX>)=Tis>Hi%W|+by{+Ws_dx8-l-&mnMv6v)QH#iBF3hF8I88z zBWSP}+gA^XUVg^fU}ztO_{ z>o248jC;vqim|%f9F}*i9{MrsS-yTZ^|$nQi?2k5V}z&g&YI_9F4a}XZmOxp@2}jr z3hD%!&aXUYkpP7SdeQ<{ZNom?de7~#=8a|^WBWb9fLj>8 z9ojn8w^{S6&9qqG`P+R(kJ2C#wEgQ$1MB6G`*ThsvG!+`)8f%*Bi6OED}>3lCz;|F zH}|S{$4v?I+?Ug7+w-#-L}6_{v6c0=`ig&)@2eR>{S%mdVtLvmF0+!(G1q^ zSlP(f7rIryx0E}8PKADRuE%{Q4y~vy#S$A1)Gl)m(R-76%ktp?w?*YNATmS#X)@i( z@O_@*!cOLYhCXY>$V#Vo9rD`Z&ph^dQl{C2&NIpPkL^g)G(b5PaDuqFxC&=!xw&0I zITp9LmM6*X8Jwx4^HZB%KMoaH+-%C)DGhz zbYh^|xnBw-YJ^RjRsa-H^`BzycESG+YZ6<}6BU0EBQr5E0bhzgz{uP@qX#V~bn+>D zd)eTRUoH4w2$>(QhtOlnn#|?UZ(Poa7u+0JfQJM(aE#Z%k=PHYrGyIXGNLbq!Z+`a z_QpJ#sVd)Mp`vQD#ur+RxiADoy|F&^wqdMF+fZZh++t!wr zg2BHPoq#BnW`O1(YPPx86(}<;LakyGs9nh3mzZU?(M<*s8671HNb}38&IyPbuRt7j zQkoB9(0|#dnmxZ3cGFRQ_0nAOi=G;X3yv9Qkv^XvS-W`}9^1p(4kW&D39 z^k!;)={HM9d_P(KJU_OFZ3HXg*L!H0Wc)9lP~kqrF~ar!tCHra(Ku;}g6^4E4erc} z7zNBxm0H}_jKYmJ(wP-mRVjhw{UXJeu(ttgMQ!+0^eWX>$&tjbCC8&M4>@PkQBLI0 zbK~oD?x~qv&)o~V{*M4FTkpzy&*X*fc9b`TvuE?@i{w>2X@4jaW^KoHqsFU%B5*P4B9;6<)`2-SB3a5)BYsgZ4qZ*~h+BUT@69KBiRJ`%-(%G~7HX zQ;67;q?y2uj4BgOsJ2?~E;HR{Y+38)K)hz{U~Jnp%+KzeQx5AI*&~4j^cDWz52Q?z zEoS1Y(#YNIr8Dacf*8;L2vw+aF%qzk2mQHixy}uIZ1Ue?t`{2R(~imSo&^7u(BK}- zz7_ZUGR$MZ<9Zs|`R2*(6CLhUUXSg&y8^1p6N4nf*~mmV0Wp9VqzGyn5J6Z4{sA)k z4Lhls%PgUIxirk!M@!hEI{t#HW7zU}WiEniKaRzb!$W^aCO!(F~N-I2M)*!njJ+K}(*SA{>XjzYi^$JS!~9B?LV zFRJsQPavKhk{hq;2AJ$d%L|uq%Q;B%QVK}DNk!8X5@oRsXo@%N@Hqt2$i9k2de_uM z)v;HoXyT{j=E@bfjjq5jaKCdnLl~_nM_HUubhGO$tPFD` znP5sU<%m9|;fByw>k9tE)+^|vL_<(QUpu|a3uu5qPe89$1z62|zd>K_I!W>q|M(wm0s6Gw`I$lN_#e<_EH$LOGBY$9_N3l z%9#t;6r2~)Ci?jev1xF}gf>KUSvM=$Hy#Hp1tnk5;4=r7oi~#aD{2 zTg;R`NWtjJJ|aP1J00K7|9N>c3Se_&XOCH~YG~kvpuT(t;8U6&RB6R=2T@nRM^}o% z2!u7{pt*d0JmZb6r=hGq_k&DaFKw-a%6v_fcQ6{yE^ZPIx;Y`f+uNuQ3*eDC%n+f-(T1K7)z*a-Zh0WiTZ>51V?21SwEri==BW?J z5Fe%a$YlQ~A9_@2s2lyJM#9ZV?R01?nWu*GZm?NKiYOg#CAG>U zJMdJwDgT~JN%POqm{r%a^3$BPwudd#Pa<*G3LSQBK@X*usrYS%E0Hw1c@LDfOtyaF zx7xwmDA`Ah73S`9uA;-psWnGQ+C^RXSavj2=iSCAzkd&MyQ@y_g~usIe*0%CEbP6v zempjKMwWS#uG{sK?2x;!_1Mo(P1EdZ!@KpoLH_19_gPCY8y}StR+Sr|Nq7t|HVlmu z#$&ax;p8x@2J;*om5pm8nY(81U-rSTg>K8HX0!%}jT6WRK$m!uxvd#G4?5~r-i&91 zA4;-~EVf#lS}{=h_&Bf92ad<>=tYu#iTw}~B{x!#fIzzYT{rLN=;LE=qkX^i?@iwA zV5I3a)-_d?X=JG1+%RuVF5~ygXfYfi8?f~wZ*fdT+;6SRmzH265BHspMujL4%GZ_O zrRaw09mnB&tA4B;Fx9YAYB;RVi+xGTRf$sY<6|Md^v6H2(rTQ3|wELW>JyxVMQ3KW?Ira_g`662*g^de+E%FVMu5uGI8P znEWLCVQWm&XRhL1;f#uOU3Fu=aF*m^ak$I>F6+;7>`rxkw|PN^ zmOgshJa6Bi3!cfccFeKOCp%@wW?C84QhfK)-h}-Y7K64vm(kLS&amaEalmf4Sg@q` zekL9%J78%vOiC9f*{#7y>GCSH^-g{mi@x!8aO#JUrU>@Ps-O_O%FWET8_eZe3j#XZ zfQYVA&0BHT8wR)Q{wb)|ktm^lrIyq>dIH z$y-~<;j+Wx-}kOYimsntSzWFfg+fwg#Y1i?wyAgq^4-=7+k1t2Mn6EIkWa2tGaU|Y z2hD755pNui(+)dops@oybrEU?jT{yB;;oJQv2q0sC8|Hgw%DgvQz`GD(0M#{7j_DI z<<5?Nw_Nrh(|Yk~d%o*4Pp5eKF1Ev{$AU4xEp-2j6hrYpkz$CkKKr9nM-_#bUO7hU zm}^*P_39f9<0ml2;`z&f$_ zH@-RV&;G5VXYL3$^V`v_J6*(XwiG&(q$wwfUI4!%8BPiJ5uC7vuGd;0p6uTtY%g zDA2`zbHNU32eK37I#UaakbiWt)jFgt6ky*Wt|#))6kv6ceTskOwq-r_`=I-~DxF>g%88pml6wfr zp)vk{2_BbE6 z4~{Xqzv&pl^02?m^w=#e+dlj)P>UT^eG(^qvjcMwNtR)Rw#D9EjqQ)s-z$s437O6e zBspIq)6>bRH4;3$iz>bU4}2>tWWcu)ojAEMRhNb}L^7>0K-)$YBZgClnA=MVZPN?N zCPna`XJTS(m+Hj2O_A*SLygUg=q9R|DyxN6N4vr!S2m}#-Sp*5tO^`nV2-P#(^8pl ztoVIJi{1`AUN0(t?hh+_S9R<)I;DrGsis!rio}2F?SFaWQxRm;aQD*Zfc-gAxaK?& z(PWof^K9`PtvHdqar%`GT{V3=YL#sgPwbstji5@LKLasK@7a{Z29u@F=^+UC$F5s|LP}{hPH9dH71XiUp4ISpRKg3@NPR4&u@*q%@nM= zk3L9_M$Db$aTyZ4GQ9RzU%b7{FqurQ!)MFa^02b5x=K2Hcw0?6pRqI2)4jC-TclVC z(3og2E6d9P9_$a*E>{sJ1D&Wa0|hdNPxt5N2L}%u+<0FJe{P2NCd6d(Efk?uyH zgoXDsT19Vss3Q!~%A`E7^!0k@Ma@@7y%}qxW;)(jDiuKtf~lns<=Mk6VIDE{nxq8f zXBSr1PCkWo4yL#BlkM87d>!t=4Xox|wX4xcX3m+GU;7Mp6CbDZuB<7p2~1;Z!!4Rubnn-ori3fnTh49S;T)QHwyoXVJ!JvrgEWVSM1GdaW>??7S5bGCJ)@IBI+OZ%T7IRpr6?B~zBwTlhQ zRq_?6BSnBRB@hON5~^D|2^7nNhR`SQ3!WE!!N)-}4r*B0zX)aEh1$)2NE=zFR4j?9 zt++!VjPGePMDP*Gj^Ghbm(fByDD*xL9d&MQhK7a?N97@gqeM<{<xZ zT}ZKm<4N7qS}Z47aUT_|GDETRx(J68ZVL$Huzu+wxB1f1%pi8Y6M6gYBWSYf;Nak+ zb>dtP#PiX>F`!^ObAOdRvUh62)OGBA)9})J9`hCYY(C}@0+IX}bO)Vl>dl>zmiS=8 zdGq<9#4Y8?(0{oA4mO#jjaB>xuVtet;52y~>{TX~mTPKH`)qGfHDk~}@dfrwvo1eZ zHQ-X7=2E4zMN$(%%7ZgDmf26jAdo!7`BbmxwI?&xGfxE`bnbxAL~_%XbGE$hmnCnb z$4;Fgc!oQ-xg!^$KV^J3FPJtWHp3T~Hjqzl+t%#)qS`mQcV97h${#Osc{ttfs@^{z zki2Io_1vs|Y;MpuU&p0CUkq^ek)|SI(7**4AlrL3lLm*UD;2Mh^xpo2YI|3W0a_ zwXrz#7qzgs#Y@E6p7-u#>iqe8v7~YPu;wW!E1U^`jX?Z|TSwh7Ua8W!r^oHRQ@e>_ z4xi_WxX?a2&;vd}xD5|aBw7zb_k^`%Y5G%_(db@(}bhDAmY9*6ZWDz}5F5g4&~?mnAK zevY2m3h1FLrN>CC$#p%Z-Gq`WJ1%lQ@Cx+i(OsGHnY<2{ci@=c>z(TC95qOq5OqdC z8q?2Z=-s;K!#~=Qh{i5qo^9+a|L!#Ssf?A2p0aNGhC)krsJ4=p?K)0oGpY$|=>O{fd^yuTGt+zlmQ;cQu zFmNpA8t(bwKI$K4*~w#I_ONPQyIwS5T9KQZt3;!qay%rTkHc9=$NJYHZlO$;l9?QG`Y#pVXK(uR8?)QkHmeb{BzS?{9y4=&7lsa)4 zy|s}@es~zz-ZsAB*Vfu%u-h;3dVQXB>iXo*%0~7A zlH#hW7_p4AGkb`&rRC?+N$uhozp^P+QCD_6Agx?GbR3}SC$IFcm6w%^DqN-VVMkT& zak#6>FZcRXQ{UIq9rUdj4ob_&Xnao1C+y++~p!qv_N zzOu@vOYcOrjVLy`KXG@A2+`6)j@h# z!|+wh{4eq@jWv=RI3At|G^WH)k=j^W17dj<6%}v_Pns6w=aWLf^J#Ky?Cj8l3MV8c zCTsn0i*H(@x_?tUk_g3Yf|TYCl!HVHpQYDk)g{p)<+RJy*k)xBzc z>DnNXse=uSFi=nc6lz@!3Ee0k*Ff`6I<#MMi%--#RD)^AK#Uf~LX-oe2-Rus>~gvj zJ61n=1?OPw7a$iQcNmu8qf%m^s9P|j0vpOW)oq$5-V>qgB6fE9&@*orv!K2eniWPB z{AD(06KiZ7l|ZCb*NfH3!`hFvg1aiq6yQ&6jwQVjWl%ySMBB3LS{C;>K0eVHrRB8%V@aRkwT@I>c5z*xI*H7 zV3y`hdW2VIeQL-d?LPk3WB27@iT+C+;BLLBs(_6qQ`-K^_MpvN8a|^ydWQBXY>$?N z73SS|y*j`&N3H9(je%FC*ZfZRPY(R(Q4+0hL2g+Y^WRp(0GXaO$r|1IyIa~UAXx%y zn5|Q1XSK1LJ{Pu6k+C+jgtgR;Q$T8?roCZP(?gKnHoqs*w*?gWhI6)jUG$d2Q!|#pX{aEAGzzXsoE$v^) zj&x)1|0O&=n(0gHZ7Na8cVj%cz&LI{7Z5r)IURKPcfl^)5m>zdn>?f8y!>;-uUOB{*bc|JeL!DdEZ=i|i!DtkdeL0-q(za3ICB@#a( zJ?#4$dLdy=z>i1foEUi#Lrz)wGLQd@?^)c$C3qSrRM8J4`vgTL;o-x;9!Nu0_LzkD z;rlwy@z_2QB?})N8-sW}W{lkd{~y_R6ge}v!4dqZF1$DLrdM_{;u^!PwR#=htz`scoufxCn6(K*!MQdCtUOEo>JHU<;;3r$~ZY ze>PTHrARHskH;%Z%V)Bt^yX z4y1>nUU2e!hFK?tdnS&oCL_#!jX&Q?&urex*3;UCJXUiSZ`2)dF+{6{%wyi=;Z2uf z@tP0k@HcHTs_&U~iQU9A&-1Kr&&H0^s69tdQ*2jL_FRX)4I0+D-#W|6hEJOAjTaIo z4>;J`l0d?{K%E4{^(y(S@j>M0ClMhzwj}FSf3!%ro`?V6rZv!-+837|zMxqLE zNs|O!9wpzdXy+?KxRZ9UL%c^51o2@K+lCOH6R=sSI(}eCNy2Pe|vQ*Dk;f^3!Jq6N|yniL?x3uWo2a*6%_=5h&Voj zo3>}_(*P9=4-bbz-eV)+A$7F2rj?cso3b(?0F$P>+kFogN5_LpN%qQzWpW_LZTqAr zss0G8_9x9v@M$F_B^4ECvFk;)q}Riu>1hfsQe9`izDjp>oQEvP@?Y)zUO9n=~;jGZ4KG-O{3(T%ER&KcRtOLqXPLAAGkuX(is_uEex3< zo)2r6-d6}{kal-xfl=6cN)vqB^mJ`T#@gg}Gb%M+?E_;7Bq?x&-qz0ehJttS=w#O* zI5QJdK`!D>rn~IBwy?vR<7y{+xkHr9ETat;QKtQOBVo%N z+4E`z6twKaEzpYJ^d0bUxnY6_{+;|dY-W~2yRFAOPl$yM{r&hREG$J~vs=e70!iXp z1H0J8&kbXRrQV&Moy}t*dN!`Dtjvp#%Ol2)`K9I82vk5W|Nc}y0Q-rBV?o{SW_rS@`A|6mEvR} z{np%$RYV~e;Q`k%nXJXSP0pu>LwgYhj8Z9F^nGlsHd;Rz9cfA&rLg0>$2?2*?k>$c zL(x|XYX2k$=IUL@X8j8Hl39oaJfRWy5Py-m!~6Jt3=YaqD>=MLhDw_TP2k}j`lPo- zz}(8~d1@?dZJXUrEwr>|`r|qG97=%aSZ`XgA3R`!`=7*sITvZN{NzndAb9LQmg``| zB}%!+>P`)^@3>!;oXzTa9iMp*xpACr+aV{;tbLrE&O{>E7pG@B^D&_E{E@i7a!uhgTX zo_{UdEY)C+5q9r*-9PVTDMVS)W&S-mIawfizc-r64YpuWD8X&vj@-dYJ@m!tj)~IF~Zq!9-mm|4ka+yAD&fJ^=*d8 z(uwGk)-NaWk$4)Gus+dF6nbUfb9A!W(`TB~CGj=e?cjp-vscE}k#Vw%yuE$TO06QQ zyMy{;DRwG+lVIMkr%+>D##5kz=P1u=MSktQbv0I-fN@2)_!Fl*BXhTLC!S%+Aj4x$E-t@c}&|CME`rSlG%nRe%8;Fm&%1 z8^e;{2(kNn)xE15s;0@g+T}-CZ6IQ=%8$F2uxjgW4G}b(d|$IRc;zBAm?5XQ)td^n z1v5IvaF1N9pRV20XiZwHh#}K$ctRnF%m9nytcZvXosT-dTrEKMH(y>*q~=o`-+uI% zszuO6;w)kGxUT5p!%5mUkfwX)RoF?DD z2rcwFUe=>2F4y}crV+b_GUqAY@V@0RoS{%%vZVP3#-%18UxEZe8%{t-sbIY6eQ8EU zMpadGK>Y-6xd!upl8Wbmb^En@=;?TJ-0KsE=tVB1-eeCMn2Vrp_PssoEc@fYVOvE- zFAgS=&G6IBDZx@jHitT^c{=*3ZgHB&Nr$NJHycd!=)>^qHMZ+}%>kz_zPZQ@8bfuj zQ|;zzAVyr>Y(%c_oMOpwAewCDO8_TiUc_`Q%L^(50`n7I_Cyw-cX1M|`dT~hJ3EcL zEmLV%?}BHU$$3o7$na|1kvwF0nEoO0T;|UedpU7VI!#@;_T4%(Vw@YT%%<{$#P7JhYOZ|q`~pGpIRVY@QvHU{>MUQu zi!(%Kz!XZ4K6T0Kd_jJ+VY{GzhWL;bNOsHuL2IygHGZHlU7X~Wo$x5Wg37_%>1kw@QKgF{C-6TY@rgG??Y7%{Qtuac5M%4U8j_>bz z(VT;Fp0R1{_TEEX9HY$-JkHyqo3Yt9y^jyoO;3_}$Rg6}TaKZL)vTE8NwYUMY}tYf zVZ;G9_x5Uab#!#X>0w^`;pp{yF2=&QfvLds^1;BRlO|9ksbMq)$T!T0XZGE!)zkq1#KWXomwKq_W^f zl~pli(b|)*m%EMDoAPQ-R|FAUG$5CypTQ}`Dgrbo>I?Uryp$MUSF+2QI`lHX-C#{1 zW6XLdt4v${C@@ZviE@kSgMK;goMyCle%O~-R@JXq=3FR);rjG1*vX^T5H2GF z-;(xm+Mkg@kFAT6lZ(SoCcxl&_)o1y7k0dxP4C-HKq=8(1#z234FrJwz>auu$r}dy zm3nZg6PCVPpuX}FU{MtXVWq85chqe%K4w<}m4Ay5=zh;WS5VgB0WT)Pj*&lHv-cZ$ zd8@QMkVBq!uwZ%4msD*3fVC!91-t zx5aUA$XB2Ay9j*;mbbV%LnAmkDnOhPOpm;fIAMGE{%@@lMwG|#^9ICu%BLG8Fx)A7 zuVyFE9~9OIsTf{Mih8eGR_K5 zniW@rW!LdRG!g&VWS62Rh-DrDD@z4x&_zrSl%3y&|KO);T%DbkT6So~Ztk5=a;OKy68eNZJ1s^b%u~`@x zw*Jjo90ZWp7X!W=!2N*-kgqzIq^TA7<5Dd+0cROZ_<;Zui!wG|N>Wfz&@1I^E-tRQ zh6I2AE`UYx`ue&)1t`^o=>cep`jjNFcauaAq(}|>{#|{2eVE?jX=!=+$mC>AcmDi8#4vKl~Mk+`029=b_A_&y6q`Qg?YUoJ{}sUGKP9yuOD``Jm~~{|94B7yo8W zU5a1+4rG9j;Z^>6qGmuJ#8)F9J*q zwJRs;r$K`Dse&oZpI*4sm7>Zytp~D`O~a$Nxt@KO%vz1kP#1R(2$0MOlQGk(wH&$d z-WkPlkvY@z|HU5 zFX>NW5~*IErSQu4VLZ(E&C?FtHXP8K*O*kESSKgraK7n$1Ejw~jn`AN$@Mx^vt%9V zf)#DOw)P=^+1f-41XI$3!X`UQTDu$qa(#v;kQe12HG?j;uxuA@w0S7N>134WHYRb@ z8RH>qd|-LPKfOBOyJG!pY@=kEoL%ybsS(hq_^>*mIa5OtVq_z&0d4A)EZ9KT9tGE= zS7rs~WnIS3oVFifK5sJd6Mad;p|5~G%CQy`fDOr!>}aU$Hm(=g=^6h%+v)BbNJ?T- zv#Yu!%}BYwx+$+mwISP1n6v4Ti)9v-Iw%3JEZ&#I30Q|=uJ z=Q0aG)hrTky}iHlg>Ip}CmwsQw*nh1SMZAGTl=W{a{AEpZP6<#iiJfplQW}sZh5Wz zfms?=lfgZn!~9F>-dHe_)zXkpK4{AhvH9ft(`~kV0ww9-o&Jz_5DYumq_@zV#L~@Wag9nb&MZ%=_2cC0 zQ7CpVRE}~{r}~w)gb|ChaDCpiXJz)LgbV?&}Q;O54S22!VYHicy_Z)m#Fc!vhdVMy6@PU8dXu<(Sv>f}VANkL4D2 zgj|>viI^^9r7xKF9PZ4kpart-4!DDR@%Ypud(yr9G>>O$pe=buewhrWn%QBJHDIxU z_V$!3n-&G2DAHX%8{l5H{&&3JEnnj1dq?RFg5SFLpQ$=JzfI$DVm`$=aaQN|tqA;F1yK${fFP%ZT zyiT<2^{JINC0>tV=RlvF0viWo2J)7Xn;2KuUi&!`*+%=_tt4rm=Ve7UW2Z#>UM?#$ z93RDSmk{GzcyRV#X1JQ|KDjv(P8rH)G`QFx5Nt!*%|dVPp_)nhT!D6SXL&^+hQ-*! zn8~NSP+g2wF(Y&9%12*xY*m(|9)R{Kbg?ygdD=cmcdq`y0<7RGVkkCb$IoTS^GfR` z3-cH1-Cc1hLW5*m0P(B9=|_+91%sQn&XF@NqLyyl}*{Z>wWE#BH~ zIdr}4<7`{2B{z?+kx)UE#r0~UxuGvr!blj{7$BgPM>OPyPMm=HdB8Bq=?2OsVV?nS z#L1d0A}%0zddh6u3G=1Q+WPLec1w|$PF`_wba#`Xs0{-uYN7E6G1=&t0v$Zg)WXo%d>(6tYcA+elbO}cA6EAvj4LZ#W&0iaRMk>yE52fWdw_@MFZL`$yzTYFiWH4~(Pt4=WxFT)D^EBG>|iQEVTSI~eK?Q- z^=gWtEvomGhsqDl!U&Dc3yo}qt0i=eT-$Sr@a~a1DC{?PKV@H!2O}q>qp@0GT8|GC z>BenjiiYC?ALBIeWWpuu(&^_Ht10xNU3C?#0ySQ&lqc22dH`x0A6@1 zKmi&Srd9Op(S6}27QkLMH8m-)z5Bh}x!9aYVo!}?14(TO>9oqqC>!{@(`o$HuAwyf zKCL9TtcZXF1k{Mf^}*v>=vh#}i5UGRO)?A5(|GM;T%E$B<0qnLf2a?ZjK^Ygo{BJj zwixz3p2ebT9+fBW`cXQ>!a0we?PM}(O-M+M8DO)dDOUIdpCCFyZ8ZM7=Zc*O(Ub;c zmXt_&cn-YfUal`t%T-Ze_zmy7bH~L-2v_FOuV5UwXn*I`z&{3=MBQ|Flw^0>kTg*f ze{$2%dGWq)-6a7ARWk;b)$)7CHeHBh?Mnh{-t@b<>0&b!zUJR#^`TF{51ujo}akAXQ6^$ z+xLU*yyDap=yEr4!(RM3i1Z)#$1Fs=LfD7lkTiI${zz*L>nuM~P<~RflCMN35ZRH- z3wDI2f8wo?rm`J*)@9jO+@nYi0(JgilJ3*2O8dFVrkw5@KDs?$3u|CVA`yjTPR7Uk z+y0s4cVIgW_hag$ikuRfM2i7C@!dC6+thr~blEW* z`WBM}ee~}y5&-GwZ`~fkH+`wqMB6981kW{8EIW=V>2!@wzIpwRhw_4r*TKG6qhox- zGQ{P*!Yy@omDegF=zXt7A6a+wh^H$--xAf?f`7cMhkzKs8*1Z#Wn!!rpHWp{o z<(4gZp>G-u~}r(zvyw;3PbX<|=YeiACqp)bewVtww%`&({L2g->La$QCms< z-12+5*A+HKfEQsn=2%CJ7g3?a|4vy&QC@vCSdt%_)EX@(3SCeC@k544lXL~ME|ds> zbL54dH|un-buT<>D}xytLV`f6^J;Ju+j4cfW2S_h%#)WKOXD zS-FkX*ACkBC+W^|oOLqEHc|Bg?)Dn6`|Tv98^H&Mb77qvr#bGVUlpi$!Ji}`p`nk* zqXt}ZcGU*nQ@;fYSaU8+o{JWD4=q@{-eLK&tZPo4RuMboqAPLqzr%$%`k!^096jEf z>7AEakuG|O+T|$D?Gn+cKNIg8_w^GbXjqUfJ9e^9ff;aANWcsora7>AB@Z|1!QJc7 z{{qCYRf)*FXMX?&rex;sBlVAg0T4Z(4F#%7_qK71otU2^NjD5xQf0UEy0=0gL>bN^ z9X_7Ki{CI;?a{`&8B5-y@G+Kr8=VxO-@ zOJp3aJ87m}(A!G@op}9M`3ulWX}7_c+4Ii#69j#_-5>xllDwch_r?TN94kF+XQg0X z_c`~nxB4FG&AkvFuiiTY5$VjK(fvhTXSu=5=jJaIeZ47X%Df#uG<~MQYJMSSZm{np zjF(YYyA!^R`CtzM8B2+FuAcfkq}oI~VS>JO(f?sCVN}(oqhdugfN)W%ni&Q61`ks8 zY9`q$8q%c{R0O;QEnCYxawQ*`+$P0g{(oX$RtANL@$hi|KRRV4Nln_K4CIw`b|b~f zrZv_K0C6v0u&;z{h5$k3QfEVZJyO&Cd+9+DHvon4YzQJJ`e0)(FL+vyGr=O%@#XdSQ5)tf zk$9#xZS_O~XUdds26SPOAh)B<;Xz0dWbQ@Y{wS`qT08|2L0e5hJ0w$s%gI>!Lo3Fj z1+$K8seW(Q8xe?z-7?ba@;Kxii)6*^0fvp{>i~&UUJL^aKey3Xe|la8p(?i#XWfMy zDR+W@R;|x7FYE z(wbNqV+w_g=`>SU^f0~u39C?tWU>kH({EdqeeRx1@Ln>APK7>IoBY=hgj^jFwo1^x zV@devrQNayoe5lhPWY_eI2+|T>(U{sp?o}AerX)qeu{Z8T`%gjnDpzI0%%BMK#KqU zXAvi-+<_$}uDNtN&~dEjd^VrwwHs^>sB-Y1g|&qZZ^IY;B&bO?c;k6KnaJyPHr-i- z_B_~}sa`0Emww`LHtmh}xcST7x9!no}$#oLYpeDATxhy)#R}3I!4XHcb^5%Mw*5F#6myb&tjMpvl%;u;!f7_Ba3W-&8Krn9SLAr>ZkuiRM=O!g6DEgv+!vIU|B0$J__5;#$2NCY4YJI}ExEr8m;wX=}=6QCi=b1UW%CCDW)mGfjka7n4Lu$k|i82yu$op~{bwnTNQe@glqqqQ&O`ixjzp zi@Nax0{p?r`Fc}Vw~1aN6ceXu`gxy%lZRcs4&x?zS#l1){q9$Qa8pJEz!*}Rcq zhGAFFg%M28o(}{>D9vY+ zgAc+_E!s)1ycZa<>rf@9R-I^7$T4v|*J5yQ__A_%zLDr{rz4|rxu68oc;d6@|PpGd$YzrghWYbp|*Ty zr#0)XX{;<&EIx+BO?%!*^4O~DrA-n!ug|Stj|3tknIR-(FA;i$OyC%&^v2?j-q zC~mf$Og4JL4(=8d7B)g30J&tKiesuYZG4*k+*+{5frQ~0n}lp*6;eM+LPAT3kJ4@q zbC@O}nNC5%pSxA)zJ24SRwZ*0l9$3lUs28(=GsKn#@5}D|`jeWnRnT1AQQ?t;sv?Is3OD2JEGQ%;|Ky_sW3yMqhgo(I zXayz4=#T8n|A_6)HUrm4NHa!tTs!-oI*LzhM~)jBzqPD*l2YhEM#edsaRxDIS$05n zieIRjs@D0zv0}>*VdES&hFKoz)kg%bpEO* z*5kxp6N2%Z$N-j;1INu8U7HU9$TdJ3gO`_>QN(p=mT~%hyneu3q#VQ}?b~>owFULc zB72PTUJuqiIGcb;B{=b`7MtxrK7;Ld0zB8SM`SONwC+x`7HSg%*u>3t_P9`c>pXm?NZu-0cJN{ZR3_R z>iY>?DWv#df!XI7*X9|5@ORDT1Iu#Lw-~DOVtEq0+atjjz1kkWYQ^3PwAS#eqb^l}g_{X_(%^E&UVfY7OvsG;f|XEUvAbk_^4u(`5v%uNpn<4Pi2_MQc%34qELkHB5dJf6V)} z$`fSUH!FI^94i8&B}uE7psRDXF;&TQ=})o&b)KTUV!!p}<*``}r;c?wh~vhrwdpRd zg+eT1?aY@h5&hygex?_9zdP3n8#qES_&BeKJ-j+;()^(>n^og= zWoBk${n=67?lF;EvnRM0NM38;)-i^65T9LhA76SHJPwHtkTWuV&U83^ofutsHc-3O z)KFE{qZM7pS@eZhh{niQUuIA0tvYG{Ld=avlJV~(8C#jibxzI;iw?n*2b;J=F|?Fd zdA&bz)b8JyE51LlMx4&Y(B(_+^mB3oFnLy^ZL&}!n}1^Et~V{BptSSecs)&bH|K%l zlT^x|)F8#5Khk3I#qULWi>`8uWgV^H9Y)!G_GZf8q6GzSv_YL`M(>39(Jbi6sQWK^ z-~8BENt3a`UcK?G1(KoBqw{n|6Dl1S0PE4X-@2v&mDs11B@s}1=kD>PQnM^dME|c0 z;;$++lzIAfTVdw!4-S%ifsmRH@u|w(m%bsbvC+@hM|kex=-+BiVlC#E8fQHry8;U1 zC{7!j1)8&{x}o@$FZgggYkH2O$4C6wIlV?Uj^&w4rnvmkjV)wtBLsa*$ti*C_67beNb+H1C;r-ng?`y>VR=Z6bCrU!_=&% z$N`(znt|O$uN_Oq_2aYD&!#n-A(dkb3|(H-pFmgeZ4bfBR}Ke4hN!FYFa-AKo05 zz5tC_7H8&cbXdPPrt5{(kDD_KCc69(w+6Dzb`FzHuk|1?6XL-yx=+O)q^P`DSs1SR z=48mp%!=pqw8}D}N7v0kB($J{-rq@r3^e$OTi-W))0rzK4?I8m+T*JB@-JAwh54Rc ziKT6&j+{mJ^9+OiFJLQKAoT+qj8Bqw9*;36KALLG%d5btDGi;HtQ`fUpEFCmqRVOm* zki0Y`@XL#`CZWgTV3g#0{aT#AHLxTSg=xtoPY#Womwc~TFsS-m<1KaP)#B9Cg)S+t z>=ju%yX3hnZK3NHT^wp;$OI>imDy=oVM$HBi(CDpysWey(iJLULo2n3y22CultgAB zp4P}IHL!V4{7M_W@!M#xl*Xeztx-NG)OBiVr;Q%>cHIaAZhXLC>>JWO^KP>J=<)IH zO;^u(ssQ!(C#ga)deW@-d<+e#g>ok&2?8&Uwx%8;4jtBAyJR$lh~bK5y?FAx8*+6n zSY%in@->IX%VmlpR-*%~Qz>|y(G!JVBIAkFn#S1NCRC_+W?8IUI+hzLw1b6(baism zq)bmA1&2-;r*uzgFE8^>`HYXb|A}ZkGvN3dvMu3;N)KKMwi4-pKoOHaE=Mu;7Cu%b z)YF!7nS!Owl?uI~vEcA-H>qft>1}F8K!Djos>J!s zfyq61aRTMbiUlM|K_s(*S`BPCQ1aPn1JT))5zg7FY1CxG+N}qCnA3~5-ksZ0x5KzV z!S}lQG_0l*wBjD-g*u~~m$Aidr8kICfY{Pnh1*q_z~-I$sLmU};SGu_A_hB+c*+gx z&-xbi7`#!{XEm^ScH!RN!+xdK+_V!wjGw!vQY6jT%h*snT&)%zO}}6G;jYmeNc)F~ zj@;qbAuWaUF%93ujI5ajVGXrD{SY0t==9a8lo9)TipSB`7+hBv&#kYc{z{-0(V9Myu*j9?tZfn6DQjdl zRrS(Syq8FL`?h-TAip2sW+2mk7cX|be_fi;>rKK)`y9dzBo0_ls!;;0nZSUqm!8SR z!ygw1{Fb6S&M>i$a|M+boJ}M0c61)tTDjQHmfid9C#3%$J`WqYIWLLv^|LMNk-6BK zRb9({37))x}@0IJ#HYB5grH&ht|P10;T?TJ0*z1t2c3)d$ zB7KV1jR2j|8)W-Sr00~w$TI*&eY1EwgbsW%5CgxcxDE{a{cMM7{&(1$W+&$N*4BWs ze2E_V&S)kjp|X==7um{k2Q6r1(?p(cWxk3PX>*d`7W$46YD|D|Wm@myfcTr*g?gbQEM;U=t~uH@5S z=Yx895lvfbNtqQL4?zx=x1WPmy%yed{}Pc0y3_c7IHR(I+3|aN0C1bo%@6+tnE9zI zoOS=%=-iBdL(`wH{4B^&AkYBv2xKGl@ab3HrklRF90cWLHZ(Aw&|?Y~@s6UvLWe zG8jxEP$>f#2|i_ZyM?JY z`+u!#fCTJ}if&TF?p@aaPt)I!iepgkAR~>!*oxmR4&Syv!Z7|1+Uf5S1niEC04rO; zN?r79qD{QZ>e}!wx74cXfR|4|fYP;w#bQkj{N^G$qC8SUO$fD& zbso;MPP_l=vMY!n-g2Tmp8keOZ{`v?lxbtgODP}@{`e1OK&c2qD_BBl@dA;9`2}e6A32+B{ zj_?^$egDcgRh5m zBaIj|CU|H}{v|$z`uUG7fopVv768$-;@VZfMBvWyux^w24<`-rcjxos=w%g6E zDea@9&L^~_@z6!!XVDuRRFsh1+}t=okyuWBz}+z+~OL}Yoj+avM}hHUX}ufgl>CDG4}*~NjspWc<}I5wqf3T(d+KP zGo!k~Lomx5=^Yo(BNao@Yg|EiuTrG=p0jr*4=fSi+sIxxp zwsf(qb$hJ9MBMDD5yQ=%RHE3Y*rnfe$uoAg_t~@HtbNWyx%~_YCiOl(Xe*Gu_J2!Q zcv|w2Qhty$l?03XtAP_oM6-n@OV!uD=d-m125pmXunx7+N_FTtj->{I4}WhX z;lCl7kG)_uzOO&bRmhl@c)BmxvjIfL@m3Omz%__g(IHxt9e@Xqci^YXn(B6@VkF_Ex+Zi3ojb^G!#Bk zn0G3K9;wett0gnNd2hkI@&5F#5$Lz^cq%pGR0&8B^!Lkx5;%;djc~(O>Er~Ct3Hj7 zj{_WBp`o#_<#v=FpigPp8ca?!Ug0UO7B|D^(yqIhl*M%q5<1%?p3rBSJfV_?sPqlE zuXVpg*fdW60AL1#>AgNlZoXb;JKh*TAQ^u(^+ml7*Iz#dUDAM4D(%d7nwnCk z^eV7w-vf8P0qZ%fcSi#?5XY77MDG$ZLDzo2Q6`YZ+%ep5!V{%{@0Z{#*nB++p>+@3 zCW63S=!lA^2`DCam>*Gd$ zD@&Jq{f5lu3V5GD?641fdsQK--mfbXo?E_{ zSQM@~?S8;2M1>Y?7AXh_Pi?6CXc-RQN`((-UWZdIoQbJCgM;Cm{Mz2$dcXPzZ1e-R zFB^3ZT|49T3mH)z^EnJWVFNwY1I-t_L!mIwjf&fFxpS(En9NoBaPQT?{$LZm*Y6yS zKu)vzo>h#9Q=Af0ng@u+0!rTI{HcSH&`p~Iwjdh4mHIa7RLWfC)WkX!?@E~Y&e+gm z^`+B!7PNsz<7T_1o=cICb6>YENjzXBLO}*$ufFeMb<|-y?Zl!uYLm#c-UQA}a+mA1 zhB8rV-Y5>r)JX~Jx_G5O_nI}Bv=l%%8NrW}>wXLte0%rn#Nco5hNL?M0DLUAKr;DY zcLWpel_Qf$eVo$W2aui*mTWrnAOpvAhM?+vJscu8ac;hn27IRG0<|{NL3`sb)A5v#Q}v<*zb2fOXrFy85TJ<; ztJ`DmQ$dm$fBtzZVfCun+sdMy$5M5+dQ#4FL(%PWy_ur+ybKZ0jXL^rI$#(^S(xI{ zRrwYLfVrs_;|vPjT{{BS!^dHz#f*QmfxjR1TWYHIsnzDcP1`WpcDiJ1a?utT-}7oS zU@M?DH|U%Xrj)aT&D-tuCfy(@2i)wxFa)(Q#v1z}%oqs2o6Mp5M{BbB%vzKACG@BE zQ$3*)E)$ig&|+*`92jbbtT=z;DXl#0Jf7ZG2CQ5oN$9m1Q6i>zz1epxolqi!#qFOU zY7Pa%7@)G7Bx!Ca%aS5N4Pq>*k_J>fJ#tXeVy4+z!9+$M@jTxbU@R&M zKT>41vu}@>NWcB`JG-N}#9{g#hk_JGx#s=Hod8)vt<~g)*c%Hysg1EbO-_4z`|{52 z_EoB)Qr^YOnX4o`74`b|+Les&>yv)RBrOdV9iMlN5wW3Z*}{DxY-pC_Dt1f#g=C<_ zto(6)Ik?`!{R~MpFnzfnq8eU4(yg+wbe0@t*wY`iF%-#)d>gOH0m?6lA)SP!MnYOf zN^WaJ&u2>LRHg$V?4xFywdn0DdUY0+C>HI=xMmA$HGI`+r1y-|(gZ5JSVL9Va$I0O zZYX7v(sLG-ukO^XA38s;eqw=f}J?Fah+5bgr$Nh zE%%oet+mO@GByQq07}omR&SM8O9< zzPRfMW=%73h((wf(~E2{S|+gzN)cuen9O}+_^M9tY}4zM4J_%7_FY=G@LA;zEp=U| zQqa?cr-TJ(9w|KTfuf8-BP0q@{V%ZgcWckgoIHEumeU#VL*avkm$1X;UX!){rqdQd zdS49k>zB#}ta zFfo!GXq0f~p`l0hjdi3B80LWi7>5?iTl$J9}DvHcN$vSBBZ3Hw~m1~OJ2hbiAFXO zj&${kPkqco2{H&{%3!BnaEP_}*Siew&mtRK~BGSOURj;08&2>k0@#&3Ujyrew- zG)#9K$}E{tLLRJ-C44Ji9R&!ZFgSY}8as4g5CvhNPjViZLm-3~KTi;HR4Vf=;$tsq zvHZRyzmlI!Z7JymtIr({-nGVhFh|yemF3%xx$B#tyDm|sCB}I^u~IV~QnV;xZl1dAI|xLKf>$mvV*1)2_sPPcq=|uN zT?5FjvoSz-Ntwfx;kNX{=xx8N71p@A)x}alziYgbB8|m{bM&M!TpDxNe$vQ-Q_S8U zWh%|0~4@3TMETZ%n6PT$Pe z*Sh@a0g^VN;#m|Ch@te#-^n6IqF&QPXPc*TDB!=9pjT}cLC0%sj%Wr#Ag6+hV7^z# zJiFw)p{>Ltuck9xBoa>8poY^KmMY@4estNF>dl{(@#?XVU!(3WGOF=*?GrUBeeeSgV^>wQ`m>+rV@BEb`Jaab5}w{u5s=EwhyJK&Usz zE{cSCCg>L5)J>f5IGu4<c@{ zRb&z)X9O4AkucZe4c)wkUp~#xpC583`;xfL*Iz3=06VJ35Q{375XudEBWR zi|=)dwqZjfaSwP2aY^Te!2cqgQh8VUoy9`wc+bLzfQdMx1vn z4I%zvY+9uK_0^wC=gId9Ke{;$>(;*iXSc{NF5VA*Yzc52CGuE*qwtLB-< zpHG&8-h6VZnys;2WT~06UltK`-o5Bi@WP+|^4~6g%etCyW|O~gTcn2S59>TAurN(i zYcN9`?!#sPc7TG3YpukZ!t@;FAu3i{7${xarp|b=$#cFK7NYQ{a^$D+Ry2tQ2rqDK zm?6d0_48k>tJOp507VY#+=s29G{>`W9B?Z`yPb}A?C-Y0z8X*8vc3#D5&uMsk5|Pr zIXUSMs0TGwRlozKCF~CUqpP^CC3s1nfjw5YKDN>V{QTaH(4HnsQ+;j3JQVbX8@1el zdu!xk+elGM{;2d}sNJN=3G}zP+YolsB~;Zr_Ngu~m^)2h3Uvts8PN?(P3V1#@4Y z+c28LmZiOhOCj?pLpew&uUY0-XFlm2$axb#wRWTai7y7K--w~#?~{H3@{09T|OCk`9<6(Z$G!Ei|mb#pYLd{X9kULCQ9gpjhboYLbUHaVmeGTF7=&dgBt=(Nbid%uty@J{V<*|jH zM_nUBJ&{r0USyS=OXv)f3Xp)lE9C-&2bnO{M;@j)I5-faDy;z%2cw2@3!GF^9xDyB z%EQIsr+-u?UOzvbq0|_gW(YF4@cRy9^_t8{1WysCYBe359*v>h9JaaBT}*1uBi(tL zE8Mq?(9e)P7d?ge4UDIW#bc}E>iW7sJ@cepjm^|OrmH_f8lT?eLC=1sAGH2FI~!D| zsjk6AUHXK3U;6XbXbcn`h3E?%1(;}{CUu3y(k}utW=1Y)Xgx{N}b-!v|7_dI`O<7Ei#@RDJ1C_m8n^uOvs6su$|K6 zDl4%8m;Sdl@ZK#vc~E`+?44=u;l25wbH`=%Hk;Q%6*EumH0wW{x<3SRGCrV)6Aa!& zO0Hyn*J&AlEqtuD_Wwr@51r)8R7M!03L_lsC?olP;NzcxfsWd>f1zDX*q1DEEk?{qLBzr_jPeo{!P7QwaHaK zw!PeElJ7^5I@MQBMMooWthrQdZ2kE*f>$OY%~z-067Itl*QvIs4=t2+jRlIu^~aas zXV8-sn61doh_+zu9ky>@U=D|fKCiZ3HZA!7McG$}McFp-f`Up&Ns551lG5F%EJ!1@ zba(gCpdzrew6w4y-5_1k9ZPq2cXJ-p_x-->oO7Mv4=xrS?&r>#-~49onbUdq!~7a5 zio>lJ57eC(5;KkjQK0yOJC{9vZDYwPw^Vkkw|m36o~Uxp=HhS{8etpi8TkobZ?cdwu}5qeNo4UCna-2hdF|o7{!#`+^xDb342Jce$8DC>3H98*?L`{c!+ekuH$qNiltb#VPLa` zwNZ*0{(gT4tL30Tn6use8K*$;cV{8$$2vDY7*|RllDBwh;b6c~lc?S%blphaA+u^| zIy=dEdg~PXJgkBU7xm`KcK+7vc2zMl5Vu0by6QN<`NW2mmT8;8)jRgqTyy`{D>eKw z>2xaoc4Wv~=tr8AfRu>%FVnzN@3Vx^eHXa_8E3&F`NPyVlrBNWvt{o}jGMfYBxMCw zY0s9eFsN>)taX_*fhCx&wZpDHY1eVWq-fv0iK>e=s20A7z>J`bNKU?`sxmtl&R`S% za-=TvXZ~4^TQPIHSk}vJtgjeXv_u1dbbKj1b_@4RnjWfHKZw~2prYw{DikBbYBLFO z=?!cY$-&5bJFp^W|1Q$j)Ja=gUTj?5@3Y8bL|6Hkn4(ihV&Z}o2G`QCWnqjAGt;!K zphz1Rb346R9 z$$lSpN2)(z#U_`N^k7lQ-D7SqBh1h$*usntt(4)5?3;5WQERl|j@y1FcdvufxLT$_ zpNHLIuSoJE&4R1%j4^jH=KhQ|ylCeWcLkD;Edl{8mw)?dx9^;rz~xE+KBSSvH7&z8 z{A$;m`kYP*yyP4gOz8Z9_@g{YF0@2U)+=K`_YnOaT5zXsNy3nQ<0=cuwzf77%A4N! zEvy%to2huWDZ6pYC1>05unYq3;ia$H$@t8afe9SSEj8Y`dN9N8^rU4WTMWKS89KZ) ziygXt2G-q7rugqQr6piIG*M&cw<3(pEC&@X0pCyhq;9&u*8bG-JIs&DI;~$1c0dDG z7-Lifd~cN{-cnopxq>O9Y=SJMEfn6d@)(B|J*2~$J(IXz27Z{S^hx+Rii?8AGiXR( z=^Q~QQOp$7+m_Cvp8G25iYc;j0SgDNt9Gy_)|+0MDUBXGnhOFI(0O!^4e^yL?3|vd zC5+}fLNAw4a>MvNg{p43e4Au{gsxK9COkHd)p;J_@12d_+x_&~oN5<)4_Q0Ej5ja7 z{rT}?hPS!01NjTYb;);?11z#lT_xl&IXye~U^0@_StB-eLFA7p$x33?O}WNA)s2Ry zXy8wYoa5UM2wt2e8b=Oq(|{^l^~=}$l3)BuWgwry4J08zwg3tt0f?kxIYvV)Ia6>E3c)jj(VS>T(OnD3mF(pgOPj~bI5OQmV?s`o=<3{Q zu|u_O>Wm4CeO1n-RTQDud|`d5bJGt-EZNs`pC*7dK1F_sV$RjoVVB9 zA)Y6sZ=uNIii;l?_k8<7q}=XGF#W-})6-Q{p(~;KUrne(n8!yQtdmEw_j#wNM{MAs z;l~dOdVAtj{Z@1N@&H7mhUbAC-DIGGyE~M{fCX>+F0d+g;~*sEmJ{*$gO2s$y()HL zokoL_2S>8K-(DU}a^wzMIkG{dUiaCFp`IsE2ep~JXNCHV+Z@?GpjvcZb0Ul#$=bEM zp!_Wr!5Ap2D%BPRwa~UuGNNt7HqFc|1=2@Y$4|@YFElhDyfcG)?D3reu-+SG4O;e9 zLWt}u)h`JNvHZ2EH4e_qE8XP#v1|toO*pt{tGsk7v`r7q$t<9D-vd;OGB_#{6DO_8 z&pi{{x-is}VK5I!QmBeLPi+JxsCOCdtj4VjaFU{YB=Qi*g6TQ&5r}|;)iYLl$SLC7 zh#A?G43^AmW#549qt!bc1aRR4be7gBanbIY+V!)!Mm_ThIE%-TYB^LsknLgfQuU;s z@5V4ktn$u9=-8D|$|%{K@;%(+_W?Usee`o&>(Ys>^KA6>P2-1xQ&PNA;AYb;q4GCx zki*nW_6pr<)n&oWnp+cx!;6P*s3FQ3&p~2F_6|T|EBSl}a{@3Jy;j14tIE^xrC6gm za3O(JGCJaPUvSFjw1TH>AWt7AZp32F5PCQY?_V3j9B>up6%5(Z2cTrhgZmavt|&3Y z>FRLcQfiKpz4qFA=V3@+q58I3#}jywYZQ5T)6S7yVjFWi>c@dK4&+F&#R1bYJ|CGr zyj|PNq1L?&H8Wd#zr*@Bzn2V#7h94e!;i_-8MMBit$oYjMYKL%X||j*>P3x%t7F{J z!$iNmA3GzGzTEbMKox_ix2J;#Qgy1jm)7yN?*&F%cG$6tk7-Nc)9Aj%$6yZBM6tYa zhmmUCA-?F*DsJEuPg32uwh#bQdKz=;=7~#&QZ8ZPi0YQTn#=kkB@bus zyxLRyg{;n?;oDUIdb(e6ktV&+Q0PF)F=~=)&1%k$$D;G?BUDYd=#dTFCHlMpeM1ru z$aiM_u#$l<=2X>*KIw%m)UIZh=*)Wgw-wwF?F?9xkNRg7_oxYTnUl34Jf0J!W|dOr zgfXhCClxoV7guRhPo*t(Y3EYClGe@&YdVA3Bp#h?jw_)w0T_C&s6`v|2<2XWXJAD%)04cCMl)I$B4 zuRf4rTW`z0c&N zFKAk)0eTI9q|bkH=cEzW`QoHuvOA0POgX4Kj4CbT;ihW{kCW`gB%4e}m~3bDskOOU zKv$C1@n&Edwi-rln*-;Q*?e{5wnx7hl`;C*+1Q<}usKD!D|_cDgF14HE?hnW2i?ZW zU*+rh*2Et5j`V0?ZT@v!6kkcBnL-H^wW(b#I{wP5&SD^?bFGkPSS)D7pse~zEKEgVXOKhOY^l2Hyh?Ulft9Qg<89grsbLYdrQt`v!2hk$BFnN+h zXi_3&NsCc`))r>dm$wGx79^EZTP$m;v7ac#TCxceXXQE_UQymwp3Z9#Znh17Qh^uZ z0HKj=t~@!q@|!O14XJf=YK&QVvXPCM@=sC4Qj2(`=|KxjE+_Yts1q7fS=AnE=cpST z^5jwn$r?R+)J&_I)M3R<4?ub)Yr=0ZcO)Qdm)A=g2rA(0>neuTkV&B$< zVzaNw$s*WMV*VlQrpjCquT;}N=@I)WJ;>3v(N`uEOC8A?cVJpyj}(zCgXE9WyRKsW zv@oom%{1A*7p|G^z!M_5P!%SMe-q`_?d7~WRY%@t;=WeErLOApCLPiq5XF$Q;a~oQFP&g zJfR63W(uQS8yn_8anHd)`xeR1wD=Sh6qpYXh(62Gn-(IS!Y#G>Vf$x96cj1p;R9o1 zW5cS-%F*HBcFX9s@wU&L7elyDX19BC-|WnFuYyTc?5OTX&ssU5yOqdFG`k0zmlQ8f zKD_jq8=`y0S3mhdaO94)o1-srKi#peH^SzLjX-)yL!+XsTvl4@=w9-Nx;suX(FHy~ zul=OUG6ryHuPNlSqr7}%jVS%Z0f0@E@MgrsgDY=jV|1cayx{ln^4(^68v+wUP?7pS zme~vDqgEfJWkeuLWtF=hU2CD>W=UJuXVnM&e)dDAEpI}_KXGionXuo#2fjVe{>ShK za#m%YB%LD1Jzd4@&FwwA>jixF$6qN zR%dyrl~SS0L=WM4q07eppBqzH;k})DgNZ7>?f+rpCoZqo^;|^>^zkj$86J7}=V6 z_UFdFZa+Y?3fnD!MMOk+Y6HW<hqh;VQNNM%enWuNI6xbkJ$EN+=jUF&AySgCec(~vQbO6-df zLn%Jda=R1;TcGcjpIoPrv(*@v=P_7nVni;@D?7#zw&Wg0sZ>)fQgpQ`_t<{nzA)iZ zEvvC=Q|#^BJ>h6^zy3P{L&YZakBf`N6xFBE&(BYnMI6k^$_fm{zOtsWQjIz?6gqG)l6#X|{JzdjW8gTY zR+kc#_P=EzK1DZ9)ka&qdlIh8n7|}eZ!b4*|2W)H^zHwbP3(I(LL~SYc{a}(m5i&A zr^{r2k`p;>+|>RY7M`QZyENLBotPHr<{qrXmC#N!fbZhh77fv{BVCwtK^mJ5Y!iMMu99&f9u@`Ms zjCr|^o*gkFDV`AR7nn~zakyPf(T$`{RdcPbtn|{AE`5K3+GlpY?~ zJbWdf`MT%JY^nnt19^s+?5D|ZfFYPNY;B)iOBkjs;+1b;=+l^G@0MgXy@K zLH$1)cs2Lr{%phN^uRW3Kcc@9{R|XJeDN+6kO3hfA)lUq?h8yCa}qei?DY+2v1L^( zX3y?CArCU;`#J77iecn2+6AzGeXp3XvAWuObRvYQo-xRD&lZ;h<+iNVWwMN9W#7=v zigdZ6fu(;MS-7g2nXRu-jXuHcFuc!D#@DoZjQ-RIFua00msYl(rgXg+jVndJL&o0; z1gt4=SX=VB)Fl8&O1VwCEwSJvg{@kC`&w*iKqOA_rEM zQBzmLYb9&t8EF8ciY5EnIxGeDAA6{-?bRRklCKS%HG|e*V5~qM<41jm;XqAQSAq8D zfRJUkdo!>_Z_&6>Yz}~C1EU-a27Z5;l(Dklt@mb?49)?Z!Zy~uYn_9Vl*BLjO5J3k z@KVbL$=mk=QeT4NePfTw{4Sqxcf#iQ(I{}cCog(7h?^b@*s9+%DF>l zZxItLX2L-Yca+J8m{+5G?Lfk7K6H#>dAUg0k9+pI=YKU`*NT zH(o5P8~GwKWD;VkzZ>fXIPHvAGohugB=WYbYJDR1ReH*~XjgF`{PJweEw3YQ$jl{n zn(R3Uz2X^@f`xJ{2R?ix>st094UT++mGdPk2vqqA&Gwlb7`YtrWX&slcJm}Fn5*PX zhEd1nz7tpxuq1HhW7Km_c(=8aVJIn0JM!7Xox6wme^%^4%GsGqmeSKWy~kvHj%R!G z_LTx$T#&&^f?_ZzTr%BYXuL5AS-d3(&r;&&tt|%g{ z3eg*VS6*CPE$?3B$lt&_WGkuq2Yq?Yw@M3(73~p?b#C(PUGw8?Y5aT8t1ZOZ8EyOK#Ok>}$$ccQ`W5N0zWM3n%3W;Xc!@n` z=;FfSB0C$~Q&Q6Er+^=njg2ke#YqJx=#|n!K+Ni0h zjxB~wDx*-I|Bi9Y{_hCIuRvZ=qL(Ukt~tqNH~?%OoZODT$^VhcYuI3*5*6lvKguJF+pN&OGziGkVd!H{X=5K-a_~ z(D%^ijXZvUIDNEa`ry*iQdCqF@ckyg$mTx?69xs;DiCZ57YhM&0Gc;xjWl&oP>=!f zkI7MdEXjW`5fah^qyX?QmidB_stwE=*gyVLqz4>8KohY5ba!U@A5@8SByU*_iX;4V zBb&(Mb>#v;F`6Gwa_84o1RA!;Z9?t_E%+v{a#SrZ1Pd&hA3i zF(iLJ@b|1|O%o6P&fai9sC8PXe_fZcmz48+5J>mc-Q6z(p3ppf`t;YjvWJI=SR#sNPcL0aWiHj5GvXkQIHa6H=9)6lyHp}VC<1HOcOwdtWYJXK0 z#XUqGZxCVi(?}(np(sj;>&xq=;-J7h*C%mz_&Yl;X5_u%UxEzWW%Qyv(@r{^d> zjUywFVe7hHx-eLi z(!Q$7Wi$DuyoXDLmXUT$z({00J^*t+9kue@f~_+M_BneZz~bjPHD0}IqvNLERpM3W z0x}FH@hv9!RRz5#;yfDrqDANJeN25Wjxz%>670I(Vxz2>K!o^D3jGapDTLW zBU}Ia9y6^YSB6Xu&i~1cp*(Ow`)9o$_{YS=w0jn)YiL;f{!&y_Ojj_IzWc(&1~T#xL1xORJ}qs{c>eMatX8jjqTeNFO{r_Uo7#BoJOF|4K0SU*AV<>tU8XBp z9Q?Acop=Pt^11QFlx2`>Z({M*)Kn!#%&L~05*hh?M80q2!JfdJXbVHD!CZe@ zGRlFQ3)Mpwa=miLxA&@&pkaM&uDl;rFq$)+;y*~h_e}w+yzz%gfOCbXIb6E~Cwy>j z3G;gIZ-%aX=)WKgu;$E6O<`9GzuFhy7~Ae_q56E;Q?v78jgyk z_i*+*9v-pkpytT1TeP9X_ik?MwCB?EBl6MT3t10d0{YlW2e>D40!S{Ky7M1EKJTM- zgw{4*z{{)c&pvp7l_vDI(pA#@bMR&PfqN`bOz_WKwXex_g<}G_-)+yeKgLBQXq6dW zBo3E31aaf{Z}1|10BEb6w?{$2)-!A|G+9xJ_Ej7^BT(&NRH3A>A~PpJprFFgI-1uR z;DX1`HE7hOVvYpv4WyR2@`_GAkhWett@5OY_gMCusU5g^tm5}Ryh8rizIuW(M4iy_ z4Uu`z+UE81kN0G2C08RXj>K}N(;_cDkbTwR2YiWMptKK7-&m{esa%o$k^Guk0E_?* zjiDc><61x9#O_)?7#mZSScS9$+&FzS)&dfS)FaBy_QRh;E64ppRoxVW! zGpE-|6;P9Twd(@!qMED3#m&Rfoo?YxYv0LVO`*MX6NM#f3ooP@=nKw6cQb|QoQ!=C z-;KA?{lZJ=InS>JL>b7B#>>pZ#&<_Zd(>;=jPlU^xDxJ1MDB z4h#e++8o(&k=HNe2k2iGJd}@mOJMZ8o<)9yB@GDl?)Bupy3vXYJ8rnTr z!G5kDN%YLlz~6Frnt;B-BoXqcc>V}z6hKPgp^*{jLNgi&z5Zods)#>;kATXy#gqC? zjey2$c>aB`TA47ixW4Pp$^Ed)Oi8KN48u*M1!Vf?I~b&-r6mIf8k>*yk&uu8FpH$5 zd+!|o{vL_t zo8AKej1>KEcskXX?SJqI(gn)@D^WL)nUZ-2HXnUucD?{s;&uR8mrsP8Lv0 zeGl{^mLBI3@cDVu-!87bP<{Jofa>lB8gFqu#yJ*Ht%%q4U3(a#WPtGj zas@mzGcz;mtCRTmA)ONNJCNQ_J@+V0(Udi2wT| z;2O_-A$D;PcIfSPxDZC#;&48DzFjc8uTvq4FDfrk{LhUUVv(!C%CoRbu>93QSp7}j zA&fRqhYw?Q%Hxn(XM9v~H@V>P=5E`AKXc1s3ebC*PvUTut!I;>oV12#p{HMk2B=D5 zX}9og8O4l|Ypco1ff!mPZ?{4wApEPf_Eqd`Hcrcnl$ep_{HY?>fuW!k6|GUwKdZQ= z2uzXusx&eF)b2#m*ODuhVYw*F(^)qgK3w_2P9s-WEhkz#u$uyzQ9v4PBElZ>RjJI% zeR{}*JUv$DX7ZyuMRh2whM8G)R$yhVaihaon!mKZuI^$#gs!zJRwY>VMs*rvGyC4r z!3(;+ni%CWOWzzmd~@pA2jj-TSca#YMF`FzUtm<|RnJn?s#YR=j$+*nGZ1a5si} zr9izJdhOm-oGqpKu(>r1lAE1;JW+MLxqTjxUtSw`JM6@0{1@pW9o<>bPJHl;*5Yk< z{E`xav?D(?_V}iC#QwE={J5HB8m%Nvcf8=oO#hgYm~&Sx;3C3=w0t7VEB>NxWx^S0 zDL%c{#{o%2h&ZYbxZW>~nO$9zRaex@jYT_>UuV+MLblHZtWFqwmGGaK2*?+2jYPpL zaQ}TfB?IN>Oa$(O;pkc&-0bt3*X7ATeE#u~m)asG7swlcpnH_=>2=F`<;Q>5NWb5ZxI7p6do-?H@z{1dEV;ZGS75J2k@a{pw$_~%fQAB zt?D{ZnuA8Kbf9_m*|)d*b18hSV+R>+?DZ2zZ=#9qNF(rk-@Ylz$y@B3Q&~=Vwc}#= zkt;zaH|vZZOlfzIKh;>}TwWeO;iSH7tm;+&f6qGI-K+zSz?O|93i2ecOPMDA#Z)_2 zS8k=+`Vu$I8HZDpQ6ZMwx`z<*U@|fxL@ltpdQY)sJFAT}S2qW^m#=HgRxd9_Osbmi z;=^x&`0&%u%_YqU2Y15tE}Aa~nm+-8H{Uuh9xa_D&MqjnXC zGd^nrEEN+&Lk=!3C?`Z&*~n0HjsFt3#thU_yvXjXI|9O}dlI+{>VF#{9~$9#c~?P4B%1cXV_Bk=j7H&uNfvXQw0|f9f5b;j~x(_4hKtG6CAM zqN1XxXn))!CAw?5y%1^698gg+AA0lW zB2@qKVwmvFc%K(w8J+@>nlGuSD4Xtm$a!+dChK@#bK=S*3seHq)cQXm)eUow+hwC} z0?B~$0(iSWg%=MiEACy(zI)gd;MnuS)lne)ELUKy;p2Vak}QzG_^!JbzW`Q>Mkc=0 zVYQbV;HkFDXN6o34Bg$`y@^nfY|{ZX0!{*=%dI9#vwr;eaVNpQ{7$#9<=0e+(dezElC_Z;bE z$fXE*UK{~c9ZkBw7CCK>R64A(VX}MQTmjsXp(>K?F(B`MKZ;Wegg^TMIMH$KJ*cy{ zcc#<~0o0H&WE*GpKJTii;JJG``ae(0rU;Q~ixT%1+B-M^r4GG!Yfn23izltCAK%Rd zRbjxNaPrqj09}Jo0NOH-V>jv5u&F&>ij>=#s#L^5k;lI*5i%DS{Q3yJuK|72 z{A#$stIT{j`!=PvI?N%v=J58m11YY_lNbkk;;m_;nDvI9hF=AlIf@n>67nfNunafv zffA;=RRiRm3grmty)xo4XfWjJL5-PXbSt`b#k#ezB^A1<z)9}FluT3RcGg!FBZ zRKR6VFAUJ4yvWgsHJx2ulhvOiyOSXRB;k;`yqvw&9xHQLl`R-04FK1j*6tEe6W@FN zuBG1Yb(>u;?`r8CRB166IavM8G_B+)T+0VmY$GPC!- zi@@DxLC6Vv0u}jAH->W*Gh~fRDlJCyeSK#ps}+!VLeIFd)_8tePfyGMA^*0LufD4Q zg;6bb-v+AGdtHkK6Tk60j@}wCSxvdUS_PbHCF5HJKn5wGIpL#zktBZSn|W;Sv08WM z$c~{(2a}TVsTret4f~=WP#vEEboch13oxVV=K2ytCki|N`}gnOw>JPefTH?thvN3E z?Yl%IFsj7c%lg~PZ5!{)E}a{D0Ejc!Umb`NX$`^Og+<_va6Yh=1i%y@7U3=m(zogT zW#YD}Fz>5HfNypYNWOdaC$MoAL-_%$e*)}}y3-kVVArlslv%hFJwl=Zws*hX9vM3l z(sn3w8_!Q^YJ}@*lJoWPD3FtO%91O^p_dm(Bbx%Wmnjd&3-~;3t%BcT$X4bYhh#ei z2r{yVv#j@#`ghLkgALjZ*?PXRF8;z`ekR~zyE{|+xT#_@zdFDE<|6u)O1@V0$wCk_ zb=5uFjo)34nFLmSnk)m!V)+{DR?-2x_*xWsj1C@YE2 zNdZ5-a75YF_57DF#^U~&QLGkpr*aj#JrRwG(LnbJ31f}tESeb@#}1HRiwpBG(~Mcb z$%wKzTjzX_vHs@zEOwW~P9WcRL0&;Y;ViK+CMvtM^avrIru zMdHx|Zc&6THq5fxxE+Tk+$<_~ojuI?;k}DQQ2;DQ@$IAA>6?DW*~oh4L;L5H>#2%!!hOYM3ER7 ziO1`H+M&gYmY{VqY^g=yJ*l_Fa)=1#VhL54fX#^?;p5%7$-<>o;S}zzbJE9IlR}0g zpW^Ow33x9qrHc)Ja`oAOJG;QkRA$4mfud)*Y^A0yeHY5nGY?}h-B$uxk5i*lbmliS zQdEE1bstZhl+%kmRuKTomgr>Za5Y_yX(*R4ueo~cJVg3X0C1dZJXrY8Jxe`?g{y^%UbP{`boI*YqY!HLjw-Xrx4tLc_qm@g zp57h<$w_I*ffFCTJnQK&ys~p^k9ZuLr%`etY>>kpR-Nmu*S8{fD7-IA>Txk=X|P@6 z{BZVYPfn~o-w6a_JIk&vxL6}9*s56DNs;5Zo^GdrVnT&IPUCiOyiQ()j~N-*tR1At z3G74b^e3)ot`bsbZ*MBSU6*K79^bUQIuF`O=ISk8Ih8Bu4XXxV&-AD$$U^ACvF#Ba z15!iQGLI}J2WAtC8)n?vX>QedBkFSBv|uMov!>VOE;+FVJ*aZ~*wtayZxpC$g9$MF z=ZP^TR63^c%0?zX)@HSVy`gO9>RfBude5(Vxp*Y@QnouSo}%ENqc+^9MgcNNukG&S zPUv-A)6~8-L-;mV+LU?Xh}DqU?YS|-$tlIPVIi>}<@C*R8ijH&YmkSdI??*zb#O`V zma&I9u|8~|#r{W`J5fi9aal6d?-){_egPCH_Hv(I(E{;54Q9M`IaMsu_TD~+*HOZ~ z9KyefLOE2y&_vgm;lbLH<8G#$*~IIvu->PvX$!OTe$d5gZKMXel?t!*R~-3#HuYEO zSr|I;JhxSP@l|_eW32~W%ICWCL;eTOg3ekA^y;-wRfTfrvqndByeV8Ke$m)D?u-!rohnL&(_gi9*wjL&dpu$Q?hi!1llG-aZ_h+Q zKaDfCvd??+M!BtA`A=?!%R^Z(aUII@lSoNP)6dIG00lKTc2Hbc1b{&Al-rizQRk40 zfpp=sMxsrjn=w;MOAfH%XlQF|tJrw>`B{qnikc@Te7&ZXJwH*f?;=)sbSPKj@Z{5Q zj_}1Y!a10)p>rQl&#G-!=z33ppy87+Z|Kc|KOsZ?+B{nQ#kgNXlCauKeR7v`<{jY- zDG{r~k9o3mSL4fxlU`2pa{&qYJwcWJCkP#xvmbiI2R(;%LBP&Atvl7!zJ}TIi5_@M~lB z)&VB6aEmLeo^dl`62o-hy_tOr?afS*BG*2PgC^`|tUh+%51-S4ax6V1JvVuP^Z^dB zyWJ$nyP1GbQMdxH?M%Em?;UZc?)A=Jk3Jj?{~<_#U9Uh18Z;Pd_V?y4l0j@VpL@?# z%j8uEs@h$$=92?$w93W2es|ngUvu1;k6^O9p3E#kC(VsDw?UnC%~=VOQm25fV0-C? z)bB%h++v`|URFmW+f^@xZu2Djyi>B&!oW3p2@f#x{O^jxwc+rDihy|Mh zXl+0)1*xIK|4u7Rc%f4QLnz9xJ(-Brc9Uo?!~*I=0>I0un*s&NLm5T&bz9>j z2B4K`0MzNYtpmj&s!o``yMzN)L*0Yl7SNn3=o>Q9+}BrTyMI}t4*9$?wD zRYydsySlo%19v2O&i}Z^+N5P!5; zY6(hGIUO-+v;-xshDv#b0R@v#thYu(o$evoD$w4+*nQE%Z$N={Zonl95E`U>kjb$R!awd$=pZNe#tgNhY^!hmD8<$x4XB1Zbka=qQBnT8`sRz z((b)8EZzX@+$YX|Crf=1%$QWCo|#rK3Tnc_L>+VUigpzoM82 z!tnBkS@Uy*^TU|0&>ttfj0u>RiSf#1s!BOcPb7^swxuqT>Rb?;j$>Aej1W1;dO7hS zxOEN<^oaXg+x=u+1@IB9Ya^t5X5Q(6!bAS;O|4BN&J4Jazj4X9WjunSYs=k*eyeEG z5}*?tP@jx@o)|46XWab}ODCb^ge$A^-o9mmCu^8!Q6{5h9#eb(>J(N&wmL)h3UKAp zKX2Z`+)Iw)CT^I37s7avRwYio)PL1c;9@^{dfnU!u8lXB*P2(U5gj1)@2?|6P1MJH z_VL6ESZEpwKt^Yj3Z)+t#bSrHErJFW!P21gg^G4cP&KJh8&kO0c;0TPC2gefjB#IU4MP& z?2oDwHYMaG7IeM3zx2jeH*iy!w*xW+Ac6#reAh)oGww3}HOTvIicFjdd%ObCCrbS@ zrBY$%HFhct%Vj^Bm@=5-VHN7}Jz$TUVD4ml4F%+k>cQkkd?)LIEx6LH0={*<%>r0D z4bkWm#}%XVU1z`cVOr6apgH^Ms{z9X`@N@i`7x(T#c{d%yjN)cv9d1*v2f)D!@u4r z#|l6+Q!5s~M(sPjgv)b&k#%Em8#yl!?f++<#7*u54{+)e2Lb0eBPiK3KpalV4-Ft5 z{Ayv@zB{&>2xj>TMfG9I0f7RC$%0XmAh_X~lM9cGzz*t>MN#7POIg7t`a&7Z`57;? z{k0Gr3Ca$rk$J5KMvFq#D*@j{j||5a`#EF~&ysBwh$JpJtGgEMt2D9vX%YuT!4svC zp=X~0l$sZ@GDN|0zEjYIVe>L~keU25St$W+;*_X>^Q8D6W{iv9j||(bMz`XBj3>m; z6#S}aTd{T5WQs-1ABhcu(%L*uVj7K5KIA7)$$O{5Z>wW!twUF(ix+r6&^CNBiF z*2~@D<3I9C$2ofD?ItO zZlLICewB3N!nXx|5CMt^c&G`LDH-Q5(fgo2W#Ql=Ae+&oXG8B~Lj$f2?F1uwILbDU z+Db099e3xmV56zy95N0rFS2T}^TfHxZO5&+WJ-v{>Ig!II1=*CBdpwCJlQ`p7&mNK z4l%@&sSOWxA>&H;!8>K`-}5B${*f5pT4D7RG-nuBx=i4RD`5pIW69Kg5h=`#da1Ai zenH76xuD%M51mj-;Ql%LUIIy{Z6H)eQrNl4byaI@VUX18xbxiwEAipcu;`c}?46Zl=%D_g-z&20SNPZyPg1|^giuFzDE&%!jItgm=%WX+ENJg52&d{%+5wXF9>Zx4AiLcYpxGx;ln!Ez{{=rT-p zU(*Xlc5r!DRi$NLE@_WTi^&iFN+ne8F`m$dgt=Q028rI$rGc5z;gT{hqs6nZ+;iqR zA^vS4{7NYKGBQg^Jm?|FtXg>=ks(|3qio7@;ndwx(m?)a zw(8G5s%D`RNn^@xpPyS+*}EabFm_SY-BLb;eP_(qtt z#vQu%CWP}X>s$_}$$}CrSGm!UZ1MEbDQzVuYb{i|HeWw4yhZk-9M$4BN|EVo^lDdD z%Ap6P@gxS)B=5V$V=F!T_?46V@k2z1AwynH6%)vfYsEoS$>x_8LAawpvZT@UNg)iD z++&`V3)6SzRmPRyl4T^5Hul`PUs@8IzHZxNjex7+4zIUEs2$6j7uH=Svd%@pj#>kQ z3w9HcLjLS4j+Il$VKU7>b-DsopD_XAjsYvShg2r;^7&~Vjfk}~7^7us!|Sp*;6qoW zt*CylNYBi~B>a-t&gsD*#nGb{xWw&X(Bw-8aV-Df;3i3=Y> zm1b*P+~H-ri~9k=iueglfb?oHFEc#O6;GH1vSX>eOK$>!xXRne`Npyh0(<7)i-L2L z#d%1AKHtkS1ceXhU1kV$sQy|(o505{suGMBj_9vVGwpqA1W_(l^St#M@EFhq5n_o( z1h}x}@E?s3S^c0{G^=h)4}@)mzML8yN)u;`d9URqgNxfk5heww=p^I}dJtjtsMUTm z!v4d|SZp#Zeaja}8SMJ(ru_|{h*FQWBDi@0UDT>bmG-MN)q)e{Am8B3wI>O@3Ea<)x4xgNq%FzH|*ZhcK zE(-GXt?Jx(U-PDbirm4`);Ro_F1zOk^QGFyKe}-O79F+rL4CA7Hbr(uct=hgLn^(2HMfDq$4{J&| z0vp_T>Qjpf2^@Jm z269Cm+&q2bGTuq@Dhay%GI0hrf6&T_>*)`O6}I)Ym@(oZ$%}N8!MOWi3IB?V)a_Z) z9yxQqtzvf&Nz2C3+0+EGoX}UF<5sUQw@H1hDS4gUUZiKu^4PL$FAq1Sfjl{+-1%wx zQ+W>M!3BwScG!!H+3#W=$ElT>4y0wLct^;Pd61oxtix{HT7#<0 z?ytfu;Ze7p*G4X38m$~eSbJ9C&{Xs8=B!eE{+Vj+mew>&uv1rd7ZZ5~`L{;M(T1th z_?ewZ>AnG*6ns;?Rc8D87GCoX38@Zr4_NJ=CVk|%c0jm6Y}aJ&9-6b@Wp+kVe)7n> z;nS^4M9uW7B3wv^BQHpti(vl(9{58|{dTT^@7eg+OmvdMe#Jumk-Mg>248YJp#n;a z{%~TlZaUtP@1TC!B-PVkWAIDT!-*tcIYMbL$PBjw+=P~Zu;zWw-&wamSjXYdNG4^D z#Yloh<%A1>%JJGFORkN98nhh5A~vdKj3b{z1A586EyD}Yx;T%KGiQO9^&yQD;Qtq# zFJJHY(3Nn~s$U0LR4I;=h7b)b3aO+kwSveK290BjBi7?Mnk^^Mycsh){>viB_)mF^We_p>PylQH5u|V>-7|dm-EY2j(xu<|Ee@ zrdl;C{R-ix>fmC#dc>0=p6zi{o^XP2X@+Oz_zy_Q{5EXW7s6d2VBmddTKGk(QPQAj zf&s-M)kz88I50!{`6r=rME=VsSwG~ZB{xssJB%sLiz9v+&VOE+63)gqBV|utM4O&3 zII2qG5)2oo(4|d)0fJcdNw_Km%qCxeAnM`ZHgGVFGv!DRTI=}MMbbW&B*}o^1rz7O z4K&<_ZqDH=0k57 z;C(P2>UM6>6~n0U^9(nG-e`HTvkRbIIW0DXT%A^yBqFJ0@->J13+mH%a6O@Xw#;gPJ6^tM(#lVn-7uLijR1MSLQ9u`OwrHMj-SkSy9XJ|8`quASLp7!@hh>?T)LtFf@F z_%`gqjR7Z9c2go@fxztI!|MWadp<;?qY)pD8#9%)g&uj_LZBh&=db)PO(c(6rwjDG`)0!Oaz%Q;6>(L(%b zh#!wdOS{}Y)nt6EK9#EbYq1zrw8*sX+bMrMIn><~ssX_tp2xnlv@b;am^bD@SB?&o?WsKh^EHZ`2^cgx1B%YMEtw10=@f+xV5#;5B zgTSzTD8uOYuL}~;s>>>v5zjWpr-Z5}bHZTuhT>hzMHrk3Tm-Wza5bff=XKAMgnOA`&i=n=w1+QOGuLH%3pOT#_9q8ZL6; zx3G6ELheualQyvD2i>|Or1`|j4W8~UXhq@F@fmAW9x1dEE@Ps8KuMQV(WT?rR9gaH z?XCr-L>Akf-p=pL0|ENm$LGm{!RELQu}pq?Fv^v;--iTh3$g-!rRZ~!`VA~T?4=Bf zQ7K@hc^F!q-aTgj;eiHyfzGkHOx=zTUA`zN!e$IqkUs&@o-yBePKl2+_nEz{M!m*m zzfi9UYzPF8=Y@$=M*^JGNkBdZO#-&?)qBK=1WrVUVY&4*;Et`bn*13P0+dFA1ZIi_%in>)(4ad046~CQZ>V$6|6uQ}gZc>e zZ$UyJBzOp}!QI_SaCdiicZcBa?hxGFJwR~RFB06{eTUrN-B)k--n*}AU)8JKdh-WN z&3AgHr>DE8`+UwhJ++1t#}TD5Re4`F*);F=qY^}PQvD&LxG1eyF(^LrgC1(-yl#xe zjAc~QmjJF4|3UxsNCiU*m+Ynk|KVRhKHEN~m+aYQwNK4?NuR!imgM=wEl#nPQjT%Y zu=WG+kJIDibD`30&*-g@Rn)TLT}R3KvG(d&{C&ml2`x-3-jdzp(<9|jLz$P!!T0O+ zU8_Z;ri1#6-@Th77v1u#RM_aST3RHIFxK$#+)sx!g;p+-FfVQ!eD)+xm9$Fqye_UO zOmAs>qz$r-UwSv)enUwg&;N-oKw_YkY;WGV*!4Q5qEx6m+ezbXdTI_Dm0o@7c8qBq zp+@6`7Eh!~0o-L|9g)GZ_i#kPCrXtof%rBM?raD_o~zKLd?Zl_?*gA7N*fIgW*VaT zyhupUdf_f`BjR#9@p3Lgx&9Uu}<76SVZAl zTgE@R09pc=m=^@DriiW{W7DKw5CGAuZ%0H(Cj#R#p%XjnhnB~=AOPv`1q}db;()~X zi6)*~%|TTV+}_$P1oC?@KB%qZ*F$k|I68f9I}Ewm;as`-8yAi=EB1!wjcu^^^$pi-Y2g4**HT0MB}QL@f*$!Y-}l1E@oC~)PVcXLXDv~OWNqR9q?%g9yDM`8*O*H z2G+GboGn#$j~4|f6kiP!GyhU3fIJf20IGJ6m-}7yMyt`?OCWYd4FXne10Xj?&-n^X z&&M0?^ME(9f^gvt)<}rF*!amxRPrbcw#7qaeW(_N# zIX7!a32-Uy|qM1B&Dd83Dl%dsNxWFb>P0()i>3NPxE& zZC;q+glp0oK>sth7;P`DZ1VgCDc*6}db@U4thNyAdQ*eX+E|xY)PT2o@$2-2dz@hQ zHX^z1x-pB(QLA+Lu$b6+<>r)B@Rs1r7%*<6jgCJIw{?n*FhD_P?d{otxkZ?RV2xp4r&F}#>vo-QHSZZ+<3t)pIYDsSKzbSH=H9=yUKIjMByrdO zX*p+I$@7$Pj42lQWk9iSumo~4TOaLQb~wDa=B*cV>eJjLSt~EEw4)GiQ1-q&$)c`) zezEqh6F2KtOnN$Me${#aSdVyamBI1J$k%hqFSnw?)91yI$`PH%F_%0pQ<bsp!SAt4{3?a>Nl`TgF72fXM3G4>k!t2jTiOB`e+qK#Y0^{K?tm2JxnhpwAEsaVR z9dim(g9=XDPtN17Zh)$4DJ-qRCC@8TbG)DuWmX{;H7;4gJu98?hFR+EdnGdhG{3{E zlCy9AKxl9epu^E5ff_8+Dh>nM19j`#cmS@B%t-vSuypxF<;%%5tp}IsgKEvX48j|Ma9cPWu@}bkl(6tM&nsLHfOj)O(s7t{ZK2id zVIU5gu<9yG`$jse)-8@^%6)65D}6AJdbl7wop96Lx&+kXcsVy+nI_NdSVvImK}fp0 zGta|)!*=CcJsdBck{7BBux1dL18p$N~i%5@xz8((aLcQ@I zk=__#PfZVL-~QyPP#Zn^WZFT+Xs`#R!Tfc1ENU$4DGjBg5NF8i*a2t9$&?Y*qi)cl zT2$ayo%EyU*t6hl-&i*>NeoZwN~))me8sPs>)$s@oekgHZ#u`0jgZ(uo0CR7k2`Z% z+=+xBN&=F&;AAx`@52TQDdl)8lIZ~$T({Yqt{^XC{8Nri4eh(v>k!Ijr0JYPpvt-Q zUkU4*ey(Q2P39f2jGNimm&c7TcK96Tv>PYwew=u;K`G2_Zc7H}PUGh4wH3JawQaX& zSZh;ISR)um@AIxFy;VJq`<0j72-vnOAVoE+RgTR#tkPcBEyVvUi?fWrJYok0{{-LF zsy7{jvbPx5Vn0yrOAINTfqJQ%iAm-${N|`M@3@k?!v2aeFWfk)QO$fgc~?A?WwdKW zSw}TE5#PE)!axvu&~&-a&Rb8u|L0!0yvkEp+IosnwR-J!WSYkOQqqusb+wKyAraJ{ zX2kt-B%MUj+=#NE=g}lq;%SncF;ns|l%~Y|=4nyNoaKBkVj9iZVKDT-L+2&Eo%;B) z>aD`=WdofzlTMcp&_@7{7nUn6_18g24PKslh&nk7szOn?Y@5$fjxi<0?>rxdz1#md zO!8*(>h@galp4*9dFktHAN)}f^fn&5nxD7A1i2rF7t^NmcrP;QQ$KJbXdHMOJzd1a zs;9O7$*3Psr>U_Jv#)hqnTt`RmF5q@}vb$Q{LYZH|4h^L`vHij%gGN~-V+d3Y;qZvAiCw*t> zSpf^Nc9qTxm<+B|uwzRV9uDlX5`QZ;Mk8X3{yrZctwg$vP(Dz9|}| zC>pd<5v0|=ko9yP9!{HirIKK=yxU|otF zh>$>!VOx15`Kb(3>?d|2kisUk{Acl+L;$CmtK}4@*pMb1K#dQO5sgrx)WR1;+) z_4IjQJaEl@T>sO+v)UA|{7*&Vv>y2ag;qWCC`BS1C-wrG;GhJkU5Xcl9>d8veC;Bm z-h{Op+&$els-bkH(nvbXt z4rX}h-X1RWRu&Kw){>cY63S+{m=YhC6A-}g+`nzC(0V?9L0heIyDkVFjQc=NpohWf z`u(+&MxBztPD$R}?*ly5N((b7LFo3?w?zH=smTtf+svlIOR&%K>)t}5q`Wx? ztr|6ISf8FOv$H~ZxWW>GW2qa*hOLWyrpZFS*KB)v>1(Jv$}HCCTNIyROU^ND`mlyv zasbCnwu-Az^{W4BEl03u1s4OZXi}c1z)Ky`70KPCGR+>2ndv=D$dtJ zH7PM*Dlg*(b)3kLMsk{I6h%UmBSr3*hI+l8bEQLOY6qsN zTzKYfMKYf_1puo6hMkA7r2xAfAnsPHP-(U}&@;cng8)c<)Cd?umP?w?&%xnv%S{T# zhYrmvqfx0h9BpH)DGEtzcQ`Gk7o#M$Bmu9|_cfy@>Ymy>*80wcUrFF+-Bq-!h}6$( zyIm;mGGYNX9@W`z@ka-q`PGbuOkU)zofgZfW?cHI$sC^T4K?RJoQU&%dT%-8W5c}^ zCqtem#)1)=a$7D`EmtaKj88n!pSzJ8#C-z{kO&6l5^}YS+c6`Ckly2b;e7>Wiz1uS`2(VI9 z9M#^vpK+|d&mANIT=jsZAy^Z|ggj`>Id+^)G8bYDb%C-4lad151KQnhX{aq9&KC?A z90Be;U3!jq5)E~b$_V_6V{?Lb<-J(k4ZZ?xXMAvYwXatmQoV9a;6}aOs>88x@mn?PpjNs z8TLCcsZ{CqMOIMP(3yLlq6l3<*7o6`L=X#Fxy;Q~zHhr=OpLhm-kjA?Ho;+H7mk#nC3tV&g(T{L*F^w<>l9++ul>%G(j0XI0&aIl~rxiebH}Qq%u)|RW z+t-Kk%UQq_0ZY_cO34+(rm3dDl?2z``2dwu+IH79ll(EfIsvvQ5Mu7Z4PsS0uqis1 zVbg3%kRjT%!M3BC)k+ zO|*3S7$)!uo;zbGbZ=4O;g0*@?L98Z0_-C|i{S=XNgyc$^Q1>$CUyrf&EIC43sZnB z1Ly*84(CsQdDF0)>*U<+4Hst3dbi|~5wt^jy-p1!QIe+95Ex6UG8s=_73ZPt=jWy~ z7n_YmQ58m2lqEB?Z#?FbO}QIvr;Agg8agSiIJ`oxjgyP*PaKx)ge$&0^t`K_QZFuV ztIM@2P1llDW!`QjV7T~ku3o4m6XWUXHI2WPA*;Bb#Z#;o`8GP-Lfet3`d#zB{#t4q za=`Ml^XKUkS|?{b5ZiC+z~mQI;?{$xJtoh9@shinmz9mspM*5pOavWbQ9F{xg0jp( zoXFQ8xo^_nF9VLsBd;GRaJp@lY=hU8EPwXCOt_n#o1s<)2b10CFq}xbF?V8$L(cM_ z)KiY>ujP z^?e(BnzVo{PQqk1f6P_Pj02dGG+XS&!x4+7z#EVc*muD-aci}XR+8l|JbeScg|ep3 zhWshLEN^xce++=h@@64#$HhYkZg0V&N-6tMe(%2=<#o!?ZACuGV<`i#BLg z(FN_l5f`&$Nd^rrj%vqwCLPxeF4u$b%$9ig<^CN;e7yS%MNy!(Ql z^cB(Mlwu+=6IPkT(klueiommD+iFv6aOA|vM7@t2G(Z<}SAH8A{y{%`d<`%Avm=`KS({@-ULV4`iDRpVC^`M0GO9vhw5t_ zmm`ehT$dx=eozP)3lP%ChMA9+E*bHzPr;sp?wAyhZ=J5A*oSNbgl(Mz|~>aQn^_72R)?=Op(?X!q6d>9SD5r+C}J!wM_J%){{36O;00|l3X z&g3l(Y_H0BDNJF+=Ps<#e&o}|=+8HPczHT?Vq5Y)wVzTqN^M3qR%jE=a zJRiX&f8P6g4|;`;$J_9r-kyg62oleqwqF2>t>;{PM67? z##+3~h!6m|MoB za<`G0UBN0C#YKF0eh3VVXoPBg+DQ#`wWO)@9KEdF5n|mM-FcU^tjbP7-?+cLwp>i3 zSz|jg9)uoPZM#T_z@?owzH1nLn3*yUdrE$AcfP}kT4}$+{v^_}x5v@!cx$!R!JWmL z*82B+{o0bbnXqIiO}p`t!=T&cj+gtPfy7yH9&23Y`)+p{^sm<;&9&cWu`cy+>H0Y@ z$2x|0Y0A}UUxH>yPabvG^2%Ueh-X^c^I$iS}Nzm<+yV*O7bHeeo_8h@p_Z=3h`}=V~%JC8z(HWKKn&s^wvEQE`MXPqZ zct>-H2g9s!ewSPAneVj-d#Z1(INGtfk$*X4G>Wa>{}EVCbqVh+g4PuxBl4}~h*3ZU zIt`F5U3Q(L09vkS-&7+ny=UVuFaxtn-q~6nw|up_=m$-dfloWqhY^6H{pgCBd6Kxk z{xHt*zUH_EutSK$2bdTbZzK&B^*W<9uu`Y}tcLhC+sH+-a&%ss?$Tl2*1H3MJNSOU zB*38Hx`;S$Hp{5|IDlnlE-~U3mL4k*wwakp`j^VI$5}s|`p}0fdRYDr)e7d3Os9c| zP{3ksjlnh<5GBU&7#gN9V>#-J?60@(6>+Hk(m3y$Qq?znOm)2-c`=Bu)yrO7XefB_ zlUSj$2Ec(W!4;V`eymcOHjksbkj8M?hyxjhxUw`Bas{?uTsC?1Hfy)gU|@Bm z|JB_h`a7IX=IK5<0m3$mvbBW?{$nd3T9o8bLC!U_o0gZ}P#l{)+JBRfAdj{)AcE zSy}IVDk!ei=GwiDQ{W%jZhSrQtOI zT>7SUg3odxv2krRnlW6%@@~~#`|jtq20xBULq3f@x{%fA7vAPwOZ#T~4NYgw3L~CH zJ@i0b0|s>kMegO$oqkzRVTSJ)YrOfho*v%WTl9kB!FfF_`@k6WPMd?70K6@h4!3bh zX~Pdn8eYSinRdX)x_2mOwjH%}6hp|WH`m=7zHHSh*_~-+ncgNH7ib?+H~>@N<<;qA zjd+X`{?FZXZjivIJ-wu+U&d^rP3jhb2J9R}VF9u4MJJ8O0JVmBHpLdyFH{Pg@7X!T z$8zB0a4iNG2qvhSOj}4ngFnX|J-E zg}s}DTie}>^$I12zwQKD>TYZ{0o`}Ot;yq2j~Mw%0=uJ|lig2mj)YK-n%*xda>dBy ztJ-&*_B(%^@QrtKcCU42(%HG=?~e)(?ODe(vDH#AG1@Q$iz}iAXbn?A@R?)JJlFR+ z@u6~>q~qJbl5Rr6amB-ajq|xPPy<4=+iK$G`y<+DW06d+Hq2}Fz0F`g8E&V2(f|f^ zu)iOYy9XE{R%%r@H6=?GnscK?7@3-qr;T>B)BtV`)@vPXk#9DRdi0{PY^x8Cs0H_)!0k`)P3o8!?0I)! zmsEWt~wy{JBYmvagu1utX^OaWT#`dyYAHaORZqs!k zjG`G5xS$VrX^%sdXV;KXEhlbZ`|pf51ikGG&&Hb(=f5lSg!^FkVuh0Dzdovng0eX> z3w3fNI_<81=f^X{!#?#LZ=2SDEO3?ow)r@l=;&kvaO0}{+rcg)zcNQ#*TKn4yM*t_6)QR z*47p_x>kl3wEsRG`oBnLX=vrBr)&2gQvIvD**oYuINJZ`bZ>?8f6b+howc!@A+UJ% zf4n2!zes6nscUR#Py5}}!BW@8{y${*Up=Ieg|)7O;eUF_f0gR3NdI$2|5ef0Slc+- zyp`&Iw9Nct}xP*-2y(83USpVoG?`i^#XhE@(VE|wPW!DPgt-r52fFbn*< z{p0=qtINKfW@+f4`<5;uPCP1&4msfV86|jZZUH^WM-aWZq(m&EBq6nAMKJOh->O@v z-+!c&)*6s7CZ#`WX4zLtI(e6hol;olh4b9$`glm2S@}=(b-%hkK_I>IcqgR{2=@`A z1U4qjz$;AAS6a`)Wr#s_)~9056$JwFwLlIjN$TsV?~P125hUA66wkMRw9`w;{Wa$y zHIdQPyXYk4*_6-}&Js%1)g$Bo$Ryg?nCPJU+vo=Qzhar`G{%I}mrnXf=LmC#Bl=F? zen(RZz9@g^#`44S`wn^&#bWzyyX(_vPjwb82uE*g5xpQHN96mMp6$eIvr6Abe4 zYm2)~OM8YF={Y&c>Ib&jxu=-rZ~hbxw>gUWV6q>y)000*JxVYPkV(7?eJ2)$K1r5i zg6D6Wek2+^1xvFKF9N;&ApA}Mlr?2M-WeAq9L^heMM=VI6sJ6KsrB+pd~plEhxV?S zZn9o+N%Y;A^gWuZ;?iT^ke$`S3=7>tI6qjwF!rR4ur21%cVuG!Xaf-MdM{>E!lqaQ z50-0<3LZeI$-yjaVm*k=5dYLZwZc zS!zelxJ*R>DJI`f`vLXjr)C}znbw9$-Zy7iDHi6 z$`U!5)p84TZPY*q{_iUb>|15gwK6p_w0EGfGcX#B8@2MIL-ar83NRd7;G>0vv?Cj8 zHp}W_4pf1py+}n`?TCz1lKok7kTtVeA;h>AS(kRKi~T#Srl?ziNuQi}NW`RmpOT}M zra2$Pv9!=YiI<9VZswPtMbYAHMZvC}7(mSmYcXD%OXiY#$tB%J&^^m$D+1eJ7y0oM zZp3%f>n#sV_OXpmM#{1dhuP7tw_mx#Ryc~YYoK)-qVMK7-FLo57_XDg za3X!s%IW&L`$9ARim_2y=OkdkHfu|^3`Br>&wUj{$mOYonI&)(3% z!PLsw{;j>us;kRyHX(WOcK#STBqxc~RY}KOu%m(ZT|V6kh_xi9k0Gukiu2d@R+H+f ziRPONxtSS*(Gg#fD}6kyRL|7zq~*7}`O*9ly?t=Y3q6HVf_n$b!7Xn8HvRH&_-phGh-rSsa^X(pK6H2apCn~yab%2e zn+&Zloc0Z>Tb$F$DWGzk22kg7y=>KsEv^VQ1t{GTw={pnfY0!SxGRjc)X-`4@i6^e zcq6+IH>zQS^%lhsR{u%U9TsQLbIM26-|IijT?m}TSR&&%VLjME0Z4^z3D$?px8<%X|eU@vTh=tSje2edx z8HgWzc?DM~0%~v&*8CPb=CM4Mxxs4A*YDk*zwU^DW8HCvv_S@wtCRICuTudlr+J6p z%cFBnZe$y7UaDHrak^(m)4-D4Wj4FV{iz8ce@VubCeiMb z2>XgKj(&bVu)v#v3E@-WdlawYH~6UXTc@VvT`5v8-`GKLe=xF7->73Y+@}KMo9P59 zGy2a(bllJVNTuWq6W`rbHsZkzINqVjEtW%{2^uD_Y)tftBAeSmj5vdTLApDrL6rHW zoT9F~_RMNNvP7uQ4g?HJu;9Zd%^hW(%Z%qWI$VC4&15}_Cq2aVx4kTR)q6*x-n<@b zbF!M;YUi1)RUbs$}SV z#w!!h94G@WM@z#5)MXQAh4i?JhoH>1(}s?IkQKb~tv0H!SERK{NU))D-| zF2F+TI2@(bq8qk-_q}+`u*!qv7-4FIsMi~M)tUO`{R!PYl@jF-_K)4I=E3JC5*p}G zhnZ2m@aOy&Tql-}M4OUK7ST%MUVEGYcAz>PPrqS<&f}tIwky#!BIQjjg&^({PmoiN zStDHOBrWH2%p}Iuu(?qQ&7^d!KL~t> zb%~S(B}8Cs9Ui(s@!rv_L_guWBB7o_3fiIq-`ALG{~vFJ(Hmi^C(x~|*)5jTBF9Ead1z0VCsw7p1{a z!%;w3HEUdLM5U-#`K?F#A;>QmUwv`c;>+%Y-ZwvkX>M3BYW$j;W#Yf}LPXGkiCwp{|De%p;H1~O-7UY8WGC@c0;dL9EaOOmZPU0JEk-2A89m-pW zOK=;CwZDG+4)$GRdxL;LOQPed3@{Lcy~hdn5E4x-?ew#wX^IbLSCWBsAx^EkPKnC+ zRVjkFxIwMZ;GE76&)>|iG-z1TkjBWyXkAg{4-c2mS2VMjE)joxwc&}jA(}s7iACrO z3CB#L`NPlMC7hj-cAIr&c=IADV;QC#B%ZAh<41mEK~mC{7|@X>a$Ou$03w@JTMn?E zvsN0TD|h&QC^81^lT$(=O>^9F5LTe=t_h2At4MKZi|kXgk9o$F&YuA87y~I2bk7GnvdjV~-c*nL@){_Sa8wCt_VN zn%%K8%hqS`#KRi1q9Qj7jTwD}6v+z-p>|+>i0V_iq~80@XINmlkhlavO}(;Ram$Sq z?+<>|Z*ANSL%5a&60l(!bGU7V+_nA;DNBy6fLn-XNQr)ZhO3Te9^E-?B!8*3yto~$ zyWMO*DjYwreSFF5Iez^+_MeNT?^FVY_6KzTX*>epsbKaFt`>%WMmKhUvMaag1PpqB-R5cg1mA+F5;ZUq8vkOWB^NEi#4#Xo5ZV z)8KmC_Ufj=X3~%9$MV2Fb6T@?nTq7HY4XB)dp=C2zR>HvnO-kyDt(9qmr$>UhM4 z(sQ!rbp(6y2uHT=6qK_5Lt^xVa;otXUpgJd_Iu5!%;)(cHX_@HZQUmFP>c1J$LAL- z-WEcKBfOArVBgv+O+``svQL}t~>Y==DW$XJeHDDtdJGJ5zVro5^4bEJb z%3JBK{_CJ^y}_s&=l%k!>I;rBSA@Y zzp6D0WW!<>Vy>tmj=!Is-TQH@){REr_EUki>87q3J_OE5{&;`24FmHpn1AkRblC1v zQi^*D!uRK5NNM^oaso&UW_kI19l(#Z()7~mTG9L{W|$)gC@G>D?Yd{XCNg%>Pdl-c z2#g!ScbFm1=p0I5gc&v!jG@--F(RF?WJEC(_SJ%(Q5N&NAgEB}eD0!kF&PoXTp9%p zA*P3r?7E}v%ISoPASTdBhxnPr=X5{XHZ*@J)JY~%M{lQ`Bp4SCko1&(wZ$0GErt3e z%pO&3@}XC_{(Hs8vPeo~J<=ZCSl*5=F_NqZzpRC}de6|ZA1-ZMiFCdr_94%)NJTR1 zuz!S+XaB%~AVrgHbxU+Q7}=i;&$HfcnAn*czU9qmBfxJc=6hIX3rzrNy&C&*y6#78 zpWw%L)+l&34x!ly^i=Q~+kR0*#N`6BXD=>2FH-sj)Ba}GS;?`~HDizA*|he3Jo1ld zqZy~0T;7Arf$6+?Os9DWf%E)$v?LMscp4;$`dWXEhqq!+;NOoqelB!5lMga5OZGIF z4&!O0F8l2i6GX{F(DuDmz-ZUZKy*Jf*WybMe*mR7Cx(E45R1WOcxzks7EGWA1%7mw zCo85jsRP~~KWCZR|MFGj%*tmhK z8K>hQ!xC8}u0%EnCq9fQsBwYPY>(LMJW=u00*S7Ztw$g`mk9Kg`CXyDDE*Igym*u# z!M?x^rXH$%UBOh*np}a;;Ge*gKMmR$864M{ItXd_ZD~gO&c~a|lu7omK)3Xwei4Ys;P>N)=+lGhWh9JBcLpuu7F^;1BYX6E&df(# z;^fidEU+E-@w*|yos8S1iqyUgJvNNiu%~;4W&`!fvGt;_*+#DhQjfsZU z5Z2RnH1li6zsHT&Z(JZ)sCP~$J3c9!3+kdvU#cJKq#O&ok`=Nxa`5(pCh6WisZh-N zZTS|T->QzoQQ6OtW=VfilRN4&FZI|xmrv^I0{A4pzZpBSr3<-6;(dt8p&OXjS6pj9 z2Zg9HK_vOUp32B+8`WdFQbb=UfDyYpj7ay95rQ!z*`tKM+_kmgUiYg`Vm4a1+UZ+` zxSMvAJv17QLTfQhpbD0!?{&?Fp+$Jov%2{!c`*rb3L7RHaa}tJq}6sIr4jF$G);Fw zbpBGMz3Ej#OJ9TX)I5VcKX`9|lXSNBISh%#0O7Q9!Q3X&YK>4UKvUM5k66(7gA59h zHZtG6Yy_Lgp8yH10GrVg`wx}eMROY}Uu#(>``!2spo#I2`!g_$z?x@>>dNi+({D~N zX`uGXH?ndXI5lLu>G3M%yOrho6sfp~9n6oO>dq@;iE1d>Tt9gkr7Vl`qZ&#gRW7Ra zI(?b(d0p8SY~$ddf`N!R!T-ke9uDxx|Jf|2eM>)szM*VfZKrhbD>u|SY5__AkG3AV zVhl@cY0FAo$W5QCvJG#m(2`=redg`+M526{#!)O&Ii@Ul<;iNGDad!}n*9!m@?%T+ zSC?ren}p>@5T-9Lb5cTkdIM?+rmeLaBmAVaFUTzkX87v+A%>&+_@1P?G-6%qxWPs0 zi12YiNd99@-}CQxp_^BPx;>8k>&-qRXb%BmJYj}SwNR%(5>+Hhrk4CA?BIKOp@Jx$syLm6pX zU5kk4#R4QNnf{XwP2(){qqR-~etPpvw;i=&*Rrmf9d=6d=QefJ*H+BL!2?QJ8*U_Y zin`1otxG2b?js2*-GSJSOg8RvT<-MTOE+QIxC*d^exdndi|6HaB1&Z*?d0^$+Sk>X zA6Y`%NiLSdG{l`opy_?2Hi~5Th@HI0l0(^bUKW|g!iP)KQ^B-Wr4j2tzdu9H9-+?{ zgI%v=d;NGu>pMDNrGUn+h7HYNT8Jb2Vy~0ysW0gOT_43o%Rz~fBqp7iptUb$%Iz@Q z_%wp9*d7W(EgI{LEp=;{Ecp|=(a>Z_yuyZAt_`w8kV*Q~dbBna+;5_rAFgVAT+UPd z@wdkTH>2Q7f7gyOwx!I#Jxig5F<#Hr=9BREJM)P7VX@7Az4mW0A{Qze-*5&c4MLno ze3lSvhhK&GOb@x*&9AnH1M%hjV#iA3o)J=cvx%iR#+;d?F8vGTGM#=pcJQY7x14PR zbE*UtH4#zBm8BEe&itr}BE0z-qAgUT|2t(U&2bmo?1!D)2*OyOA9ETq@?4hyyD~iP zQ;O@F5X|A0mTQwnF7>3LJ~~;H$|=5Q%Dn$r zwwnmO;2zw12<59^j%^jQj*77)ark9?aqGp)*k&R*wxs!t$4C>fRZ#*KbL74 z@gVqVY{V&RKFp5-w7xbV_K8-DPCz(2XSp{qgv!s5&F)Ia^>Pxzo2;)yv#M;#9PB#x z_q*$Nk3{BRWS~e3qMhwh;z{|PW=WHBcm84xDznR4@-uA+0~P+Q)}Kh4M5lV78P=bh z!R;S6Z%|r4lDE#G=>IT~I^oOcX8lmr!qdl~YbzeceO3>r&L5+H*^eTMR0AbR9kn4w z^7Ar~t*q`{UBq!F_$MpyVrT=0FCvOxH`Dk^z*CkX*)m5 zBB2)IZ{y+Wi?>%?&v3(%(9$ImnpAR0Oh}GuLV;jl+Wm%F%S>VCD5R;0aJ?T^Jk-@w zmdicO`Ckk;ySgZmlb|P*%Dg-{tmn17QOv$wvVnhduT^Bjm0Y5*^;-$j3_Y*34s=MTyX);$n1ueVQs8;-~^8oYKC!L z0vkxDKErTH@UUYsGw!a&lN$`=dX3=+YodtwV~e@`0icUC4kY}6gJ{X|ZP(jC=-FEw zAHyojRbRr09Yz==wQ6Y!69QkN|~1R28vlwQQ3d?5EFoY%%3jl&%hI z%{$|V>|<7nMmRy;65`rZzelu%9^x$)bQ zn${-|_NoI_)mvLK=*^?vb}#-4#Mf-yIlqnY0@owuwBqi+uxII68-F?? zRE0C(4ZLzeTFVYulPemfT%sTpGqE(B=7nRQKo|Xp!ue}V7bvOZn!HP76LR=OF+GBojeV~wjagtmgG47}Y*M`L(9O)=3}$<* zD5P1$^QF5Og75%2LBO5{JsS0UZymM(D~yInFn@Eg6?jt;15GjK&yQ=fj^^M7Kl&>u z@CECr@^vDofe?4DCp{2=S`ai{HFm~ii0YKMn+R$=AzwGn4(8Ef3V1!qbnYn1bS0Sa0(Ys4qc z{&@Om#P61Lm;E!W$UEEc`>#zLW8v2&iP0`GRV?3mFKlL>Eemi3Gz#6;{+p(vr{iaarpT`&Ia4r!3Xy`4qbAoUMMN+-ye4@gQ!f5kkj$I>_l85_f_ zwO@>M!B=S5ll&;Pao8RWDD7wwYv{$%}IBe*+SmO5ba}HS8 zk=YGP(gnFbjn*SbO*Jpo1_B;+26I+OvOXhZf{K&ZA}u|(H5o2I8xs z0GC9ak1;jXYrpne#54$0Gh}T&Eat_@*t?&|y+6O&BmOWCVm((qGd3LPX&{%0@Nn01 zOIhZ)Z{KyDR5*X%k($NZu?dBd(eeU~l}7c-SEdzo1|F-PxdbP(j%0T) z^mj%YxVk-7%z4d{y(zWNEt~4I4jbx9M)Jv0r5}{>XtcJwQHQO^dtarR7isq{%9U|6)e~GMS2{@P>QER9`OwfGdJ9nk=z|u~E_jF1)z;`{su@0gk zJ{G%*{=U~>gWMmS7{3^?t7!BN`)d=~>J!DeV=B*G?f7;}@9?_n<~8m-VjtN@HQhPa zrbm@tXB46+KCms3PYv0wHyY<);k-Z8u5@7A_zW!~1^SQ(NnKvt`?n|c{a*WBc{$w? zV45{Y197qCxV!sQ;17!s$GUc$cMDuef^sA3wH%OV4ubh)Q9fOSK zq>6SyP&e%DH zU)ZwZfokQP`ZLKGZs)lNOg`ki{bq!ygTeSnNN`dNd&3+-TA?jTs`l>` z6Wn77Y}Z;|3ki3thn-)nyYkoIxY;~@FGXj{ja@V91$g7|&AIi&>;;^vT>@DdpLlu+ zlRfr&KW14C^WXONfAfgH$M~G&4GD`&S9a~@ynMuTILJCFL1At=R9WFjQF<8uu=$PA z+p$LSezv~z=j?S^e-+P^iso*ZS_WPD9G|}93azQ*^Nr0<$5S`y_Lfm*CgJCYs>15P z7x~I>qiwUyqmy+1NDe1u=xC=6-wGJD4^oXEAYehxi#JTFtWMjY+_4S{i0euhtk>4& z?5dFKhH5K}bg`@)SIWwt1drGF_Sa+us}5>8^$X(E1+{y13np0NBA7`3dY=HP*2;r^ z%X!Zy|BoYTz>z}B)ClmO#YC1VNp>F~H zFb@H6N)EN>RR6KoI7L%t&FJR4<+1S!Mvgl~m8xe8>>$KC#7mJGG*k z8Qu9%aCnmibJ*R^P(N@i&pLt{CqVHM!Z8%ieu3H1VQG|9nZgL0)VwHUNLt8w0TZEh zB^T6V$z&lUUBNlRWaFHmsMVMTiiMcQ=L8LFeipp z?(#*TWe$I_8fmX&yqZ?++Mc6hqVtbs{}+PH^fZ-CeE4DGI;rUK=m(Ps!F1THckTXUll*AQocV)mQ0Dx z9M~em7O19dhQRZ2vqQyJ2UnZa)uGxlcnI;UWLUNS`Dpj{ONJp;ucia+>x`pD_7Z8Y zu0wxs!z=|unN1o$^$XpTFxNN}#7DBJbM}vt;U=31FKr*cySJ4t% z2XnGP2%3h*sD8aFW(oSSH%Q0N@qYk}Ky$y=lLIZjb26+_+ZuOK0Ln$=vdy6ue=>|z zW8CeuibNG|qp`e`VWc!{sT#{W9Y(7B(bZVqlQUA$&h$t1UJ+X2HX6%285TuN0T z1XVfui#QQyAlLI}7#_K~cl~;|-Y!rds5WvXoBjr>`^^TC$AN630lFtw6YT&ZjpdyT z&(v?U2BQMXDcfj-kem!x)NgjW&7$4YUf#*@J*Ae%Z8Vm5GJH?J)f~49q#4;pV|gdT z_sHO-!v;8YPlxZ}xVpo}-gTdd?@4?HGL!Hf+Z8+n2>PC5TU(mQ*kTOK-_k;gU!2VY ze}Ccbg54))-J-iQXxAG>*pb_4EbnBrXXTa$QlXBCxO+N`*T8I!ijY6I(OBN;FkXZ4 za9AMp$~GF&t;b=!9w}m7%sIpWRMc01}FhppLjiHZ@HjP`a#mWHoVa!$OK z?RqSJzq=Z%cN}i&;J4-|em1@1@LC67{88Lg`p4nK4v#O7;l$EEMMmziyhHjY&ITUU zX4?Nk7{sHn|h5mR@UsI8pJl5#tR+mV zz+#}}x_nA2wOtEF zlMlIb^z^HwUKU*W4UTqiC0}o3bqX|`vW*Vx55t-K#$kVy426vYC5=BE>@_1?PkYZa zQdaBi*IH)topv=*H)*)k8lcVE46g|mGp)!G1Q5NYV1~3DY25)W8kvgiX$ClmRVeR*EB~3uB@_+&Kh^(L+;F> zBXET;K%Rh?!C}I>>*!{`SN6l?cqtG5dcFsLUC)Dmx57)04Ykrq6(0*~CzFm>rUE@| zy3od+rJiIH#}3jB-ZTzcXXq$CU3->++Pd_E@)t*1`q7f$#wNCUJrQSkKp(Rb7Ek@i zp#znbdJ2FH8dj^wbwsw&0mx2#+npUcLcO1vK`8tRimHzilow44$6L&X0>IvQ6wyDg ziqru$QSxc%%{QJ6y)g7<@ngzNK9zRqM!o(Yx2dvnlquYL5qiPg9KkwmOL)8j)0t1B z{3`Q>I|>(OXnSsD39p}>O#!U1Ju!xaxsdt-NA>MdM4y+w9YVv(%(I|>$FGFL_$rx0 zI9lF3i1#52ggLuAUCpP$HA9im0+rs;95(vvj(&MNdbhEk*nJti-Dq?==v7*XnG-!7 zy~05Gb#I7DMeE+~o@F>5G`|{6O}`RMNnorzn9c%*(eT~6+Ym!?XS6j{Z2185** z-2ul9(bZ6>-EFXCxdU!6o6>-)fj2i;o1F$+m;W2t^1lSfR@wfLS@7;QBRr3}v{v0} zuK64uacO#Jf8V7^=k>=~+P?Io)$I2AT30}ifMkrw)eJO7+>r$EE_G$nkqqC|Z9CMR zYdMy#ZQz!#cl0~G+l==*(CW9_+P)09=ux+Q;L`352W|1Cz-3<|-0HW4PDtY2X4Fzy zX+n9PXKzHY^LHK#uCXC|LVPo>=rHSlyantm4(_ml-*meuL7mLzX^VtUx>I`OzBGcX zcl*}hG1sx16uJYVKJq$tV@-<|+?)1hu({@?glo6r6YAO9IfQ_q8r# z$4=oIBkJ)5TW^!bh&PO-4`)<=C^sw{MtnPNV>AiW_ z`7jruF&{l*q&pvN75}EcneY@>GinBe4d{7*mQk7{BpJjF+Ao%_f!cNog>X` zY(QpIvaiO7``Yoe>F=h0n)Zmp zw$N~dyxVjD(;bn`s~RKf(TD?Z5u0vGx+7|q5W3rRqtPC5XlijL9X*VhRZK;%+|FHR zUxSoxX_mXrz6Pmwx82pr({T+SwcD=+chP z-Z)1yd*fYqU!2ovj`~`dyt^;X(dPEM`{JBV*BT6T48+}iaZab-?+G2wxVtaT(dK=- z`{JC=u+<*vV7czTIHzL`o0isy`{JC=xKY=sYkk+!9kGmReOKRD@;gkKytJ%*sx_vc0VOp&`d*e}7voDqCHoBdr z4rkiawsIawitlNgC3g&E)tHjDg>lC`LK;)jwjpYV!wajUCCl`*>CrAZ8Mj`$Lo2y@ z+N4T5J%nbJx4XVJz0seNUQM^VzBbX&uM)3U6!EUFjrq%+L;Czll=gjX&zYT8v=#P` zy4J(eoqcWX8Rg%KAn??-_WsccsBX!!yZ+G$sBWC_UH|9=R9mIfKQIAJe0KCOwtX(t zap`#f=yY7Sdg9&S=ybeiS#7Q8r@_9ewr<;Mb+ks@SJl?-44N$+#$d3w5M{PULmgta zS#e%>M9~pi3e3$~@46%EvRj)~)-^}$HO8i?bE7vaqw9{??De#aUz>H#bw_Lo)6!bw zzUqD5Od@v~wZwH7*|9`V$7+&M8C-WrT}~vU&UX>+-7yS6XHMGu{Ugk2U)0s>4}mOd z4Y@Dwl6ACohTI!@>GGW!<(-Q*doAyb&RBcnEvqrC>rlLm;>5Z`vVK)3_;+8#rCUnu zE~BKZ?vR@GweB)1sp<~d9d<@qM|JN2-G0B*ZR%*aGAd*04ylxFx2#(Qqf(WMi>znhit!Sq2MPMD%p zHXw>39m7L>ivT5e*YcK?ST4ty(?!&TKKB+O%J6Hcw&)ef_Tdzc*<>1LA#rmW zg{yg1o4FU*m~#jK_8$$-a3mqHmY5(rnCS+yV9C#k=ha(_P#SXugsDXWNX^X(-m`9KD^|>$8qt-DJ&s-DsPq zn#wpGVm6Q~M5i-#gA365?*_kg!;d$M5FOAf{F3t?gHEYOO9B(PC$`BxlpM;o1JRV$SDXxn6ji z2X&1-Y3{ry|KtCD!F$rv-IU&=?n-OtUHR|-?~C1)hSrue9&47h zcQrNXN%L{(30ju_22qzTL1`X_pd?o$IAHyVR}qd#b)6Nxpt8o@CMJ^%S1#Dnb`Kbwy8`&!~LHnGf5Ja5(;gG#+ysSoa& z^{ZC*stpWPvoq+{8~rYQLd|~9UipDQS39^vaJk^vJpaSbKmWd#{VE3rokkH1n$Y5I zDvUskaE*a6wI@>t-MoZlzgrf?1niSEqrsdsv2)R z?;iHcFhgVIEett!?fF|X4FAr!x=_0avr#)bPu$lC*3VN7i+1rg6!ReU#YTq}0GiI$ z)`~|p%FnTT_B_;Dk!O4qjxYnEH;Ab<2<9s`-Xl!l92q~c7na_Dp4Nb4$KpgkMv})wS1~%y?H0&S+O~86y;b%Mz zz*kd=jHc!T%-eWvhd(Zch_lcimS!AyFy9#<)EV3ZasstY-ws#C(hjG_#11dzSC`cM zEp+5ojXy%4GjoO55D6wC#tATjc8I^0o_`M@KwS7@HCdv6<)@$V->~Z6nZPuC3*8bN z_%8!pJB^>osXe$fhKrCVJ_Tq2mcl_o5dc!I8VnDD!NwZzT|m=Q_&-cx1p71w9%*GT zGlz3Y?qhW;KHhpB2DztY&@H$aJvg2OHQ-eYZ}?z)i@;fxz9&7ncybr#$RGMP5KOh7 z@Zirt!*E|k5a9YlAZxD>g#fW4F4gl_#`)%GrtujVkozlk@DFTsZa$OGran3*Ps4B? zT-9o@Wz)s5I`U>U>bNEXb8c7uY(AL4zD$q;LX!;M|Hci4?=ts~{mLc}Hp~im>}1oQ z_!+iLe)R^wDh7|fE2#|@S2b^Pb9g9^(hs=1Ny^VRUf2|Zi)#mpsaWQpUv$sd=;B?i)ipL=u(HaOX1H?MD5VMt1;$Pq)+BMT}}!n!UTm;HuTR zst>AzR=3}3HJZJI7?mRw)eD6pXmLFX7kp=d0?&o7d+N%KlBPguYBQ3^G>~_@>iLrz zY9@jjau*n`jr>VvKABf=zszvqi=a9UXU;bh;R@6SKpV3Hn^>8fwttpI=$;>KxR|0G z1$1~6D|i$UUj`u}FJAz`o1u7YRkH`pk~E7h39w@A`k-50uxMz>hA@JSJl--n{XK=* z^!+N5Wnl(YDm4yTAa z9Q$4iW;(JiOquy+Mt=!(CE4k7m>S&LsW}f6hq%$`(y+(G4%onGxkU1C1-POB#vxpc z=y0}hLL2VN8mk#xXz)1=knz5Ue&NpGH~|O>rZERHKcD(2=Hoap6_~VdKOlv`!AUkj zK8yAx(Heog1kQ%^Hqhl|Bv}FOG>s3?67DQYDlS}Lf6HaqF7^S<{d;3FpBR-2>~^Ib zoowXN$u9Ky>a&SB=PJ<|N={cH%TM$>p@Kx$D|(iKB)>_0iXnfCnY*=h^_r%Z_`vs; zYdr`$=-a+;s9bqhlp#X~1A{Du%E?^jqq!3(@iYAB{I$FI@cP1NRNK`?_gmwIH(&Xv zoiol~T%d}eW&Gd^0sP-MTq1Y-0|vwr8n`rW+)*jU4F|iM+Js2Q*jpfbyQ15H-O^)Z zU#2FY0XmqzuxtbXh^hjFUX79P+`>@r-Wxw(fBbm;?(UD5N?wCc5Rt&mo!KEWw!@X- zn9H)A=Q3&%rdNh#WB4Poy#KW}g_~5nUoLCaiMyCjB=(SOuf|SY5jQ>Z+_61bupZtR z+iEP&g)n|H6T4CdSD+7}Ll`-trQ3USXUv;nVJ3!mg5+32&Sg6m7dV?z%Nmsfjb}mNtctwgoTn?d9JhH*mTR0X1 z0LYeIPK?jmMihbT#xv^E@&v0S#pgL7(p|g6{Kmg?!f)N*O~P+uLlKX`T@zY!4a;1W z21xEGOCzIqI2QMZ>3){g{mgU2>0$a`W%a)jreC`IKfu>;%;+p2#|fvl4qQZOj^q!C z(cvDv1sd1v8rO3lPHwYd)O%O0{#Cnee0XcTdU;p2*PDO|(8%+VF2kf&wRaar*;y6R zeAGCTO3Os{-ZySuBDZ0qa{^u(;<^E@-!kK`7%3o}tSS%ax%{&1nn;_E-+dIOW4wbm zm%zKRltfs1X4;N%ZCO6*Phlqs0^~Tb>si4BYQOu}g%^JNqp)4y26+H~2;=-EO#LPN z9SZ-#z~eNP4W|ES0TKfIYI**+Y6(1L+{K|_Ps_&#S4PUhRd$gKCzIiX#2@;oE~`uE z;vXmY4prP`JChr5W!xr|Z76uLT&F>lBuyd(E$rK;+gBeeH!lycb!ju;dG?=c^0a9+=qq`-^*XRXhJL7YiG1u%`n{c{e}A;w@ZHkg-GL z!dA=y04GOuH$lN_>*CwmBNFkFPx6J+T-kMiW>o`M4QvR`cIZ6-EK(A@%~TOZqEYCq zKtB4{1z<{xjd`~8tN;Y+j$lo|HkTsD-F)pc&J5i*P*y}O4B{5j*_gpSTRJ=-K@WoA zIP=IT>%$xX7Z^UY1~f%{5}{j3LS@a!-b>X&S_ISkTeN>gYlIU{a_-FYqZ|Hr z#(9C7whIfulh<$Zhcx%3da;~r*V?K?_)qxHS6C-u-DuW}fqhVN2vmjC%a?{Fg1H?^ z`eDK~na#r>i#S{o4GLl4fC-OsNn!Ht<{KlMuvXheeGL+fl(pNOk)O`Fs7|Vv1}hIk zKxeRxi#fZ^z+r?LA(#k47f4}1*oS5_VY&g&B_Q`er=&BanJu`*Il|fV zP2VO1WN6^o3@;4yxSnG}R8ApbOi!LPs1jWn?_<{oHQ$LRy&i_>(Irj2M0}TKISpdi{MRdN;!+uE2#BPLgXerszEpXZx4$|5Om|AW|WpFO`tS|<}QV%BohsrUVmUY zLi(!&iy@u`^rAw=0==S3Sip>LMHKmdr|`PSUK~5&)}FshZdYW-W|6@+@IbSk74@OZ z02f0iyWonUL-D!%*bJoCCVb03QchyZNrcc)~`1PREixX_wgm+RaVT3T;mblDUUJTb+4BK(3T;W z6LcDwuwO9>@XWj(NUKvm~>E!YXg@&sjVboA%rJzObsPe`d|E*S?HERI~c`#m3 z{X8q9e$3YhHJZuMA}>odBB?vDX#~LtDWJn;84QVmIgzilX`zNp#^~TNpd+xkE;cz6 z_A^oFM+yQ{0sKx@7S#bU4scfRwQbQW!2CRUF9>|;lTOM5X+k1ouZ7~91=ObwLPmOi zrGhU8o9ff1w36^rcmeQdjsM=aSKLc%hkdaZbtudk4Ul}b?=W-ev$IcM?Lt>C_~=hB zFkdtXqp_!@@nD;H9t22Wt2y3(Ar-s2u6xbxf_>IxQNIoNKt@iGS_j1+yfmqZ6F8k5 z)Sx+s)L`r?g{~_9vhQZqcG)fZQ0uf8Tp^C#1DLH{bD&6MXo?~%SOYkk+HjH8OG@QW zs44zBNPb>M94xxy&4lMQ9dh4843(N?{NuYUQ%t3Uqo;rjOW!;c@Y zZ?k8+*8Sc%b*GI{pXyw|X1f4w^;k#4Y*4gi%4?W3d?*1cZ(-u}02toLCS4ZsSDRe= z`OP@ido2PL-V^bOk)JV1c8s6_Z%uMHt_2&1`j$)NvJ-TjlzWy%xfy`OrUU=91-(in=7i7VnzE>Ecsd8rAfIHh+a+Cd;0+kOyApF8B|&sj?#eXr0eifvI> zr~IWZc?U_Mh=BpEK*<2+4XE5vHq;VPC%up3g! z;|XI?R&txXd^WeJQ3Etm^P@nxm|)}q2SS@AyVlxnt_Z}T36=@Z{ZwS?sg-XN|8p(*kMW%fRr>VoEeR}e&xq$OoakZT>I zQ#6hH*T4Kb>yZBXm;ar<;-PeuMg0=0t>%trTAQmYP+tNaK92&EfNYI1@*4@((2-^> zsh*G*TiVkiBFoA-L}IH*Wm?q@?``HAn(?5Z3(lvHZ-PkOlLR^jQj|H*jX{CzTmmQE z&*5&*J=JCs73~aZE*O-nd%GA_hX))!xG}G+1>pT)5!^?ITBDYSy4S>nz|v zF(XLANLWhUmfvHqxu1ovxxa<4xxa<4xu=D%br1_*>vSx9$g!$zjc%Q>?KnncD;7J0b#w1H}|-D_tUiL?tLdG0>c_Pk<-sDR9JbJa$MHbF`BI zQ6}bfES9n%kXLu)EG%W=@O*oMxfhJLH+P8<*(dWQl8B!{Qb8=eGy|S{GM5Y4K8EDf zw)9k5mue{&r&e3$r6QUjfX^cL298&4Rw?t4;v){>Qy`UHajF+))@d9mn#Iu|a$E|p zqcsw6)nmmtSB+QXbE9TEqrk%<8j_Uc*Q_?G^=eZN5Q)sqY7-vU<>P!N1Lh9jDZw%& z^yPoqLHq1`fGrANYj)^(8Zf`g#}^Gb73oI0M4pitC};~zj9NHwZI+Gs0|H`bB902T za0oy#Fb5B!yo;1=mblv(x{=b+3?k`K#tf5tHvB0q?j%LZ-^MpZE+25B&h4tG(iJfN z>4OOzElj>9`>x53VCw1V0T!BjGZK!@fxllAS&eZZ;FwZ70pnC|-(7#W{qg-B!e(yz z;YDGF3D{dYLb{TCf#s;rYM<|LMXGotTMA*f`3JdKBN~t|KHdFL>GRL;N~@Rpk$RU> z3&C)1Ap1i(Doc#|@cwPBO=q8{LEl_`4(#-I%OxQE7JbpI*BiBedi&-Ux|o?2RMeo` zp!z*|{`1QUc!8y0bfFHTHc!>1#eMd>)gJLHoiCqD$H8=FK z4~K&>oS1tPsOJPb`?Kjw!&lVAU29n2LJ;c^_n!35D#d)H(nHQ0U93kioLk^Yi(Dk< zu9UdOLG~{+q6kjr982rgF$Ip+{`KyUGhO^4EU*+-USRD8Shl^M&3m|*z-XU^M1fh@cyt)=^R5Wfi<~Md~4xV%Q(;p zCRb_|e00cj6@SEhD%(0J0FpPiP#i~v$+Z)Bm#Fwau8Gq*ZJ13|OpdASU7TPARvT$dGv!>3{BjmNo??3!;uLsDe5-yw3qI_l}Tg@ zr@vQ?Z;pUGK$J^#K8W0D`aho<0{9tokiDhi8r1v2}< zVX(Ig9e9~aEX8nK8XUSQ&>2a^bW43?oKg;-#+p!!FSkb|;|(#D!pwV6pKfR(Js(B8 zi%U5NY^Npw!oE<3b`pOr&b7iCg=BaRo~tM?9LzN=l{&ALkB?f-BE}Q-9{^J&{zk__ zq69|tu`3p?2eUUvs%8rq^OT-e#kO*8=kzudPDS??we|0CJdCb;S1aHCgjf1bdia>( zr9nPvhHy!*q5@#0h$r!<0!$e$s8fmyN<$-eHTg~jxEeUkaAff`FkglIb)5IU)bT6` zk#9u`ne>HGq+Z`lh4cPFan$Pk0^*ak7hv|}<$?ncN5EP*a(z@^29!G$sL0a;P; zhAL(&njT=cTiZi3Zh<3=JOL4N(Av(^yc{~pW1L7_y#k)dSV-xtn$lJ9e^W?SLN3Cy z=-njZ0oOT9-pUx+Aw4LU%@MtsQzNq!-zF&@ZojBBtNDkbn^7F5qV!iZF+2+U?@FgF zU7W5A>3K@Y$1+bfvxxkNE{?Hbx4bkyzW9cG%+MK1B_B}xD4klzV?{`zae{=#ICju`~F zY21i-n9LT5g(NCF#GNuXVw~TgmD>-=(L1p5zTq}U3$n*@+hbEWcZ`?UBadw~jXT7y z(Zx50=IDNj$)3;;9F{h;9Za8qxya!IR4(}BWsDMI$b23qLNVA=AFwu-n}I7iAvb7Q zbOl9~<Hm4hi8ksm)8oMg zPC*d^_-SsLGN}MBQi*1JF@d>v2Jq=C%MO!*;N?1Z%SA_6PZO~q7&#&lX+q}`NrTBs zTFUPxVGjoZF;bN0o`NLnZHLSN%AtT@rag&}S`(X>B+FSIMM!7M8-&OwirGhWVOFwl za>4JS1)EOj0d_$vwk?UN9l4jgv36Lg33V@bZ|ennuxDxqlZMb-=mNX_jqyJA_+oi= zY(98Cdh-z*j;aD8S%;gd#6xPf`}P&%71->;1%JPo%_FP=8FjLam)`i}4WFJ2XgGGrluOH7caoZT-Y1DBTHf1^O#+lYP6xZImL!QfEhT;|@q zg4qf=5R{`IL$MJRD#N`JAELXONhO%&ca|6D=(SzU7)KsmGF_!qK*1KvU@~J0Kwv)M z1-g|wQRyRB=JFbufrwcwH0qfuc9m-nMu~-4VZ|k)y!${iSwgF^J-n&J=UDQI3V^`e zhK`uUY5y2n6~nRvbLbEUHj&k+%69z9RWYx;4_Bq?IFvaL-wVnnKaJyglR7mS49Va| z<3u7r9N)!;*!lMC*vX1f=g@0K>U}Oq>W|q}&ijPgRsxTb%K@kreiD`SOk_6~tlX{) zz0d3s`0v-M>OGOb2$=xKN%w3C!Y;9OQV}?V!$KxR8y(Zj6_P9g*J4YzRHwmxK}bqB zNY6E9AV2u_uLuhQAKug$x~Ztbdm-j10j!tXLS4pUGUX^^%vwELt#!jil9R+H0EU*$ zPx+ibS|(Q;6}-}QYt`FZb1VvbTT4_|47liGq0<+|&c@UUwKA5}_QF}fMXMb3e@Nx1 zPwe%9y#9d){V5$#%e1qMKw9J%fb79d6y6@|)KrK_?3{~ZTQ<;kkf)-cUZtNzN{*&c zSz#3ozOt7n6j*jaElo@qpgbm#ouoC9Qd|Z&Vbl>h{lu~Bm|9&(`A&H$=-TZ791(Z& zP+lrU(=rln_zs^WJiv>;?MCMF$YX*@R=koOSAogv9(xV6M=Kd)Y>&sVUzl%+GPq{M zX{&U#@>Fmim|T-`0F$@%5TL=K@5-~BAIZq`wX)VSggU`eu~R}ix=9{7GjUL(Ovre* zg@c-L%fjPIX)4HMswU010Ee^vi~*As11JjAIDfD5O(j$|rUN9*lAz*D4@)=I50(mz zI>VQ$!k5%PMY?>niObB&h|%$)(W+gK3m}qRR+WneK9YLf*!ku?A>Xgpn}-{&h6i;ir2Cwbm`c%(f)%L}hbfH`Q~7)f3IYbrh;y3j2&*t;AOi_7pKj{QcvlPi z7$SpJHPY-%AjJA?O7XfX%kuZHUf;Z5{8qCWoc!f5yV^%UmEB)pVbeVinVxkRLyeLYk_D`E7@L_!I~o3Vjy z>6a`X*UERVLaJ2u>c^B@T~U+DRisMSs>`Z;vg1sy^5)9e-ZHm!ONdxP!||YfU)%X= z6@mx(Hv>UJCWzc3a9?GX^w=qYwEY+WPZH`LDXIGdDPZ14V5g9;+l6b?d$P(|V_-Cz zSIzEK2cA%sGr2cX&yZ9z=#2RqtFy%WO^bSTWlG_eWZ@w6wF=`LHA!Y;DbrR-e`&&D zp-OiTR2$CDrF3IYZF8Jm95=9B?i9Gk!uLJ@ir9e|kXQ~^vx8B@VyxZA68Hr5Q90~o zig@p_7dfhw+}UBZy|*9Uf4sYXcW1nNfA=aCNMt1Q(<#vhy(ZY$vDHm{%_bo23JE4q zbvW_J<$$hgBAGdzgD87YWq4hKA|gW2~ILZNOzS1%)xBCDNe4+R_a!`Vc}qj z7;><&(KGQpRyUy87{$QIc@hW^XBm#?S}hgdB4ioo)`xbL2v;wrf#- z$MqQ`yoeU)0l?&Ph$UwT1B2)(D`aA%%Zs001(YjfDL8P1Sc%wNZ$xmixiG16B9Mi` zJ>}lLCU3ESC`!BX{>;fEs+!{J-lJDnq75sbv7TAv8j2oEEN4bR0o$CQi_EoKqTli_ z_1W`v^FHfJ3rE@vLMW*uEvSB#i09lg?n zLh(Djv*(=gk+eR_8{vF04?^g2b|H0(cx(fkf)=7M6J5O6uOHY?dr)K)&UX`F=xm&$ z-%8*tF8&aKvqfW+*YEYV#3YnDI=MG0->tRybpheR5~Jj3Sw5r-J}Dmui*D|47}M0d znC8P6h(#rPm%?!7glQk89Fu~{MKzt2md^k6n>UKj3(DZOL6J5mPQ38-&K>X0{j=r; zqJl|TOevm}voO&y3U;`3Z!v1JNhMu~>xzz2{CmHjOsUk6zSRsu2^ z5{B4WP96J1nVX$XEb^qrFc9I;qnR{D-YEMVbjAiq=u+5Z8&3D^D2dxkzZna_-s+XB zju7P(O~u}Uls=PQ(Uup5$Drs~e4gc01ELhNo@=~=imat~t1R6tv2%P%VFI!QeH2ix zjI|rvKrug<`!s!}4CPi1{G2E4!QYje;~vgiboeYwxYlBJWn6RK3RWbTn8cNI7AE39 zAN^_$fTjkK(V{%t2RIVw3^21}$6FFl^{)#|_vyJot<$YH2CYU7oz^RMP=VuF8N$m7 zoa{ro(=D zv60C{{cbYO5Kasg6!F|rG&SF&gD|sq8G&YIu4MM#rRkIQ_ND}B3CtxD#R615>S5X|sUHPtAEUP}Fv_Bp&2gspJf4h+%XN+;+(Khvj_?NzS)6 z0K1qDdSvg=NZIIRvRZ-EglD}&E@CLAScGD@G8!rr=Nx@8fu>zhQ4aJHHiuL_-Lm46 z@;aAJPsr9dswlt{(<}37!+xSRn-l!t?tN$6PzJA|a6;qy-Ae<|5b4)36Z(4})*~y! zqrED{zaj@W3S)bebW}Y6-bMYCy8_gIsXTih5-Ayc;V6$V>1yB+DTk-96W$ofJI{w6 zt3)yZ*R`#w$ZqI{4w`h{f*nq}U$Qn@CGWtuGI107s`)_BC{JyxkoUT-UgKQhIn`AIv_8zv3!)TGmv&DW#J?SjE$?thmVXvNuj_g zEiCW5@4hP)X`gTUgxAmC686s*^P3cGxNawr*!(h$mppmQs|M#RU{}?`{zn^eQkcVo z=7zO_9eWfODI%elbA{k9jj1>1^cZZUASXjr@&#H&!2vTCy(p&rf*cS~1j07SCb1#Q zS?O}`{brid=C7i$t9H7?UVkXI#57r>Bk_58UpD-3^LVcx4Zb#I6ud=dT+}~)RpuU2 znY)!K-@_W;XcZ~a`WRnMm7msZqfzR;cjEH`bH&W&-^yfrFSrTSm3$>yN&U_v`xuA%ZNky3+WT*g{~`{ z7;*0oyX~#|ijND|jfKK^OvPQI!xAf^{Dqrnl@NI%O3wLKi4y@KV-2c9F-8>&mM7uT zHL?m?GQ&E9{T9>Ra3;#o+$?tS!ek`8m}?F!cqv?4SjwdZ*GB&M z<_Ji)yOG4|frN+|2vf2i{al?Z$+x2$u02|yJT9weL>|oX(4Sm$fsCU4YhvWclTe1O z#jGtN2aF|?0!vZK6qGz}Q|68++h-he1I!}(%;Z&*yeQ@2oA3M|WqFb@8gU9w7!GsG z=1qXlBRkZ^LW4{YT3R2`R(EVINohGu%TW;G5Fn@vWsw%bZB>c9547oj7_@qOt{uypC9zk7oPQEnR zzsa{3`WEJmd=alE1F;159=5B#c} zFwEr8z#+`S4&H?cXfO~0rf~BzB_K(x_c*AaSq7XDPE}PjVSzFoLZ#w(Bh$erzDDA` zLB_%hrvZbR{tSRxsbJ2_(X>MNT-ODwpWzsXHjGL|#!aQMUC{>S-0w{URyBiBOrpvp z#M?I8BTlkZ39ZDbDAznLQxUSbkDRE?)fOM9L>ZDf2PvzBu6PSjy>B74=LoB`uoZfo zi^3O_HfBriimH*2Wf+JF{wd1mm&I-=!Bz>c<--HBHj#ZXoxrF#>css+YPf$W)A5#+ z+ZBIV@hkX0qhZ`Hm-PQuyWW*CdJXvBRlT1+>WPwhn}_1ilgUCv?OrP}y+(8U^tPxi zGVfct{mEqwI&sM%O9Ug|9#XcNMA3E@sY)Fi&KYZzv#$2L`4y`RJ@5Nx&);|xsS4Ob zMeGGZHBd}4R>*uZZE!!<25(GiSBHv%{L_UBB%o)sqTdFHF$7W*#6{g{1Sddi+g(d| znTY8k*=^*YKa_`65dR&Hw{e4clN9H)m}BB)pL9SY57zBh%MVX`dB+sW=K^E4%G1tx zC=})_k>P}B3#B2lgE+QaRy76@(BeXjfhw6}u~LK)gvionapoYBd`;iMu(v>r*n_t0 zD!K!Vw)*$-KnFbXBAQc_%1A}u#jsEWGEyC&{Z}h(D<-y(Ou$e8Vj{soTStrB1@Lfud@f zA5)d0={!k+m2TFBy1>crWw2WSR{_HhywApY=wUu~w8+f)mLEpat}7g_9(D=Pdax&?PGY6n3oZ=+EYgtGS%qe)vAwom zh5AqYD)O>Ve5pzh;f*3c4Qbgx9phRyFc9_8A4R&dVCJU$QjIv|ALHb5W6q|i(r%|N zDM>2Di%zR$iz3f6!a3&N3{xnPw(-(HOEX}p65YTx4tA-0j31khhnZ8*`=az&!np4Q zzsq~fdRoNV!bGp@qJg#ZIpw3lK|<>ubILhfGHxn6!YkTC_Jd&VxnvV00U8%o=E`Gm z&Y#$#BA9QZVQOiW;|B&hzuSM z9ICA<-+Wu>;jQIeaKZH z1maAJYZM5hz)SS}LzLBr1u$h;<>UC)v!ZH+3&*Kg-V$q zQa)OlGfwHBw4z(pi_C)(3NRPbVxnv2xmqPQh^>BnUq5 z_AMWZluHOBwS>zlD1^R_DLAF-;|F1`uXoI-&|geeH2CLXR^iEZ=CT{Nt$Osv>TEj zqXV$#SC|995VwhM&ZlQa@G!ah{Ok-6*`PVFU4F27c2=oZ`^`>QJz3gTIDK~3skS=J zzIr$nD9t=OYgPx%UK5|v=)VH;v;=mlCT!zOQvAjTdxW{Xf?A`V_@~?E|F*i{cduW* zYILq&b_V@dox%0X>fD`;oq05r>jTHfxJ(#j$&yHtb%+^DhRVK-B{R$w8%EmdIlQ}5$E@{3hA zo~X{Z8&12w$~- zep|c0q^SG&@w11z_ru3NhA#Zw#oc33#sF9f<_gfaRL^IM_D5aiWME?7(*MA4-X>5_ zV??jBI{t_o0vCN>U5#Hlrzq0tXs++Gqw7;)J%+9|4$1MY*UOXS-BFpN7BjPY56pxU zGEXt2=V&wIC7W9$+R-^BYmr~*IS2w-C^Li+){S^UR`zJxu~;YbnPnKN4q~G+Ti|~Z zcBc%?72A;Nnn`BNr#pbPgiK%%Av{(`eElUxZ={ku>kY-fe#)(SRwsY1j6v4Ks9X@w znf6X0TbE-##H;9usiZ=?(Eu^l(h0pf(AZ$^(+tuia%LXtfKU43GmDH> zwiw?=w5eJukxpMj!}o(tYeFyfe;imgK12fh>f54Oxi4EAue_Q}@3+$!f8(fic2Z7hk#``Xbh|79 zaO;?+AbP}`(d=t}=0lOQC7?*wg0=;sr=4Yg--oF7`%_C^Du1a%G!Jiz>Su=-LA=H7 zeIKyIX>OsCH|*1UKC_2siZZPe35*7!GK%`cfZ~H}UqmqG>-3}a?MsqI3N2h%Ihb=@ zyTZw!{SdBU6jErO*Ris9F2QP97s|M9pZr7{QPH-l5Py;G5{WBtS*rp#5~StEfeon3 zn>U2sD2aOfUR1I)E$WbEXf8BS%8Ey}JFh-Ql3f#ZAoxm9Pw*c_F&du)F}yM|zMMpd zV>4c7lxB}t*EhOmP4*5hJ;J?BvyCu2dOSP&VaD?}t&F-syabMizhIj<8Wx$-q>6ZS!MJA-5fsL@Qt*<^*qCEGw>4J-D}OyZNOQj>spS- zTp`0c??PyyQG)06P{&E03pS!B=0c7PRB^tkE0r&q=>tWMWu7RH4eJE6>f4+^ch}E& zDyM~YUwrra#$E3AgG**bA6(U4|LpDad>X*)3XROIW%7uev*hFYY#eVU7Lzb#;vl_G zxFbJ4G_JCq48&DOre}<6O4bJk$E4|6@2dbU%jdtKb5-&4=Kj_hyLGjY(!I*P&)6xf z<8UK>u(8G}FZJr-I2PwtKK@GjlZ+P=W|En}0*F2)Uf9I!lG8ntp}BsRMB(N|J4^dV z+xTltV%Sz9h7e&-)-P-LEboVUo7&r}H7AyMy0|$Gew?%R53P6})6uccUW4_Q*T{-4 z$jv6!(cG>LAxzm}^_h75RgJXv^K&7&2bBab`Ez2{G_wife#YPPgQRK^fiH2&u%yJd z{EV%UBAHe)R|4GL@bgx?4y43qgbT5hEU-cQG9SYwF$o**|LTbrG{k&n^&=xsNVlvt z`T_;d-7eEKcRhb3N<7~<(P!*Ew}Nce#7XzMIbV6E1CVFO#WnKIhfM0gnt&D{LG*12 zeHk;PYsCdQlg3WRz%jl{=H7deYeOUovSi2-p*4r#%~jv?T(T@4MVP$SGlDtDJ8V@| zNpO+s>4r7#Kh-ihYhArkr269f*YB=Lh0SU?A4zL2Akh&4I19wX``)GwH#qKF{9OZa zIG>#Sf$~8mpSQHp_$) z^B^P-LG@{EVC&#&AS_so+*ZuDup&OObPblClxi^hda@uVsq5|+%e>kzqJzq3?kcf! z+fNv_^5YD_*H7yJ&YFF8Cv>4KTKS92#oUD?U@Tq3d?kWHx28?CO{*r97g^*!*r@y1 zS%!;fKhGt88WdL&L?MP!3U28Ps9|O+9U@+>A1DQ?N!UK;)4P`kI7c$9CFnGkunhv_ zkOq*}Zxa)?PdlQl)G%9CGS1Dj4=l!K_eCm6V3jX$GmHVU9Jg?G!C6T2R?|+mV(80v zn&$SN%yEP#H?3Ht>UceFZ(0)d=MUJ3WO>O3w4|$OqLjkwb;G`>@fRCqk|6eNTs_^k zXYyWzj|^#7iz(^_jMlH1qW%^h1>2V64t8wBUf4fi`?$2COJ#jkS4!2ZVB+<|FytHq zB)KEWtDzZM5u$*W(7Bp`X4Ah;^Kb@X1c#$8+`^(P6==O5sfQt14^lg9k1wgLMlZ;29No z956(M5&DfAXUjK%iaxnsT+aA=pAOG3Z%1zP`Hf0`H#ihB@R+BiA!9p$+KH%X!W=(= z1OT!nfRs)|I^aP7jW=i24g*s$Wz_KxevERa$puH@jQP6R?RJG99msX2U<#>&id3V? zh%!Yxdb!xRTH~BOW$-_yC)`{eex&?hnz^KKFFQ~^Uq=~KOchNl)NE_tt6q3%tTop0 zm+M#GFLhU*qXSTf0?kI5MN3n9A8}Z`7X?2j1C`w&MfrbU$SGhW-0}%Fj!kvq#EK z4B)>xKkjbog8X3lQFj+jlY8X+1`D`*%j{&n{k&ZjxPbpb0e8E0qRxKaXf`|)*A6<| zZs$%Y$6NByvOvI&-dCgn10(fKPRhYbDU1ydqW)n20|vc#hX4Qo literal 0 HcmV?d00001 diff --git a/documentation/esapi4java-core-2.2.0.0-release-notes.txt b/documentation/esapi4java-core-2.2.0.0-release-notes.txt index b13e3c192..44f937e57 100644 --- a/documentation/esapi4java-core-2.2.0.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.0.0-release-notes.txt @@ -16,6 +16,7 @@ Executive Summary: Important Things to Note for this Release * Known vulnerabilities still not addressed: - There is this critical CVE in log4j 2.x before 2.8.2: CVE-2017-5645. It is a Java deserialization vulnerability that can lead to arbitrary remote code execution. Some private vulnerability databases claim that this same vulnerability is present in log4j 1.x even though the CVE itself does not claim that. However, examination of this CVE shows that the vulnerability is associated with implementations of TcpSocketServer and UdpSocketServer, which implement fully functional socket servers that can be used to listen on network connections and record log events sent to server from various client applications. For ESAPI to be vulnerable to that, first someone would have to have an implementation of wone of those servers running and secondly, they would have to change ESAPI's log4j.xml configuration file so that it uses log4j's SocketAppender rather than the default ConsoleAppender that ESAPI's default deployment uses. Thus even if this vulnerability were present in log4j 1.x, ESAPI's use of ConsoleAppender makes it a non-issue. - There is a known and unpatched vulnerability in the SLF4J Extensions that some vulnerability scanners may pick up and associate with ESAPI's use of slf4j-api-1.7.25.jar. (Note that OWASP Dependency Check does NOT flag this vulnerability [CVE-2018-8088], but others may.) According to NVD, this vulnerability is associated with "org.slf4j.ext.EventData in the slf4j-ext module in QOS.CH SLF4J before 1.8.0-beta2". Fortunately, I have confirmed that this Java deserialization does not impact ESAPI. First off, the default configuration of ESAPI.properties does not use SLF4J, but even if an application should choose to use it, ESAPI does not include the slf4j-ext jar and it has been confirmed that the vulnerable class (org.slf4j.ext.EventData) is not included in the slf4j-api jar that ESAPI does. Unfortunately, this CVE is not patched in the latest SLF4J packages, so even if we were to update it to latest version (currently 1.80-beta2, as of 12/31/2018), any scanners that associate ESAPI with CVE-2018-8088 would still have this false positive. But the important thing to ESAPI users is to know that if this CVE is identified for ESAPI, that it is a false positive. + - There is a recently discovered issue (see https://app.snyk.io/vuln/SNYK-JAVA-COMMONSBEANUTILS-30077) that is related to CVE-2014-0114 that is a Java deserialization issue in Apache Commons BeanUtils 1.9.3 that can lead to remote command execution attacks. This had been fixed in 1.9.2, but apparently they missed a place where the fix was needed. A GitHub commit (https://github.com/apache/commons-beanutils/pull/7/commits/2780a77600e6428b730e3a5197b7c5baf1c4cca0) has been made to mster branch of the BeanUtils repo, but thus far, no official patch has been released. ESAPI only uses BeanUtils in its AccessController (specifically, DynaBeanACRParameter class), where it has a dependency on org.apache.commons.beanutils.LazyDynaMap. Based on the BeanUtils commit, the fix was in org.apache.commons.beanutils2.PropertyUtilsBean. Based on a cursory examination, the ESAPI team does not believe that this vulnerability reported by Snyk is exploitable given that manner that it is used within ESAPI, or if it is, it is not externally exploitable based on the default access control rules that are provided with ESAPI. However, the ESAPI team will be watching for an official patch to Apache Commons BeanUtils and we will release a patched version of ESAPI as patch point release once a patch is officially available in Maven Central. - Otherwise, ESAPI 2.2.0.0 addresses all know CVEs except for CVE-2013-5960 (which I have fixed in a private BitBucket repo, but getting it to be backward compatible is proving to be more difficult than anticipated.) Besides, if you want to use encryption in Java, I'd highly recommend using Google Tink, which is much more fully featured than ESAPI. (Tink allows key rotation, storing keys in various cloud HSMs, etc.) @@ -30,9 +31,9 @@ ESAPI 2.1.0.1 release: ESAPI 2.2.0.0 release: 194 source files - 4140 JUnit tests!!!!! + 4145 JUnit tests!!!!! -That's 2593 NEW tests!!! +That's 2598 NEW tests since the 2.1.0.1 release!!! GitHub Issues fixed in this release [i.e., since 2.1.0.1 release on 2016-Feb-05] @@ -85,6 +86,7 @@ Issue # GitHub Issue Title 301 encodeForHTMLAttribute escapes the forward slash 302 HTMLEntityCodec#decode incorrectly decodes upper-case accented letters as their lower-case counterparts 303 HTMLEntityCodec destroys 32-bit CJK (Chinese, Japanese and Korean) characters +304 encodeForCSS breaks color values 305 ClassCastException when using ESAPI logger 307 Issue with decodeFromURL method in the DefaultEncoder 308 AuthenticatedUser isCredentialsNonExpired() have todo comment, but default return false; @@ -150,9 +152,11 @@ Issue # GitHub Issue Title 471 Bump ESAPI release # to 2.2.0.0 476 DefaultValidator.getValidInput implementation ignores 'canonicalize' method parameter 478 Remove obsolete references to Google Code in pom.xml and any other release prep +482 ESAPI 2.2.0.0 release date? 483 More miscellaneous prep work for ESAPI 2.2.0.0 release 485 Update Maven dependency check plugin to 5.0.0-M2 - +492 Release candidates on maven central +493 wrong regex validation ----------------------------------------------------------------------------- @@ -305,25 +309,58 @@ List of all PRs closed since 2.1.0.1 (2016-Feb-05) - #472 by jeremiahjstacey was merged on Jan 21, 2019 -- Issue #31 MySQLCodec Updates #475 by jeremiahjstacey was merged on Jan 27, 2019 -- Issue #188 resolution proof: Test updates #477 by jeremiajjstacey was merged on Feb 02, 2019 -- $476 DefaultValidator.getValidInput uses canonicalize method argument +#487 by kwwall was merged on Apr 29, 2019 -- Master branch updates for ESAPI-2.2.0.0-RC2 +#490 by hellyguo was closed on May 12, 2019 -- enhance: cache class and method to avoid reading each time +#491 by hellyguo was merged on May 27, 2019 -- enhance: improve the performance of ObjFactory + -List of contributors of *merged* PRs, listed (rather naively) by # or merged PRs: +List of contributors of *merged* PRs, listed (rather naively) by # of merged PRs: # merged PRs GitHub ID ------------------------- 19 xeno6696 10 jeremiahjstacey - 8 kwwall + 9 kwwall 2 artfullyContrived 2 augustd 2 JoelRabinovitch 1 drm2 + 1 hellyguo 1 jackycct 1 mickilous 1 NiklasMehner 1 simon0117 1 sunnypav - -Thanks you all for your time and effort to ESAPI and making it a better project. +Developer Activity Report (Changes between release 2.1.0.1 and 2.2.0.0, i.e., between 2015-02-05 and 2019-06-09 ) +As created by 'mvn site', however this data was slighty edited to remove email ids replace them with GitHub ids when those were known, or with the developer name. +Sorted first by # of commits and then by developer id / name.. + +Developer Total commits Total Number + of Files Changed +===================================================== +kwwall 362 351 +xeno6696 64 82 +jeremiahjstacey 55 68 +davewichers 7 49 +Anthony Musyoki 4 2 +Kad DEMBELE 4 2 +augustd 3 7 +drmyersii 2 2 +JoelRabinovitch 2 4 +Ben Sleek 1 1 +chrisisbeef 1 5 +hellyguo 1 3 +Jackycct 1 2 +mickilous 1 2 +NiklasMehner 1 2 +Pavan Kumar 1 1 +simon0117 1 3 +taringamberini 1 1 +===================================================== +Totals: 512 399 (unique files changed) + + +Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Appendix: Dependency Updates (as reflected in pom.xml) diff --git a/pom.xml b/pom.xml index 2131e8b7c..4246bb983 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.2.0.0-RC3-SNAPSHOT + 2.2.0.0-RC3 jar @@ -698,7 +698,7 @@ org.owasp dependency-check-maven - 5.0.0-M2 + 5.0.0 5.9 ./suppressions.xml diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java index c116f1e67..9f124172a 100644 --- a/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java +++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/DynaBeanACRParameter.java @@ -5,7 +5,7 @@ import java.util.Date; import java.util.Iterator; -import org.apache.commons.beanutils.*; +import org.apache.commons.beanutils.LazyDynaMap; import org.owasp.esapi.reference.accesscontrol.policyloader.PolicyParameters; /** diff --git a/src/main/java/org/owasp/esapi/util/ObjFactory.java b/src/main/java/org/owasp/esapi/util/ObjFactory.java index 3ffadd88e..2c23e5f00 100644 --- a/src/main/java/org/owasp/esapi/util/ObjFactory.java +++ b/src/main/java/org/owasp/esapi/util/ObjFactory.java @@ -30,11 +30,11 @@ * ... * // Typically these would be populated from some Java properties file * String barName = "com.example.foo.Bar"; - * String beerBrand = "com.example.brewery.Guiness"; + * String beerBrand = "com.example.brewery.Guinness"; * ... * DrinkingEstablishment bar = ObjFactory.make(barName, "DrinkingEstablishment"); * Beer beer = ObjFactory.make(beerBrand, "Beer"); - * bar.drink(beer); // Drink a Guiness beer at the foo Bar. :) + * bar.drink(beer); // Drink a Guinness beer at the foo Bar. :) * ... * *

@@ -45,10 +45,11 @@ */ public class ObjFactory { - private static final int CACHE_INITIAL_CAPACITY = 4096; + private static final int CACHE_INITIAL_CAPACITY = 32; private static final float CACHE_LOAD_FACTOR = 0.75F; private static final ConcurrentHashMap> CLASSES_CACHE = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY, CACHE_LOAD_FACTOR); private static final ConcurrentHashMap METHODS_CACHE = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY, CACHE_LOAD_FACTOR); + private static boolean cacheEnabled = true; /** * Create an object based on the className parameter. @@ -130,6 +131,18 @@ public static T make(String className, String typeName) throws Configuration // DISCUSS: Should we also catch ExceptionInInitializerError here? See Google Issue #61 comments. } + /** + * Control whether cache for classes and method names should be enabled or disabled. Initial state is enabled. + * Ordinally, you are not expected to want to / have to call this method. It's major purpose is a "just-in-case" + * something goes wrong is some weird context where multiple ESAPI jars are loaded into a give application and something + * goes wrong, etc. A secondary purpose is it allows us to easily disable the cache so we can measure its time savings. + * + * @param enable true - enable cache; false - disable cache + */ + public static void setCache(boolean enable) { + cacheEnabled = enable; + } + /** * Load the class in cache, or load by the classloader and cache it * @@ -139,11 +152,11 @@ public static T make(String className, String typeName) throws Configuration */ private static Class loadClassByStringName(String className) throws ClassNotFoundException { Class clazz; - if (CLASSES_CACHE.containsKey(className)) { + if (cacheEnabled && CLASSES_CACHE.containsKey(className)) { clazz = CLASSES_CACHE.get(className); } else { clazz = Class.forName(className); - CLASSES_CACHE.putIfAbsent(className, clazz); + if ( cacheEnabled ) CLASSES_CACHE.putIfAbsent(className, clazz); } return clazz; } @@ -177,7 +190,7 @@ private static Method findSingletonCreateMethod(String className, Class theCl private static MethodWrappedInfo loadMethodByStringName(String className, Class theClass) throws NoSuchMethodException { String methodName = className + "getInstance"; MethodWrappedInfo methodInfo; - if (METHODS_CACHE.containsKey(methodName)) { + if (cacheEnabled && METHODS_CACHE.containsKey(methodName)) { methodInfo = METHODS_CACHE.get(methodName); } else { Method method = theClass.getMethod("getInstance"); @@ -185,7 +198,7 @@ private static MethodWrappedInfo loadMethodByStringName(String className, Class< ConfigurationException nonStaticEx = staticMethod ? null : new ConfigurationException("Class [" + className + "] contains a non-static getInstance method."); methodInfo = new MethodWrappedInfo(method, staticMethod, nonStaticEx); - METHODS_CACHE.putIfAbsent(methodName, methodInfo); + if ( cacheEnabled ) METHODS_CACHE.putIfAbsent(methodName, methodInfo); } return methodInfo; } diff --git a/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java b/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java index 2b817591c..4657e4a0a 100644 --- a/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java +++ b/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java @@ -183,10 +183,44 @@ public void testMakeCipher() throws ConfigurationException { String className = "javax.crypto.spec.SecretKeySpec"; javax.crypto.spec.SecretKeySpec skeySpec = (SecretKeySpec) ObjFactory.make(className, "SecretKeySpec"); - assertTrue( skeySpec != null ); + // Should not get to here. Exception is expected. } catch(ConfigurationException ex) { Throwable cause = ex.getCause(); assertTrue( cause instanceof InstantiationException); } } + + /** Test cache. Create 100k JavaEncryptor instances with cache enabled (the + * default), and then create 100k instances with cache disabled. Time each. + * The cached version should save some time. + */ + public void testObjFactoryCache() throws Exception { + final int reps = 100000; + System.out.println("testObjFactoryCache: " + reps + " iterations."); + org.owasp.esapi.reference.crypto.JavaEncryptor je = null; + String clz = "org.owasp.esapi.reference.crypto.JavaEncryptor"; + + long startCacheEnabled = System.nanoTime(); + for ( int i = 0; i < reps; i++ ) { + je = (org.owasp.esapi.reference.crypto.JavaEncryptor) ObjFactory.make(clz, "JavaEncryptor"); + assertNotNull( je ); + } + long stopCacheEnabled = System.nanoTime(); + + ObjFactory.setCache( false ); // Disable cache + + long startCacheDisabled = System.nanoTime(); + for ( int i = 0; i < reps; i++ ) { + je = (org.owasp.esapi.reference.crypto.JavaEncryptor) ObjFactory.make(clz, "JavaEncryptor"); + assertNotNull( je ); + } + long stopCacheDisabled = System.nanoTime(); + + long durationEnabled = stopCacheEnabled - startCacheEnabled; + long durationDisabled = stopCacheDisabled - startCacheDisabled; + System.out.println("testObjFactoryCache: Time with cache ENABLED (nanosec): " + durationEnabled ); + System.out.println("testObjFactoryCache: Time with cache DISABLED (nanosec): " + durationDisabled ); + + assertTrue( durationEnabled < durationDisabled ); + } } diff --git a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties index 87c1a3de3..e9bafc134 100644 --- a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties @@ -1,5 +1,35 @@ -# # OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version +############################################################################# +# +# WARNING WARNING WARNING WARNING WARNING WARNING +# +# ####### # # +# # ###### #### ##### # # ###### ##### #### +# # # # # # # # # # # +# # ##### #### # # # ##### # # #### +# # # # # # # # ##### # +# # # # # # # # # # # # # +# # ###### #### # # ###### # # #### +# +# This is NOT the version of ESAPI.properties that you are looking for. +# Do NOT use this for your production applications!!!!! +# +# That is over in the 'configuration/esapi/ESAPI.properties' file. You +# should retrieve THAT version from the official GitHub report from +# https://github.com/ESAPI/esapi-java-legacy/blob/develop/configuration/esapi/ESAPI.properties +# but make sure that you select it from the 'master' branch (which will +# correspond to the latest official ESAPI relase). Sorry for the +# inconvenience. We are trying to figure out how to get it to the official +# "esapi--sources.jar fiel available from Maven Central, but +# in the meantime, you will have to get it from GitHub. +# +# PLEASE do not base your production use of ESAPI on this TEST version of +# ESAPI.properties as this test version has been dummed down in several places +# for JUnit testing. +# +# You have been warned. +# +############################################################################# # # This file is part of the Open Web Application Security Project (OWASP) # Enterprise Security API (ESAPI) project. For details, please see diff --git a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties index 275d855f9..c53476642 100644 --- a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties @@ -1,5 +1,35 @@ -# # OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version +############################################################################# +# +# WARNING WARNING WARNING WARNING WARNING WARNING +# +# ####### # # +# # ###### #### ##### # # ###### ##### #### +# # # # # # # # # # # +# # ##### #### # # # ##### # # #### +# # # # # # # # ##### # +# # # # # # # # # # # # # +# # ###### #### # # ###### # # #### +# +# This is NOT the version of ESAPI.properties that you are looking for. +# Do NOT use this for your production applications!!!!! +# +# That is over in the 'configuration/esapi/ESAPI.properties' file. You +# should retrieve THAT version from the official GitHub report from +# https://github.com/ESAPI/esapi-java-legacy/blob/develop/configuration/esapi/ESAPI.properties +# but make sure that you select it from the 'master' branch (which will +# correspond to the latest official ESAPI relase). Sorry for the +# inconvenience. We are trying to figure out how to get it to the official +# "esapi--sources.jar fiel available from Maven Central, but +# in the meantime, you will have to get it from GitHub. +# +# PLEASE do not base your production use of ESAPI on this TEST version of +# ESAPI.properties as this test version has been dummed down in several places +# for JUnit testing. +# +# You have been warned. +# +############################################################################# # # This file is part of the Open Web Application Security Project (OWASP) # Enterprise Security API (ESAPI) project. For details, please see diff --git a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties index e84e83086..b868c6f6d 100644 --- a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties @@ -1,6 +1,35 @@ -# # OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version -# +############################################################################# +# +# WARNING WARNING WARNING WARNING WARNING WARNING +# +# ####### # # +# # ###### #### ##### # # ###### ##### #### +# # # # # # # # # # # +# # ##### #### # # # ##### # # #### +# # # # # # # # ##### # +# # # # # # # # # # # # # +# # ###### #### # # ###### # # #### +# +# This is NOT the version of ESAPI.properties that you are looking for. +# Do NOT use this for your production applications!!!!! +# +# That is over in the 'configuration/esapi/ESAPI.properties' file. You +# should retrieve THAT version from the official GitHub report from +# https://github.com/ESAPI/esapi-java-legacy/blob/develop/configuration/esapi/ESAPI.properties +# but make sure that you select it from the 'master' branch (which will +# correspond to the latest official ESAPI relase). Sorry for the +# inconvenience. We are trying to figure out how to get it to the official +# "esapi--sources.jar fiel available from Maven Central, but +# in the meantime, you will have to get it from GitHub. +# +# PLEASE do not base your production use of ESAPI on this TEST version of +# ESAPI.properties as this test version has been dummed down in several places +# for JUnit testing. +# +# You have been warned. +# +############################################################################# # This file is part of the Open Web Application Security Project (OWASP) # Enterprise Security API (ESAPI) project. For details, please see # http://www.owasp.org/index.php/ESAPI. diff --git a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties index a7d65d5ab..3946d4395 100644 --- a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties @@ -1,6 +1,35 @@ -# # OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version -# +############################################################################# +# +# WARNING WARNING WARNING WARNING WARNING WARNING +# +# ####### # # +# # ###### #### ##### # # ###### ##### #### +# # # # # # # # # # # +# # ##### #### # # # ##### # # #### +# # # # # # # # ##### # +# # # # # # # # # # # # # +# # ###### #### # # ###### # # #### +# +# This is NOT the version of ESAPI.properties that you are looking for. +# Do NOT use this for your production applications!!!!! +# +# That is over in the 'configuration/esapi/ESAPI.properties' file. You +# should retrieve THAT version from the official GitHub report from +# https://github.com/ESAPI/esapi-java-legacy/blob/develop/configuration/esapi/ESAPI.properties +# but make sure that you select it from the 'master' branch (which will +# correspond to the latest official ESAPI relase). Sorry for the +# inconvenience. We are trying to figure out how to get it to the official +# "esapi--sources.jar fiel available from Maven Central, but +# in the meantime, you will have to get it from GitHub. +# +# PLEASE do not base your production use of ESAPI on this TEST version of +# ESAPI.properties as this test version has been dummed down in several places +# for JUnit testing. +# +# You have been warned. +# +############################################################################# # This file is part of the Open Web Application Security Project (OWASP) # Enterprise Security API (ESAPI) project. For details, please see # http://www.owasp.org/index.php/ESAPI. diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 5a0da9f19..512998756 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -1,4 +1,3 @@ -# # OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version ############################################################################# # From 6fa7bcfc0b575912b3b7df033d6a92355f3fe94b Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 24 Jun 2019 18:10:50 -0400 Subject: [PATCH 002/544] Final preparation for ESAPI 2.2.0.0 release (#501) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change release version to 2.2.0.0 for official release. * Update / correct ESAPI release steps. * Fix ironic spelling typo. * Fix ironic spelling typo. * Changes to suppress most of the noise, but also fixes to all for .XML and .PROPERTIES suffixes and to fix and return null when property is null or empty string. * Changes to suppress most of the noise and to actually handle the exceptions that we should have been all along (e.g., IOExceptions). * Comment out the crude benchmark related assertion that sometimes was failing because of JIT-related issues. Needs to be eventually replaced by JMH. * Close #499 by resetting 'parent' when Windows is detected to root of drive where Windows is installed. * Close issue #488. These are slight enhancements to PR #489 by @JoergAdler that I rejected because Eclipse did something to cause every line to differ. But shout out to Jörg Adler for originally finding this issue and patching it. * Close issue #488. These are slight enhancements to PR #489 by @JoergAdler that I rejected because Eclipse did something to cause every line to differ. But shout out to Jörg Adler for originally finding this issue and patching it. I also added an additional test or 2, so don't blame @JoergAdler if I messed that up. * Close issue #488. These are slight enhancements to PR #489 by @JoergAdler that I rejected because Eclipse did something to cause every line to differ. But shout out to Jörg Adler for originally finding this issue and patching it. I tremendously stripped down the contents of this file because really all it needed was a single property referencing the validation.properties file. * Add 3 additional issues that were closed and fixes to some minor formatting. * Final updates for the ESAPI 2.2.0.0 official release. * Figure I probably ought to add my name instead of assuming people knew it and my email address. * Changed schema from allowing unbounded number of properties to allow only 10000. That really should NOT be a problem. However, it should help silence some of the SAST engines. * Additional changes to fix GitHub Issue #500 --- CONTRIBUTING-TO-ESAPI.txt | 2 +- documentation/ESAPI-release-steps.odt | Bin 165550 -> 273380 bytes .../esapi4java-core-2.2.0.0-release-notes.txt | 46 +++++-- pom.xml | 2 +- .../AbstractPrioritizedPropertyLoader.java | 14 +- .../EsapiPropertyLoaderFactory.java | 33 ++++- .../configuration/EsapiPropertyManager.java | 46 +++++-- .../StandardEsapiPropertyLoader.java | 2 +- .../configuration/XmlEsapiPropertyLoader.java | 5 +- .../DefaultSecurityConfiguration.java | 12 +- src/main/resources/ESAPI-properties.xsd | 4 +- .../EsapiPropertyManagerTest.java | 130 +++++++++++++++--- .../StandardEsapiPropertyLoaderTest.java | 121 ++++++++++++---- .../XmlEsapiPropertyLoaderTest.java | 126 +++++++++++++---- .../DefaultSecurityConfigurationTest.java | 52 ++++++- .../owasp/esapi/reference/ValidatorTest.java | 20 +++ .../org/owasp/esapi/util/ObjFactoryTest.java | 9 ++ ...ESAPI-CommaValidatorFileChecker.properties | 2 +- .../ESAPI-DualValidatorFileChecker.properties | 2 +- ...SAPI-QuotedValidatorFileChecker.properties | 2 +- ...SAPI-SingleValidatorFileChecker.properties | 2 +- .../resources/esapi/ESAPI-root-cp.properties | 4 + src/test/resources/esapi/ESAPI.properties | 2 +- 23 files changed, 516 insertions(+), 122 deletions(-) create mode 100644 src/test/resources/esapi/ESAPI-root-cp.properties diff --git a/CONTRIBUTING-TO-ESAPI.txt b/CONTRIBUTING-TO-ESAPI.txt index 4154a5916..e4fab6e45 100644 --- a/CONTRIBUTING-TO-ESAPI.txt +++ b/CONTRIBUTING-TO-ESAPI.txt @@ -17,7 +17,7 @@ Finding Something Interesting to Work on: what it is. Then if you want to work on a particular issue, we can assign it to you so someone else won't take it. - If you have questions, email me or Matt Seil (xeno6696@gmail.com). + If you have questions, email Kevin Wall (Kevin.W.Wall@gmail.com) or Matt Seil (xeno6696@gmail.com). Overview: We are following the branching model described in diff --git a/documentation/ESAPI-release-steps.odt b/documentation/ESAPI-release-steps.odt index e75c8599c67fc20cf6ba60c280f1af0084ef9eaf..bf188f05f965c9d5051d1f4fc60a4ff4df2e1b23 100644 GIT binary patch delta 148504 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 delta 39875 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 I-&gm40GFq-CIA2c diff --git a/documentation/esapi4java-core-2.2.0.0-release-notes.txt b/documentation/esapi4java-core-2.2.0.0-release-notes.txt index 44f937e57..64bd3598e 100644 --- a/documentation/esapi4java-core-2.2.0.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.0.0-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.2.0.0 - Release date: 2019-MMM-DD + Release date: 2019-June-23 Project leaders: -Kevin W. Wall -Matt Seil @@ -26,14 +26,14 @@ It was mainly because of these first two bullet items above that we bumped the r Basic ESAPI facts ESAPI 2.1.0.1 release: - 177 source files - 1547 Junit tests + 177 Java source files + 1547 Junit tests in 88 Java source files ESAPI 2.2.0.0 release: - 194 source files - 4145 JUnit tests!!!!! + 194 Java source files + 4150 JUnit tests in 118 Java source files -That's 2598 NEW tests since the 2.1.0.1 release!!! +That's 2603 NEW tests since the 2.1.0.1 release!!! GitHub Issues fixed in this release [i.e., since 2.1.0.1 release on 2016-Feb-05] @@ -149,14 +149,18 @@ Issue # GitHub Issue Title 462 Allow configurable init parameter in ESAPIFilter for unauthorized requests 463 Create release notes for next ESAPI release 465 Update both ESAPI.properties files to show comment for ESAPI logger support for SLF4J -471 Bump ESAPI release # to 2.2.0.0 -476 DefaultValidator.getValidInput implementation ignores 'canonicalize' method parameter -478 Remove obsolete references to Google Code in pom.xml and any other release prep +471 Bump ESAPI release # to 2.2.0.0 +476 DefaultValidator.getValidInput implementation ignores 'canonicalize' method parameter +478 Remove obsolete references to Google Code in pom.xml and any other release prep 482 ESAPI 2.2.0.0 release date? -483 More miscellaneous prep work for ESAPI 2.2.0.0 release -485 Update Maven dependency check plugin to 5.0.0-M2 +483 More miscellaneous prep work for ESAPI 2.2.0.0 release +485 Update Maven dependency check plugin to 5.0.0-M2 +488 Missed a legal input case in DefaultSecurityConfiguration.java 492 Release candidates on maven central 493 wrong regex validation +499 ValidatorTest.isValidDirectoryPath() has tests that fail under Windows if ESAPI tests run from different drive where Windows installed +500 Suppress noise from ESAPI searching for properties and stop ignoring important IOExceptions + ----------------------------------------------------------------------------- @@ -164,7 +168,7 @@ Issue # GitHub Issue Title * Various deprecated methods were _actually_ deleted! This could break existing application code. - 442 Remove deprecated fields in Encoder interface + Issue 442 Remove deprecated fields in Encoder interface Specifically, if you are using any of these previously deprecated fields from the Encoder interface, you need to update your application code to refer insteat to the constances from org.owasp.esapi.EncoderConstants: @@ -180,7 +184,7 @@ Issue # GitHub Issue Title public final static char[] CHAR_PASSWORD_SPECIALS = EncoderConstants.CHAR_PASSWORD_SPECIALS; public final static char[] CHAR_PASSWORD_LETTERS = EncoderConstants.CHAR_PASSWORD_LETTERS; - 444 Delete deprecated method Base64.decodeToObject() and related methods + Issue 444 Delete deprecated method Base64.decodeToObject() and related methods Specifically, the following methods were removed from the org.owasp.esapi.codecs.Base64 class. If you will using any of these methods, you likely already had vulnerabilities in your application code. If any of these methods were being used, you will need to rewrite your application code: @@ -188,11 +192,11 @@ Issue # GitHub Issue Title public static String encodeObject( java.io.Serializable serializableObject, int options ) public static Object decodeToObject( String encodedObject ) - 483 More miscellaneous prep work for ESAPI 2.2.0.0 release + Issue 483 More miscellaneous prep work for ESAPI 2.2.0.0 release Specifically, CipherText.getSerialVersionUID() and DefaultSecurityConfiguration.MAX_FILE_NAME_LENGTH have actually been deleted from the ESAPI code base. For the former, use CipherText.cipherTextVersion() instead. For the latter, there is no replacement. (This wasn't being used, but it was set to 1000 in case you're wondering.) * Various properties in ESAPI.properties were changed in a way that might affect your application: - 439 Tighten ESAPI defaults to disallow dubious file suffixes + Issue 439 Tighten ESAPI defaults to disallow dubious file suffixes Specifically, the property HttpUtilities.ApprovedUploadExtensions changed from HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll @@ -220,6 +224,18 @@ Issue # GitHub Issue Title to: Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$ +* Other changes: + Issue 500 Suppress noise from ESAPI searching for properties and stop ignoring important IOExceptions + + Fixing this required changes to the CTORs of the following classes: + + org.owasp.esapi.configuration.EsapiPropertyManager + org.owasp.esapi.configuration.AbstractPrioritizedPropertyLoader + org.owasp.esapi.configuration.EsapiPropertyLoaderFactory + org.owasp.esapi.configuration.StandardEsapiPropertyLoader + org.owasp.esapi.configuration.XmlEsapiPropertyLoader + + These CTORs now explicitly throw IOException if the specified ESAPI property file is not found or not readable. Note that this should not affect most people as most use DefaultSecurityCOnfigurator and it still only throws ConfigurationException. (IOExceptions from these other classes are caught and rethrow as ConfigurationException.) Use of these classes directly should be very rare. ----------------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 4246bb983..b92829dd2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.2.0.0-RC3 + 2.2.0.0 jar diff --git a/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java index ee358f12b..8869694ce 100644 --- a/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java @@ -2,6 +2,8 @@ import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Properties; /** @@ -32,7 +34,7 @@ public abstract class AbstractPrioritizedPropertyLoader implements EsapiProperty private final int priority; - public AbstractPrioritizedPropertyLoader(String filename, int priority) { + public AbstractPrioritizedPropertyLoader(String filename, int priority) throws IOException { this.priority = priority; this.filename = filename; initProperties(); @@ -64,13 +66,17 @@ public String name() { /** * Initializes properties object and fills it with data from configuration file. */ - private void initProperties() { + private void initProperties() throws IOException { properties = new Properties(); File file = new File(filename); if (file.exists() && file.isFile()) { - loadPropertiesFromFile(file); + if ( file.canRead() ) { + loadPropertiesFromFile(file); + } else { + throw new IOException("Can't read specificied configuration file: " + filename); + } } else { - logSpecial("Configuration file " + filename + " does not exist"); + throw new FileNotFoundException("Specified configuration file " + filename + " does not exist or not regular file"); } } diff --git a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java index 32acbf5cc..4f814e06a 100644 --- a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java +++ b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java @@ -3,7 +3,7 @@ import org.owasp.esapi.configuration.consts.EsapiConfiguration; import org.owasp.esapi.errors.ConfigurationException; -import java.io.FileNotFoundException; +import java.io.IOException; import static org.owasp.esapi.configuration.consts.EsapiConfigurationType.PROPERTIES; import static org.owasp.esapi.configuration.consts.EsapiConfigurationType.XML; @@ -17,17 +17,38 @@ public class EsapiPropertyLoaderFactory { public static AbstractPrioritizedPropertyLoader createPropertyLoader(EsapiConfiguration cfg) - throws ConfigurationException, FileNotFoundException { + throws ConfigurationException, IOException { String cfgPath = System.getProperty(cfg.getConfigName()); - if (cfgPath == null) { - throw new ConfigurationException("System property [" + cfg.getConfigName() + "] is not set"); + if ( cfgPath == null || cfgPath.equals("") ) { + // TODO / FIXME: + // This case was previously a warning, but it should NOT have been + // since these system properties are optional. Most people just use + // the traditional ESAPI.properties file and not these prioritized ones. + // A warning gets logged in EsapiPropertyManager if logSpecial output + // has not been discarded. + // + // Note also there were a LOT of cases in our JUnit tests where the + // file extension was empty, causing the ConfigurationException to + // be thrown with the error message: + // "Configuration storage type [] is not supported" + // I don't think that was intentional, but because prior to the + // changes for this commit, these were all ConfigurationExceptions + // and they all were just being caught and not re-thrown by + // DefaultSecurityConfigurator. I think that is an error, probably + // in the tests, but I don't have timed to chase it down right now + // because of the pending 2.2.0.0 release. + // + // Also, I made several fixes in DefaultSecurityConfiguration + // related to this clean-up where IOExceptions were being silently + // caught when they should not have been. -kwwall + return null; } String fileExtension = cfgPath.substring(cfgPath.lastIndexOf('.') + 1); - if (XML.getTypeName().equals(fileExtension)) { + if (XML.getTypeName().equalsIgnoreCase(fileExtension)) { return new XmlEsapiPropertyLoader(cfgPath, cfg.getPriority()); } - if (PROPERTIES.getTypeName().equals(fileExtension)) { + if (PROPERTIES.getTypeName().equalsIgnoreCase(fileExtension)) { return new StandardEsapiPropertyLoader(cfgPath, cfg.getPriority()); } else { throw new ConfigurationException("Configuration storage type [" + fileExtension + "] is not " + diff --git a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java index 9609ec0be..94b5e4d5a 100644 --- a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java +++ b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java @@ -4,9 +4,15 @@ import org.owasp.esapi.errors.ConfigurationException; import java.util.TreeSet; +import java.io.IOException; import static org.owasp.esapi.configuration.EsapiPropertyLoaderFactory.createPropertyLoader; +// Have dependency like this on a reference implmentation is majorly ugly, I know, but I +// don't want to refactor code and delay the 2.2.0.0 release further and this class +// is WAY too noisy. - kwwall +import static org.owasp.esapi.reference.DefaultSecurityConfiguration.logToStdout; + /** * Manager used for loading security configuration properties. Does all the logic to obtain the correct property from * correct source. Uses following system properties to find configuration files: @@ -21,7 +27,7 @@ public class EsapiPropertyManager implements EsapiPropertyLoader { protected TreeSet loaders; - public EsapiPropertyManager() { + public EsapiPropertyManager() throws IOException { initLoaders(); } @@ -34,7 +40,7 @@ public int getIntProp(String propertyName) throws ConfigurationException { try { return loader.getIntProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property not found in " + loader.name()); + logToStdout("Integer property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); @@ -49,7 +55,7 @@ public byte[] getByteArrayProp(String propertyName) throws ConfigurationExceptio try { return loader.getByteArrayProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property not found in " + loader.name()); + logToStdout("Byte array property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); @@ -64,7 +70,7 @@ public Boolean getBooleanProp(String propertyName) throws ConfigurationException try { return loader.getBooleanProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property not found in " + loader.name()); + logToStdout("Boolean property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); @@ -79,25 +85,37 @@ public String getStringProp(String propertyName) throws ConfigurationException { try { return loader.getStringProp(propertyName); } catch (ConfigurationException e) { - System.err.println("Property : " + propertyName + " not found in " + loader.name()); + logToStdout("Property '" + propertyName + "' not found in " + loader.name(), e); } } throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); } - private void initLoaders() { + private void initLoaders() throws IOException { loaders = new TreeSet(); try { - loaders.add(createPropertyLoader(EsapiConfiguration.OPSTEAM_ESAPI_CFG)); - } catch (Exception e) { - System.err.println(e.getMessage()); + AbstractPrioritizedPropertyLoader appl = createPropertyLoader(EsapiConfiguration.OPSTEAM_ESAPI_CFG); + if ( appl == null ) { + String msg = "WARNING: System property [" + EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName() + "] is not set"; + logToStdout(msg, null); + } else { + loaders.add( appl ); + } + } catch (IOException e) { + logToStdout("WARNING: Exception encountered while setting up ESAPI configuration manager for OPS team", e); + throw e; } try { - loaders.add(createPropertyLoader(EsapiConfiguration.DEVTEAM_ESAPI_CFG)); - } catch (Exception e) { - System.err.println(e.getMessage()); + AbstractPrioritizedPropertyLoader appl = createPropertyLoader(EsapiConfiguration.DEVTEAM_ESAPI_CFG); + if ( appl == null ) { + String msg = "WARNING: System property [" + EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName() + "] is not set"; + logToStdout(msg, null); + } else { + loaders.add( appl ); + } + } catch (IOException e) { + logToStdout("WARNING: Exception encountered while setting up ESAPI configuration manager for DEV team", e); + throw e; } } - - } diff --git a/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java index 4be9ba759..565235a84 100644 --- a/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java @@ -12,7 +12,7 @@ */ public class StandardEsapiPropertyLoader extends AbstractPrioritizedPropertyLoader { - public StandardEsapiPropertyLoader(String filename, int priority) { + public StandardEsapiPropertyLoader(String filename, int priority) throws IOException { super(filename, priority); } diff --git a/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java index beddf93ba..daf547272 100644 --- a/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java @@ -26,7 +26,7 @@ */ public class XmlEsapiPropertyLoader extends AbstractPrioritizedPropertyLoader { - public XmlEsapiPropertyLoader(String filename, int priority) { + public XmlEsapiPropertyLoader(String filename, int priority) throws IOException { super(filename, priority); } @@ -80,7 +80,7 @@ public Boolean getBooleanProp(String propertyName) throws ConfigurationException return false; } else { throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + - "cannot be converted to boolean"); + "cannot be converted to boolean; legal values are: true, false, yes, no"); } } @@ -120,6 +120,7 @@ protected void loadPropertiesFromFile(File file) throws ConfigurationException { } } } catch (Exception e) { + logSpecial("XML config file " + filename + " has invalid schema", e); throw new ConfigurationException("Configuration file : " + filename + " has invalid schema." + e.getMessage(), e); } } diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index 851cbcc28..c244c20fa 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -235,9 +235,9 @@ public static SecurityConfiguration getInstance() { */ DefaultSecurityConfiguration(String resourceFile) { this.resourceFile = resourceFile; - this.esapiPropertyManager = new EsapiPropertyManager(); // load security configuration try { + this.esapiPropertyManager = new EsapiPropertyManager(); loadConfiguration(); this.setCipherXProperties(); } catch( IOException e ) { @@ -646,7 +646,13 @@ private Properties loadConfigurationFromClasspath(String fileName) throws Illega try { // try root String currentClasspathSearchLocation = "/ (root)"; - in = loaders[i].getResourceAsStream(DefaultSearchPath.ROOT.toString()); + // Note: do NOT add '/' anywhere here even though root value is empty string! + // Note that since DefaultSearchPath.ROOT.value() is now "" (the empty string), + // then this is logically equivalent to what we used to have, which was: + // + // in = loaders[i].getResourceAsStream(fileName); + // + in = loaders[i].getResourceAsStream(DefaultSearchPath.ROOT.value() + fileName); // try resourceDirectory folder if (in == null) { @@ -1391,7 +1397,7 @@ public enum DefaultSearchPath { RESOURCE_DIRECTORY("resourceDirectory/"), SRC_MAIN_RESOURCES("src/main/resources/"), - ROOT("/"), + ROOT(""), DOT_ESAPI(".esapi/"), ESAPI("esapi/"), RESOURCES("resources/"); diff --git a/src/main/resources/ESAPI-properties.xsd b/src/main/resources/ESAPI-properties.xsd index 523d3a124..152411b7c 100644 --- a/src/main/resources/ESAPI-properties.xsd +++ b/src/main/resources/ESAPI-properties.xsd @@ -2,7 +2,7 @@ - + @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java b/src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java index 09096a424..6fb1c0abf 100644 --- a/src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java +++ b/src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java @@ -8,6 +8,7 @@ import java.io.File; import java.io.IOException; +import java.io.FileNotFoundException; import static junit.framework.Assert.*; @@ -17,6 +18,7 @@ public class EsapiPropertyManagerTest { private static String propFilename2; private static String xmlFilename1; private static String xmlFilename2; + private static final String noSuchFile = "/invalidDir/noSubDir/nosuchFile.xml"; private EsapiPropertyManager testPropertyManager; @@ -32,6 +34,7 @@ public void init() { "esapi" + File.separator + "ESAPI-test.xml"; xmlFilename2 = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "esapi" + File.separator + "ESAPI-test-2.xml"; + } @Test @@ -41,7 +44,11 @@ public void testPropertyManagerInitialized() { System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), propFilename2); // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } // then assertNotNull(testPropertyManager.loaders); @@ -56,7 +63,11 @@ public void testStringPropFoundInLoader() { String expectedPropertyValue = "test_string_property"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } String propertyValue = testPropertyManager.getStringProp(propertyKey); // then @@ -73,7 +84,11 @@ public void testStringPropertyLoadedFromFileWithHigherPriority() { String expectedValue = "test_string_property_2"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } String propertyValue = testPropertyManager.getStringProp(propertyKey); // then @@ -89,7 +104,11 @@ public void testStringPropertyLoadedFromPropFileWithHigherPriority() { String expectedValue = "test_string_property_2"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } String propertyValue = testPropertyManager.getStringProp(propertyKey); // then @@ -105,7 +124,11 @@ public void testStringPropertyLoadedFromXmlFileWithHigherPriority() { String expectedValue = "test_string_property_2"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } String propertyValue = testPropertyManager.getStringProp(propertyKey); // then @@ -120,7 +143,11 @@ public void testStringPropertyNotFoundByLoaderAndThrowException() { String propertyKey = "non.existing.property"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } testPropertyManager.getStringProp(propertyKey); // then expect exception @@ -134,7 +161,11 @@ public void testIntPropFoundInLoader() { int expectedPropertyValue = 5; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } int propertyValue = testPropertyManager.getIntProp(propertyKey); // then @@ -150,7 +181,11 @@ public void testIntPropertyLoadedFromFileWithHigherPriority() { int expectedValue = 52; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } int propertyValue = testPropertyManager.getIntProp(propertyKey); // then @@ -166,7 +201,11 @@ public void testIntPropertyLoadedFromPropFileWithHigherPriority() { int expectedValue = 52; // value from ESAPI-test-2.properties file // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } int propertyValue = testPropertyManager.getIntProp(propertyKey); // then @@ -182,7 +221,11 @@ public void testIntPropertyLoadedFromXmlFileWithHigherPriority() { int expectedValue = 52; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } int propertyValue = testPropertyManager.getIntProp(propertyKey); // then @@ -196,7 +239,11 @@ public void testIntPropertyNotFoundByLoaderAndThrowException() { String propertyKey = "non.existing.property"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } testPropertyManager.getIntProp(propertyKey); // then expect exception @@ -210,7 +257,11 @@ public void testBooleanPropFoundInLoader() { boolean expectedPropertyValue = true; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } boolean propertyValue = testPropertyManager.getBooleanProp(propertyKey); // then @@ -223,7 +274,11 @@ public void testBooleanPropertyNotFoundByLoaderAndThrowException() { String propertyKey = "non.existing.property"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } testPropertyManager.getBooleanProp(propertyKey); // then expect exception @@ -242,7 +297,11 @@ public void testByteArrayPropFoundInLoader() { } // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); // then @@ -263,7 +322,11 @@ public void testByteArrayPropertyLoadedFromFileWithHigherPriority() { } // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); // then @@ -284,7 +347,11 @@ public void testByteArrayPropertyLoadedFromPropFileWithHigherPriority() { } // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); // then @@ -305,7 +372,11 @@ public void testByteArrayPropertyLoadedFromXmlFileWithHigherPriority() { } // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); // then @@ -318,10 +389,33 @@ public void testByteArrayPropertyNotFoundByLoaderAndThrowException() { String propertyKey = "non.existing.property"; // when - testPropertyManager = new EsapiPropertyManager(); + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + fail(e.getMessage()); + } testPropertyManager.getByteArrayProp(propertyKey); // then expect exception } + + @Test + public void testExpectFileNotFoundException() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), noSuchFile); + + // when + try { + testPropertyManager = new EsapiPropertyManager(); + } catch (IOException e) { + if ( e instanceof FileNotFoundException ) { + return; + } else { + fail("testExpectFileNotFoundException(): Was expecting FileNotFoundException for IOException. Exception:" + e); + } + } + + fail("Did not throw expected IOException for property file " + noSuchFile); + } } diff --git a/src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java b/src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java index cf0cffca9..c07f4e305 100644 --- a/src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java +++ b/src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java @@ -31,8 +31,12 @@ public void init() { @Test public void testPropertiesLoaded() { // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); - + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } + // then assertFalse(testPropertyLoader.properties.isEmpty()); } @@ -43,7 +47,11 @@ public void testPriority() { int expectedValue = 1; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.priority(); // then @@ -54,10 +62,15 @@ public void testPriority() { public void testLoadersAreEqual() { // given int expectedValue = 0; + StandardEsapiPropertyLoader otherPropertyLoader = null; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); - StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + otherPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.compareTo(otherPropertyLoader); // then @@ -69,10 +82,15 @@ public void testCompareWithOtherLoaderWithHigherPriority() { // given int expectedValue = -1; int higherPriority = 2; + StandardEsapiPropertyLoader otherPropertyLoader = null; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); - StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, higherPriority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + otherPropertyLoader = new StandardEsapiPropertyLoader(filename, higherPriority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.compareTo(otherPropertyLoader); // then @@ -84,10 +102,15 @@ public void testCompareWithOtherLoaderWithLowerPriority() { // given int expectedValue = 1; int lowerPriority = 0; + StandardEsapiPropertyLoader otherPropertyLoader = null; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); - StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, lowerPriority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + otherPropertyLoader = new StandardEsapiPropertyLoader(filename, lowerPriority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.compareTo(otherPropertyLoader); // then @@ -100,7 +123,11 @@ public void testGetIntProp() { String propertyKey = "int_property"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int propertyValue = testPropertyLoader.getIntProp(propertyKey); // then @@ -113,7 +140,11 @@ public void testIntPropertyNotFound() throws ConfigurationException { String propertyKey = "non-existing-key"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getIntProp(propertyKey); // then expect exception @@ -125,7 +156,11 @@ public void testIncorrectIntPropertyType() { String key = "invalid_int_property"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getIntProp(key); // then expect exception @@ -138,7 +173,11 @@ public void testGetStringProp() { String expectedValue = "test_string_property"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } String propertyValue = testPropertyLoader.getStringProp(propertyKey); // then @@ -151,7 +190,11 @@ public void testStringPropertyNotFound() throws ConfigurationException { String propertyKey = "non-existing-key"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getStringProp(propertyKey); // then expect exception @@ -167,7 +210,11 @@ public void testGetBooleanProp() { boolean expectedValue = true; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } boolean value = testPropertyLoader.getBooleanProp(propertyKey); // then @@ -181,7 +228,11 @@ public void testGetBooleanYesProperty() { boolean expectedValue = true; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } boolean value = testPropertyLoader.getBooleanProp(key); // then @@ -195,7 +246,11 @@ public void testGetBooleanNoProperty() { boolean expectedValue = false; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } boolean value = testPropertyLoader.getBooleanProp(key); // then @@ -205,12 +260,17 @@ public void testGetBooleanNoProperty() { @Test(expected = ConfigurationException.class) public void testBooleanPropertyNotFound() throws ConfigurationException { // given - String filename = "src" + File.separator + "test" + File.separator + "src/main/resources" + File.separator + - "esapi" + File.separator + "ESAPI-test.properties"; int priority = 1; + String filename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.properties"; + int priority = 1; String propertyKey = "non-existing-key"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getBooleanProp(propertyKey); // then expect exception @@ -222,7 +282,11 @@ public void testIncorrectBooleanPropertyType() throws ConfigurationException { String key = "invalid_boolean_property"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getBooleanProp(key); // then expect exception @@ -244,7 +308,11 @@ public void testGetByteArrayProp() { } // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } byte[] value = testPropertyLoader.getByteArrayProp(propertyKey); // then @@ -254,12 +322,17 @@ public void testGetByteArrayProp() { @Test(expected = ConfigurationException.class) public void testByteArrayPropertyNotFound() throws ConfigurationException { // given - String filename = "src" + File.separator + "test" + File.separator + "src/main/resources" + File.separator + + String filename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "esapi" + File.separator + "ESAPI-test.properties"; int priority = 1; String propertyKey = "non-existing-key"; // when - testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } + testPropertyLoader.getByteArrayProp(propertyKey); // then expect exception diff --git a/src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java b/src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java index 9a60f182b..2f1a618ec 100644 --- a/src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java +++ b/src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java @@ -30,22 +30,33 @@ public void init() { @Test public void testPropertiesLoaded() { // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } // then assertFalse(testPropertyLoader.properties.isEmpty()); } - @Test(expected = ConfigurationException.class) + @Test public void testInvalidPropertyFile() { - // given + // given - the file exists, but does not conform to the schema. String invalidFilename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "esapi" + File.separator + "ESAPI-test-invalid-content.xml"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(invalidFilename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(invalidFilename, priority); + } catch ( IOException iex ) { + // iex.printStackTrace(System.err); + fail("Caught unexpected IOException; exception was: " + iex); + } catch ( ConfigurationException cex) { + return; + } - // then expect exception + fail("Failed to catch expected ConfigurationException for invalid property file name: " + invalidFilename); } @Test @@ -54,7 +65,11 @@ public void testPriority() { int expectedValue = 1; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.priority(); // then @@ -65,10 +80,15 @@ public void testPriority() { public void testLoadersAreEqual() { // given int expectedValue = 0; + StandardEsapiPropertyLoader otherPropertyLoader = null; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); - StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + otherPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.compareTo(otherPropertyLoader); // then @@ -80,10 +100,15 @@ public void testCompareWithOtherLoaderWithHigherPriority() { // given int expectedValue = -1; int higherPriority = 2; + StandardEsapiPropertyLoader otherPropertyLoader = null; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); - StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, higherPriority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + otherPropertyLoader = new StandardEsapiPropertyLoader(filename, higherPriority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.compareTo(otherPropertyLoader); // then @@ -95,10 +120,15 @@ public void testCompareWithOtherLoaderWithLowerPriority() { // given int expectedValue = 1; int lowerPriority = 0; + StandardEsapiPropertyLoader otherPropertyLoader = null; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); - StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, lowerPriority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + otherPropertyLoader = new StandardEsapiPropertyLoader(filename, lowerPriority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.compareTo(otherPropertyLoader); // then @@ -112,7 +142,11 @@ public void testGetIntProp() { int expectedValue = 5; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } int value = testPropertyLoader.getIntProp(key); // then @@ -125,7 +159,11 @@ public void testIntPropertyNotFound() throws ConfigurationException { String key = "non-existing-key"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getIntProp(key); // then expect exception @@ -137,7 +175,11 @@ public void testIncorrectIntPropertyType() { String key = "invalid_int_property"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getIntProp(key); // then expect exception @@ -150,7 +192,11 @@ public void testGetStringProp() { String expectedValue = "test_string_property"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } String value = testPropertyLoader.getStringProp(key); // then @@ -163,7 +209,11 @@ public void testStringPropertyNotFound() throws ConfigurationException { String key = "non-existing-key"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getStringProp(key); // then expect exception @@ -176,7 +226,11 @@ public void testGetBooleanProp() { boolean expectedValue = true; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } boolean value = testPropertyLoader.getBooleanProp(key); // then @@ -190,7 +244,11 @@ public void testGetBooleanYesProperty() { boolean expectedValue = true; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } boolean value = testPropertyLoader.getBooleanProp(key); // then @@ -204,7 +262,11 @@ public void testGetBooleanNoProperty() { boolean expectedValue = false; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } boolean value = testPropertyLoader.getBooleanProp(key); // then @@ -217,7 +279,11 @@ public void testBooleanPropertyNotFound() throws ConfigurationException { String key = "non-existing-key"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getBooleanProp(key); // then expect exception @@ -229,7 +295,11 @@ public void testIncorrectBooleanPropertyType() throws ConfigurationException { String key = "invalid_boolean_property"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getBooleanProp(key); // then expect exception @@ -247,7 +317,11 @@ public void testGetByteArrayProp() { } // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } byte[] value = testPropertyLoader.getByteArrayProp(key); // then @@ -260,7 +334,11 @@ public void testByteArrayPropertyNotFound() throws ConfigurationException { String key = "non-existing-key"; // when - testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + try { + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + } catch ( IOException e ) { + fail( e.getMessage() ); + } testPropertyLoader.getByteArrayProp(key); // then expect exception diff --git a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java index 3bc00bd05..b24ffa5bc 100644 --- a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java +++ b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java @@ -394,12 +394,60 @@ public void testGetMaxLogFileSize() { secConf = this.createWithProperty(DefaultSecurityConfiguration.MAX_LOG_FILE_SIZE, String.valueOf(maxLogSize)); assertEquals(maxLogSize, secConf.getMaxLogFileSize()); } - + + @Test + public void testNoSuchPropFile(){ + try { + // Do NOT create a file by this name!!! -----vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration("NoSuchEsapiPropFileXyzzy.properties"); + } + catch( ConfigurationException cex ) { + assertNotNull("Caught exception with null exception msg", cex.getMessage() ); + assertFalse("Exception msg is empty string", cex.getMessage().equals("") ); + } + catch( Throwable t ) { + fail("testNoSuchPropFile(): Unexpected exception type: " + t.getClass().getName() + "; ex msg: " + t); + } + + } + private String patternOrNull(Pattern p){ return null==p?null:p.pattern(); } @Test + public void testRootCPLoading(){ + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration("ESAPI-root-cp.properties"); + assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); + assertNull(secConf.getValidationPattern("Test2")); + assertNull(secConf.getValidationPattern("TestC")); + } + + @Test + public void testRootCPLoadingAlt(){ + // This should work also via the class loader. + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration("esapi/ESAPI-SingleValidatorFileChecker.properties"); + assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); + assertNull(secConf.getValidationPattern("Test2")); + assertNull(secConf.getValidationPattern("TestC")); + } + + @Test + public void testRootCPLoadingAlt2(){ + try { + // This should fail, because of the '/' on the resourse. + DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration("/ESAPI-root-cp.properties"); + } + catch( ConfigurationException cex ) { + assertNotNull("Caught exception with null exception msg", cex.getMessage() ); + assertFalse("Exception msg is empty string", cex.getMessage().equals("") ); + } + catch( Throwable t ) { + fail("testNoSuchPropFile(): Unexpected exception type: " + t.getClass().getName() + "; ex msg: " + t); + } + } + + @Test public void testValidationsPropertiesFileOptions(){ DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration("ESAPI-SingleValidatorFileChecker.properties"); assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); @@ -424,7 +472,7 @@ public void testValidationsPropertiesFileOptions(){ @Test public void DefaultSearchPathTest(){ - assertEquals("/", DefaultSearchPath.ROOT.value()); + assertEquals("", DefaultSearchPath.ROOT.value()); assertEquals("resourceDirectory/", DefaultSearchPath.RESOURCE_DIRECTORY.value()); assertEquals(".esapi/", DefaultSearchPath.DOT_ESAPI.value()); assertEquals("esapi/", DefaultSearchPath.ESAPI.value()); diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index d78f2f38a..d342a470e 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -285,6 +285,22 @@ public void testIsInvalidFilename() { assertFalse("Filennames cannot be the empty string", instance.isValidFileName("test", "", false)); } + // Reset 'parent' depending on where Windows is installed so running off + // different drive doesn't break tests in testIsValidDirectoryPath(). + private File resetParentForWindows(String sysRoot) throws IOException { + if ( sysRoot == null ) { + return new File("C:\\"); + } + int bslash = sysRoot.indexOf('\\'); + String winRoot = null; + if ( bslash == -1 || sysRoot.length() < 4 ) { + winRoot = "C:\\"; // Well, that's a first. Just pretend it's under C:\. + } else { + winRoot = sysRoot.substring(0, bslash + 1); + } + return new File( winRoot ); + } + public void testIsValidDirectoryPath() throws IOException { System.out.println("isValidDirectoryPath"); @@ -301,6 +317,10 @@ public void testIsValidDirectoryPath() throws IOException { if (isWindows) { String sysRoot = new File(System.getenv("SystemRoot")).getCanonicalPath(); + + // Reset 'parent' in case running from drive other than where Windows installed. + parent = resetParentForWindows( sysRoot ); + // Windows paths that don't exist and thus should fail assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false)); assertFalse(instance.isValidDirectoryPath("test", "c:\\jeff", parent, false)); diff --git a/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java b/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java index 4657e4a0a..9a44b25e8 100644 --- a/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java +++ b/src/test/java/org/owasp/esapi/util/ObjFactoryTest.java @@ -214,6 +214,14 @@ public void testObjFactoryCache() throws Exception { je = (org.owasp.esapi.reference.crypto.JavaEncryptor) ObjFactory.make(clz, "JavaEncryptor"); assertNotNull( je ); } +/* + * TODO - Replace all this with the JMH benchmark harness stuff as similar to + * what is in ObjFactoryBenchmark. See GitHub issue #498 for further details. + * Unfortunately, we need to do this until then because JIT manipulations are + * sometimes making the cache disabled time take less than with caching enabled. + * In fact, when we start using JMH for this, we probably ought to move this + * entire test to ObjFactoryBenchmark. + * long stopCacheDisabled = System.nanoTime(); long durationEnabled = stopCacheEnabled - startCacheEnabled; @@ -222,5 +230,6 @@ public void testObjFactoryCache() throws Exception { System.out.println("testObjFactoryCache: Time with cache DISABLED (nanosec): " + durationDisabled ); assertTrue( durationEnabled < durationDisabled ); + */ } } diff --git a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties index e9bafc134..8f171b048 100644 --- a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties @@ -24,7 +24,7 @@ # in the meantime, you will have to get it from GitHub. # # PLEASE do not base your production use of ESAPI on this TEST version of -# ESAPI.properties as this test version has been dummed down in several places +# ESAPI.properties as this test version has been dumbed down in several places # for JUnit testing. # # You have been warned. diff --git a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties index c53476642..16954de43 100644 --- a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties @@ -24,7 +24,7 @@ # in the meantime, you will have to get it from GitHub. # # PLEASE do not base your production use of ESAPI on this TEST version of -# ESAPI.properties as this test version has been dummed down in several places +# ESAPI.properties as this test version has been dumbed down in several places # for JUnit testing. # # You have been warned. diff --git a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties index b868c6f6d..fd83e746d 100644 --- a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties @@ -24,7 +24,7 @@ # in the meantime, you will have to get it from GitHub. # # PLEASE do not base your production use of ESAPI on this TEST version of -# ESAPI.properties as this test version has been dummed down in several places +# ESAPI.properties as this test version has been dumbed down in several places # for JUnit testing. # # You have been warned. diff --git a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties index 3946d4395..63a8c83cd 100644 --- a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties +++ b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties @@ -24,7 +24,7 @@ # in the meantime, you will have to get it from GitHub. # # PLEASE do not base your production use of ESAPI on this TEST version of -# ESAPI.properties as this test version has been dummed down in several places +# ESAPI.properties as this test version has been dumbed down in several places # for JUnit testing. # # You have been warned. diff --git a/src/test/resources/esapi/ESAPI-root-cp.properties b/src/test/resources/esapi/ESAPI-root-cp.properties new file mode 100644 index 000000000..aef298618 --- /dev/null +++ b/src/test/resources/esapi/ESAPI-root-cp.properties @@ -0,0 +1,4 @@ +# Test ESAPI properties file for JUnit test DefaultSecurityConfigurationTest.testRootCPLoading(). +# Only need this one property for that JUnit test, which tests if this property +# file is found in the root path of the CLASSPATH. +Validator.ConfigurationFile=validation-test1.properties diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 512998756..8f5d8e218 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -24,7 +24,7 @@ # in the meantime, you will have to get it from GitHub. # # PLEASE do not base your production use of ESAPI on this TEST version of -# ESAPI.properties as this test version has been dummed down in several places +# ESAPI.properties as this test version has been dumbed down in several places # for JUnit testing. # # You have been warned. From b9f1f87ad767a1985b4d528ebf04b899cdead255 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 24 Jun 2019 19:24:41 -0400 Subject: [PATCH 003/544] Update esapi4java-core-2.2.0.0-release-notes.txt with current date. Changed release date from 6/23/2019 to 6/24/2019. --- documentation/esapi4java-core-2.2.0.0-release-notes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.2.0.0-release-notes.txt b/documentation/esapi4java-core-2.2.0.0-release-notes.txt index 64bd3598e..8deafbb9d 100644 --- a/documentation/esapi4java-core-2.2.0.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.0.0-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.2.0.0 - Release date: 2019-June-23 + Release date: 2019-June-24 Project leaders: -Kevin W. Wall -Matt Seil From c19f74b91de5d5bd6ad6c0f7440b96e71c7da074 Mon Sep 17 00:00:00 2001 From: kwwall Date: Tue, 25 Jun 2019 00:31:33 -0400 Subject: [PATCH 004/544] Prep for next snapshot release. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b92829dd2..ab3973fde 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.2.0.0 + 2.3.0.0-SNAPSHOT jar From 4149ec23a9f04d4a9f76d25f6612a60a5a6dd5ad Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 28 Jun 2019 23:00:22 -0400 Subject: [PATCH 005/544] Rename 'src/util' directory to 'scripts' directory. --- {src/util => scripts}/README.txt | 0 {src/util => scripts}/esapi-release.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {src/util => scripts}/README.txt (100%) rename {src/util => scripts}/esapi-release.sh (100%) diff --git a/src/util/README.txt b/scripts/README.txt similarity index 100% rename from src/util/README.txt rename to scripts/README.txt diff --git a/src/util/esapi-release.sh b/scripts/esapi-release.sh similarity index 100% rename from src/util/esapi-release.sh rename to scripts/esapi-release.sh From e0934baeade527699a07fc39c5a31def57aa6e2c Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 28 Jun 2019 23:32:48 -0400 Subject: [PATCH 006/544] Changes to make Mr. Wichers a happy camper! :) --- scripts/README.txt | 24 +++++------------------- scripts/esapi-release.sh | 5 +++++ scripts/mvnQuietTest.bat | 8 ++++++++ scripts/mvnQuietTest.sh | 8 ++++++++ 4 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 scripts/mvnQuietTest.bat create mode 100644 scripts/mvnQuietTest.sh diff --git a/scripts/README.txt b/scripts/README.txt index 8fe3ea31b..94aeeeffe 100644 --- a/scripts/README.txt +++ b/scripts/README.txt @@ -1,22 +1,8 @@ -This directory is for utilities used for building / packaging / releasing -ESAPI. +This directory is for utilities used for building / packaging / releasing ESAPI. ======================== -Jim Manico's instructions for creating changelog.txt: - -This is an ECLIPSE plug-in feature, not a SVN feature. - -I use the SUBCLIPSE plugin. - -Right click project root - -TEAM -> SHOW HISTORY - -Then from the history table, I see a list of history entries that represent -our checking. - -I select a few rows, right click, then pick CHANGELOG. - - -(Note: The SVN Subversive plug-in does NOT support this.) \ No newline at end of file +README.txt -- This readme file. +esapi-release.sh -- Obsolete script to create new ESAPI release. Will be replaced soon. Do not use for now. +mvnQuietTest.bat -- Run 'mvn test' from DOS cmd prompt with logSpecial output suppressed. +mvnQuietTest.sh -- Run 'mvn test' from bash with logSpecial output suppressed. diff --git a/scripts/esapi-release.sh b/scripts/esapi-release.sh index d89d3b3cc..88c78e2fa 100755 --- a/scripts/esapi-release.sh +++ b/scripts/esapi-release.sh @@ -39,6 +39,11 @@ # Author: kevin.w.wall@gmail.com ############################################################################ +echo $0: This script is obsolete and will be replaced soon. +echo In the meantime, read through the release instructions in: +echo " documentation/ESAPI-release-steps.odt" +exit 2 + # # Tunable parameters # diff --git a/scripts/mvnQuietTest.bat b/scripts/mvnQuietTest.bat new file mode 100644 index 000000000..ccf76dcb1 --- /dev/null +++ b/scripts/mvnQuietTest.bat @@ -0,0 +1,8 @@ +@ECHO off +rem Purpose: Run 'mvn test' with system property +rem 'org.owasp.esapi.logSpecial.discard' +rem set to true, so that all of the logSpecial output is suppressed. +rem This reduces the total output of 'mvn test' by about 2000 or so +rem lines. + +mvn -Dorg.owasp.esapi.logSpecial.discard=true test %* diff --git a/scripts/mvnQuietTest.sh b/scripts/mvnQuietTest.sh new file mode 100644 index 000000000..b66ca2913 --- /dev/null +++ b/scripts/mvnQuietTest.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Purpose: Run 'mvn test' with system property +# 'org.owasp.esapi.logSpecial.discard' +# set to true, so that all of the logSpecial output is suppressed. +# This reduces the total output of 'mvn test' by about 2000 or so +# lines. + +exec mvn -Dorg.owasp.esapi.logSpecial.discard=true test $@ From 33a0b3287cf0259ae0e7c7d490e747dcf83b5ce7 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Fri, 5 Jul 2019 18:12:52 -0400 Subject: [PATCH 007/544] Create a SECURITY.md file. --- SECURITY.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..f4d5ecb55 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,42 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 2.2.0.0 | :white_check_mark: | +| 2.1.0.1 | :x:, upgrade to 2.2.0.0| +| <= 1.4.x | :x:, no longer supported AT ALL | + +## Reporting a Vulnerability + +If you believe that you have found a vulnerability in ESAPI, first please search the +GitHut issues list (for both open and closed issues) to see if it has already been reported. + +If it has not, then please contact **both** of the project leaders, Kevin W. Wall +(kevin.w.wall at gmail.com) and Matt Seil (matt.seil at owasp.org) _directly_. +Please do **not** report any suspected vulnerabilities via GitHub issues +or via the ESAPI mailing lists as we wish to keep our users secure while a patch +is implemented and deployed. This is because if this is reported as a GitHub +issue or posted to either ESAPI mailing list, it more or less is equivalent to +dropping a 0-day on all applications using ESAPI. Instead, we encourage +responsible disclosure. + +If you wish to be acknowledged for finding the vulnerability, then please follow +this process. One of the 2 ESAPI project leaders will try to contact you within +at least 5 business days, so when you post the email describing the +vulnerability, please do so from an email address that you usually monitor. +If you eventually wish to have it published as a CVE, we will also work with you +to ensure that you are given proper credit with MITRE and NIST. Even if you do +not wish to report the vulnerability as a CVE, we will acknowledge you when we +create a GitHub issue (once the issue is patched) as well as acknowledging you +in any security bulletin that we may write up and use to notify our users. (If you wish +to have your identity remain unknown, or perhaps you email address, we can work +with you on that as well.) + +If possible, provide a working proof-of-concept or at least minimally describe +how it can be exploited in sufficient details that the ESAPI development team +can understand what needs to be done to fix it. Unfortunately at this time, we +are not in a position to pay out bug bounties for vulnerabilities. + +Eventually, we would like to have BugCrowd handle this, but that's still a ways off. From cf314fb3a40279df8ef6e67c68f42b8196bc2521 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Fri, 5 Jul 2019 18:15:26 -0400 Subject: [PATCH 008/544] Add reference to SECURITY.md in section about reporting vulnerbilities. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 172027862..5371fccca 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ NOTE: Please do NOT use GitHub issues to ask questions about ESAPI. If you wish ### Find a Vulnerability? If you have found a vulnerability in ESAPI legacy, first search the issues list (see above) to see if it has already been reported. If it has not, then please contact both Kevin W. Wall (kevin.w.wall at gmail.com) and Matt Seil (matt.seil at owasp.org) directly. Please do not report vulnerabilities via GitHub issues or via the ESAPI mailing lists as we wish to keep our users secure while a patch is implemented and deployed. If you wish to be acknowledged for finding the vulnerability, then please follow this process. (Eventually, we would like to have BugCrowd handle this, but that's still a ways off.) Also, when you post the email describing the vulnerability, please do so from an email address that you usually monitor. +More detail is available in the file '[SECURITY.md](https://raw.githubusercontent.com/ESAPI/esapi-java-legacy/develop/SECURITY.md)'. + ## Where to Find More Information on ESAPI *Wiki:* https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API From a887b7becef9519c6b80eb4f51cfca380b9882f8 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 7 Jul 2019 15:30:24 -0400 Subject: [PATCH 009/544] Close issue #256. White-space clean up. --- .../esapi/reference/crypto/JavaEncryptor.java | 1240 ++++++++--------- 1 file changed, 620 insertions(+), 620 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java index bb3dd163a..64d3e7561 100644 --- a/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java +++ b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java @@ -6,10 +6,10 @@ * http://www.owasp.org/index.php/ESAPI. * * Copyright (c) 2007 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Jeff Williams Aspect Security * @author kevin.w.wall@gmail.com * @created 2007 @@ -41,7 +41,7 @@ import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; -// import javax.crypto.Mac; // Uncomment if computeHMAC() is included. +// import javax.crypto.Mac; // Uncomment if computeHMAC() is included. import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -70,7 +70,7 @@ * controlling the selection of this class is {@code ESAPI.Encryptor}. Most of * the other encryption related properties have property names that start with * the string "Encryptor.". - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @author kevin.w.wall@gmail.com @@ -96,126 +96,126 @@ public static Encryptor getInstance() throws EncryptionException { } private static boolean initialized = false; - + // encryption private static SecretKeySpec secretKeySpec = null; // DISCUSS: Why static? Implies one key?!? private static String encryptAlgorithm = "AES"; - private static String encoding = "UTF-8"; + private static String encoding = "UTF-8"; private static int encryptionKeyLength = 128; - + // digital signatures private static PrivateKey privateKey = null; - private static PublicKey publicKey = null; - private static String signatureAlgorithm = "SHA1withDSA"; + private static PublicKey publicKey = null; + private static String signatureAlgorithm = "SHA1withDSA"; private static String randomAlgorithm = "SHA1PRNG"; - private static int signatureKeyLength = 1024; - - // hashing - private static String hashAlgorithm = "SHA-512"; - private static int hashIterations = 1024; - - // Logging - DISCUSS: This "sticks" us with a specific logger to whatever it was when - // this class is first loaded. Is this a big limitation? Since there - // is no method to reset it, we may has well make it 'final' also. - private static Logger logger = ESAPI.getLogger("JavaEncryptor"); - // Used to print out warnings about deprecated methods. - private static int encryptCounter = 0; - private static int decryptCounter = 0; + private static int signatureKeyLength = 1024; + + // hashing + private static String hashAlgorithm = "SHA-512"; + private static int hashIterations = 1024; + + // Logging - DISCUSS: This "sticks" us with a specific logger to whatever it was when + // this class is first loaded. Is this a big limitation? Since there + // is no method to reset it, we may has well make it 'final' also. + private static Logger logger = ESAPI.getLogger("JavaEncryptor"); + // Used to print out warnings about deprecated methods. + private static int encryptCounter = 0; + private static int decryptCounter = 0; // DISCUSS: OK to not have a property for this to set the frequency? // The desire is to persuade people to move away from these - // two deprecated encrypt(String) / decrypt(String) methods, + // two deprecated encrypt(String) / decrypt(String) methods, // so perhaps the annoyance factor of not being able to // change it will help. For now, it is just hard-coded here. // We could be mean and just print a warning *every* time. - private static final int logEveryNthUse = 25; - + private static final int logEveryNthUse = 25; + // *Only* use this string for user messages for EncryptionException when // decryption fails. This is to prevent information leakage that may be // valuable in various forms of ciphertext attacks, such as the - // Padded Oracle attack described by Rizzo and Duong. + // Padded Oracle attack described by Rizzo and Duong. private static final String DECRYPTION_FAILED = "Decryption failed; see logs for details."; // # of seconds that all failed decryption attempts will take. Used to // help prevent side-channel timing attacks. private static int N_SECS = 2; - // Load the preferred JCE provider if one has been specified. - static { - try { + // Load the preferred JCE provider if one has been specified. + static { + try { SecurityProviderLoader.loadESAPIPreferredJCEProvider(); } catch (NoSuchProviderException ex) { - // Note that audit logging is done elsewhere in called method. + // Note that audit logging is done elsewhere in called method. logger.fatal(Logger.SECURITY_FAILURE, "JavaEncryptor failed to load preferred JCE provider.", ex); throw new ExceptionInInitializerError(ex); } setupAlgorithms(); - } - + } + /** * Generates a new strongly random secret key and salt that can be * copy and pasted in the ESAPI.properties file. - * + * * @param args Set first argument to "-print" to display available algorithms on standard output. - * @throws java.lang.Exception To cover a multitude of sins, mostly in configuring ESAPI.properties. + * @throws java.lang.Exception To cover a multitude of sins, mostly in configuring ESAPI.properties. */ public static void main( String[] args ) throws Exception { - System.out.println( "Generating a new secret master key" ); - - // print out available ciphers - if ( args.length == 1 && args[0].equalsIgnoreCase("-print" ) ) { - System.out.println( "AVAILABLE ALGORITHMS" ); - - Provider[] providers = Security.getProviders(); - TreeMap tm = new TreeMap(); - // DISCUSS: Note: We go through multiple providers, yet nowhere do I - // see where we print out the PROVIDER NAME. Not all providers - // will implement the same algorithms and some "partner" with - // whom we are exchanging different cryptographic messages may - // have _different_ providers in their java.security file. So - // it would be useful to know the provider name where each - // algorithm is implemented. Might be good to prepend the provider - // name to the 'key' with something like "providerName: ". Thoughts? - for (int i = 0; i != providers.length; i++) { - // DISCUSS: Print security provider name here??? - // Note: For some odd reason, Provider.keySet() returns - // Set of the property keys (which are Strings) - // contained in this provider, but Set seems - // more appropriate. But that's why we need the cast below. - System.out.println("===== Provider " + i + ":" + providers[i].getName() + " ======"); - Iterator it = providers[i].keySet().iterator(); - while (it.hasNext()) { - String key = (String)it.next(); - String value = providers[i].getProperty( key ); - tm.put(key, value); - System.out.println("\t\t " + key + " -> "+ value ); - } - } - - Set< Entry > keyValueSet = tm.entrySet(); - Iterator> it = keyValueSet.iterator(); - while( it.hasNext() ) { - Map.Entry entry = it.next(); - String key = entry.getKey(); - String value = entry.getValue(); - System.out.println( " " + key + " -> "+ value ); - } - } else { - // Used to print a similar line to use '-print' even when it was specified. - System.out.println( "\tuse '-print' to also show available crypto algorithms from all the security providers" ); - } - + System.out.println( "Generating a new secret master key" ); + + // print out available ciphers + if ( args.length == 1 && args[0].equalsIgnoreCase("-print" ) ) { + System.out.println( "AVAILABLE ALGORITHMS" ); + + Provider[] providers = Security.getProviders(); + TreeMap tm = new TreeMap(); + // DISCUSS: Note: We go through multiple providers, yet nowhere do I + // see where we print out the PROVIDER NAME. Not all providers + // will implement the same algorithms and some "partner" with + // whom we are exchanging different cryptographic messages may + // have _different_ providers in their java.security file. So + // it would be useful to know the provider name where each + // algorithm is implemented. Might be good to prepend the provider + // name to the 'key' with something like "providerName: ". Thoughts? + for (int i = 0; i != providers.length; i++) { + // DISCUSS: Print security provider name here??? + // Note: For some odd reason, Provider.keySet() returns + // Set of the property keys (which are Strings) + // contained in this provider, but Set seems + // more appropriate. But that's why we need the cast below. + System.out.println("===== Provider " + i + ":" + providers[i].getName() + " ======"); + Iterator it = providers[i].keySet().iterator(); + while (it.hasNext()) { + String key = (String)it.next(); + String value = providers[i].getProperty( key ); + tm.put(key, value); + System.out.println("\t\t " + key + " -> "+ value ); + } + } + + Set< Entry > keyValueSet = tm.entrySet(); + Iterator> it = keyValueSet.iterator(); + while( it.hasNext() ) { + Map.Entry entry = it.next(); + String key = entry.getKey(); + String value = entry.getValue(); + System.out.println( " " + key + " -> "+ value ); + } + } else { + // Used to print a similar line to use '-print' even when it was specified. + System.out.println( "\tuse '-print' to also show available crypto algorithms from all the security providers" ); + } + // setup algorithms -- Each of these have defaults if not set, although - // someone could set them to something invalid. If - // so a suitable exception will be thrown and displayed. + // someone could set them to something invalid. If + // so a suitable exception will be thrown and displayed. encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm(); - encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength(); - randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm(); + encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength(); + randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm(); - SecureRandom random = SecureRandom.getInstance(randomAlgorithm); - SecretKey secretKey = CryptoHelper.generateSecretKey(encryptAlgorithm, encryptionKeyLength); + SecureRandom random = SecureRandom.getInstance(randomAlgorithm); + SecretKey secretKey = CryptoHelper.generateSecretKey(encryptAlgorithm, encryptionKeyLength); byte[] raw = secretKey.getEncoded(); - byte[] salt = new byte[20]; // Or 160-bits; big enough for SHA1, but not SHA-256 or SHA-512. + byte[] salt = new byte[20]; // Or 160-bits; big enough for SHA1, but not SHA-256 or SHA-512. random.nextBytes( salt ); String eol = System.getProperty("line.separator", "\n"); // So it works on Windows too. System.out.println( eol + "Copy and paste these lines into your ESAPI.properties" + eol); @@ -224,12 +224,12 @@ public static void main( String[] args ) throws Exception { System.out.println( "Encryptor.MasterSalt=" + ESAPI.encoder().encodeForBase64(salt, false) ); System.out.println( "#==============================================================" + eol); } - - + + /** * Private CTOR for {@code JavaEncryptor}, called by {@code getInstance()}. * @throws EncryptionException if can't construct this object for some reason. - * Original exception will be attached as the 'cause'. + * Original exception will be attached as the 'cause'. */ private JavaEncryptor() throws EncryptionException { byte[] salt = ESAPI.securityConfiguration().getMasterSalt(); @@ -252,7 +252,7 @@ private JavaEncryptor() throws EncryptionException { throw new ConfigurationException("Encryptor.MasterKey must be at least 7 bytes. " + "Length is: " + skey.length + " bytes."); } - + // Set up secretKeySpec for use for symmetric encryption and decryption, // and set up the public/private keys for asymmetric encryption / // decryption. @@ -275,7 +275,7 @@ private JavaEncryptor() throws EncryptionException { // handle in a static initializer, it just seems to // fit better here. secretKeySpec = new SecretKeySpec(skey, encryptAlgorithm ); - + // // For asymmetric encryption (i.e., public/private key) // @@ -291,93 +291,93 @@ private JavaEncryptor() throws EncryptionException { initKeyPair(prng); } catch (Exception e) { throw new EncryptionException("Encryption failure", "Error creating Encryptor", e); - } - + } + // Mark everything as initialized. initialized = true; } } } - - /** + + /** * {@inheritDoc} - * - * Hashes the data with the supplied salt and the number of iterations specified in - * the ESAPI SecurityConfiguration. - */ - public String hash(String plaintext, String salt) throws EncryptionException { - return hash( plaintext, salt, hashIterations ); - } - - /** + * + * Hashes the data with the supplied salt and the number of iterations specified in + * the ESAPI SecurityConfiguration. + */ + public String hash(String plaintext, String salt) throws EncryptionException { + return hash( plaintext, salt, hashIterations ); + } + + /** * {@inheritDoc} - * - * Hashes the data using the specified algorithm and the Java MessageDigest class. This method - * first adds the salt, a separator (":"), and the data, and then rehashes the specified number of iterations - * in order to help strengthen weak passwords. - */ - public String hash(String plaintext, String salt, int iterations) throws EncryptionException { - byte[] bytes = null; - try { - MessageDigest digest = MessageDigest.getInstance(hashAlgorithm); - digest.reset(); - digest.update(ESAPI.securityConfiguration().getMasterSalt()); - digest.update(salt.getBytes(encoding)); - digest.update(plaintext.getBytes(encoding)); - - // rehash a number of times to help strengthen weak passwords - bytes = digest.digest(); - for (int i = 0; i < iterations; i++) { - digest.reset(); - bytes = digest.digest(bytes); - } - String encoded = ESAPI.encoder().encodeForBase64(bytes,false); - return encoded; - } catch (NoSuchAlgorithmException e) { - throw new EncryptionException("Internal error", "Can't find hash algorithm " + hashAlgorithm, e); - } catch (UnsupportedEncodingException ex) { - throw new EncryptionException("Internal error", "Can't find encoding for " + encoding, ex); - } - } - - /** - * {@inheritDoc} - */ - public CipherText encrypt(PlainText plaintext) throws EncryptionException { - // Now more of a convenience function for using the master key. - return encrypt(secretKeySpec, plaintext); - } - - /** - * {@inheritDoc} - */ - public CipherText encrypt(SecretKey key, PlainText plain) - throws EncryptionException - { - if ( key == null ) { - throw new IllegalArgumentException("(Master) encryption key arg may not be null. Is Encryptor.MasterKey set?"); - } - if ( plain == null ) { - throw new IllegalArgumentException("PlainText may arg not be null"); - } - byte[] plaintext = plain.asBytes(); - boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText(); - - boolean success = false; // Used in 'finally' clause. - String xform = null; - int keySize = key.getEncoded().length * 8; // Convert to # bits - - try { - xform = ESAPI.securityConfiguration().getCipherTransformation(); + * + * Hashes the data using the specified algorithm and the Java MessageDigest class. This method + * first adds the salt, a separator (":"), and the data, and then rehashes the specified number of iterations + * in order to help strengthen weak passwords. + */ + public String hash(String plaintext, String salt, int iterations) throws EncryptionException { + byte[] bytes = null; + try { + MessageDigest digest = MessageDigest.getInstance(hashAlgorithm); + digest.reset(); + digest.update(ESAPI.securityConfiguration().getMasterSalt()); + digest.update(salt.getBytes(encoding)); + digest.update(plaintext.getBytes(encoding)); + + // rehash a number of times to help strengthen weak passwords + bytes = digest.digest(); + for (int i = 0; i < iterations; i++) { + digest.reset(); + bytes = digest.digest(bytes); + } + String encoded = ESAPI.encoder().encodeForBase64(bytes,false); + return encoded; + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException("Internal error", "Can't find hash algorithm " + hashAlgorithm, e); + } catch (UnsupportedEncodingException ex) { + throw new EncryptionException("Internal error", "Can't find encoding for " + encoding, ex); + } + } + + /** + * {@inheritDoc} + */ + public CipherText encrypt(PlainText plaintext) throws EncryptionException { + // Now more of a convenience function for using the master key. + return encrypt(secretKeySpec, plaintext); + } + + /** + * {@inheritDoc} + */ + public CipherText encrypt(SecretKey key, PlainText plain) + throws EncryptionException + { + if ( key == null ) { + throw new IllegalArgumentException("(Master) encryption key arg may not be null. Is Encryptor.MasterKey set?"); + } + if ( plain == null ) { + throw new IllegalArgumentException("PlainText may arg not be null"); + } + byte[] plaintext = plain.asBytes(); + boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText(); + + boolean success = false; // Used in 'finally' clause. + String xform = null; + int keySize = key.getEncoded().length * 8; // Convert to # bits + + try { + xform = ESAPI.securityConfiguration().getCipherTransformation(); String[] parts = xform.split("/"); if ( parts.length != 3 ) { throw new ConfigurationException("Malformed cipher transformation: " + xform + ". Should have format of cipher_alg/cipher_mode/padding_scheme."); } String cipherMode = parts[1]; - + // This way we can prevent modes like OFB and CFB where the IV should never // be repeated with the same encryption key (at least until we support // Encryptor.ChooseIVMethod=specified and allow us to specify some mechanism @@ -389,19 +389,19 @@ public CipherText encrypt(SecretKey key, PlainText plain) "Encryption failure: Cipher transformation " + xform + " specifies invalid " + "cipher mode " + cipherMode); } - - // Note - Cipher is not thread-safe so we create one locally - // Also, we need to change this eventually so other algorithms can - // be supported. Eventually, there will be an encrypt() method that - // takes a (new class) CryptoControls, as something like this: - // public CipherText encrypt(CryptoControls ctrl, SecretKey skey, PlainText plaintext) - // and this method will just call that one. - Cipher encrypter = Cipher.getInstance(xform); - String cipherAlg = encrypter.getAlgorithm(); + + // Note - Cipher is not thread-safe so we create one locally + // Also, we need to change this eventually so other algorithms can + // be supported. Eventually, there will be an encrypt() method that + // takes a (new class) CryptoControls, as something like this: + // public CipherText encrypt(CryptoControls ctrl, SecretKey skey, PlainText plaintext) + // and this method will just call that one. + Cipher encrypter = Cipher.getInstance(xform); + String cipherAlg = encrypter.getAlgorithm(); int minKeyLen = 112; // Use for hard-coded default to support 2TDEA try { - minKeyLen = ESAPI.securityConfiguration().getIntProp("Encryptor.MinEncryptionKeyLength"); + minKeyLen = ESAPI.securityConfiguration().getIntProp("Encryptor.MinEncryptionKeyLength"); } catch( Exception ex ) { logger.warning(Logger.EVENT_FAILURE, "Property 'Encryptor.MinEncryptionKeyLength' not properly set in ESAPI.properties file; using hard coded default of 112 for min key size for encryption.", @@ -409,251 +409,251 @@ public CipherText encrypt(SecretKey key, PlainText plain) ; // Do NOT rethrow. } - if ( keySize < minKeyLen ) { + if ( keySize < minKeyLen ) { // NOTE: This used to just log a warning. It now logs an error & throws an exception. // - // ESAPI.EncryptionKeyLength defaults to 128. This means that someone wants to use ESAPI to + // ESAPI.EncryptionKeyLength defaults to 128. This means that someone wants to use ESAPI to // encrypt with something like 2-key TDES, they would have to set this to that property // to 112 bits. - logger.error(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN MINIMUM allowed " + - "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits with cipher algorithm " + cipherAlg); - throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " + - "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits."); - } - if ( keySize < 112 ) { // NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030. - // Note that 112 bits 'just happens' to be size of 2-key Triple DES! So for example, if they + logger.error(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN MINIMUM allowed " + + "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits with cipher algorithm " + cipherAlg); + throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " + + "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits."); + } + if ( keySize < 112 ) { // NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030. + // Note that 112 bits 'just happens' to be size of 2-key Triple DES! So for example, if they // have configured ESAPI's Encryptor.EncryptionKeyLength to (say) 56 bits, we are going to // nag them like their mother! :) - logger.warning(Logger.SECURITY_FAILURE, "Potentially insecure encryption. Key size of " + keySize + "bits " + - "not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " + - "of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57."); - } - // Check if algorithm mentioned in SecretKey is same as that being used for Cipher object. - // They should be the same. If they are different, things could fail. (E.g., DES and DESede - // require keys with even parity. Even if key was sufficient size, if it didn't have the correct - // parity it could fail.) - // - String skeyAlg = key.getAlgorithm(); - if ( !( cipherAlg.startsWith( skeyAlg + "/" ) || cipherAlg.equals( skeyAlg ) ) ) { - // DISCUSS: Should we thrown a ConfigurationException here or just log a warning??? I'm game for - // either, but personally I'd prefer the squeaky wheel to the annoying throwing of - // a ConfigurationException (which is a RuntimeException). Less likely to upset - // the development community. - logger.warning(Logger.SECURITY_FAILURE, "Encryption mismatch between cipher algorithm (" + - cipherAlg + ") and SecretKey algorithm (" + skeyAlg + "). Cipher will use algorithm " + cipherAlg); - } - - byte[] ivBytes = null; - CipherSpec cipherSpec = new CipherSpec(encrypter, keySize); // Could pass the ACTUAL (intended) key size - + logger.warning(Logger.SECURITY_FAILURE, "Potentially insecure encryption. Key size of " + keySize + "bits " + + "not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " + + "of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57."); + } + // Check if algorithm mentioned in SecretKey is same as that being used for Cipher object. + // They should be the same. If they are different, things could fail. (E.g., DES and DESede + // require keys with even parity. Even if key was sufficient size, if it didn't have the correct + // parity it could fail.) + // + String skeyAlg = key.getAlgorithm(); + if ( !( cipherAlg.startsWith( skeyAlg + "/" ) || cipherAlg.equals( skeyAlg ) ) ) { + // DISCUSS: Should we thrown a ConfigurationException here or just log a warning??? I'm game for + // either, but personally I'd prefer the squeaky wheel to the annoying throwing of + // a ConfigurationException (which is a RuntimeException). Less likely to upset + // the development community. + logger.warning(Logger.SECURITY_FAILURE, "Encryption mismatch between cipher algorithm (" + + cipherAlg + ") and SecretKey algorithm (" + skeyAlg + "). Cipher will use algorithm " + cipherAlg); + } + + byte[] ivBytes = null; + CipherSpec cipherSpec = new CipherSpec(encrypter, keySize); // Could pass the ACTUAL (intended) key size + // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then // use the specified SecretKey as-is rather than computing a derived key from it. We also // don't expect a separate MAC in the specified CipherText object so therefore don't try // to validate it. boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( cipherMode ); SecretKey encKey = null; - if ( preferredCipherMode ) { - encKey = key; - } else { - encKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), - key, keySize, "encryption"); - } - - if ( cipherSpec.requiresIV() ) { - String ivType = ESAPI.securityConfiguration().getIVType(); - IvParameterSpec ivSpec = null; - if ( ivType.equalsIgnoreCase("random") ) { - ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize()); - } else if ( ivType.equalsIgnoreCase("fixed") ) { - String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV(); - ivBytes = Hex.decode(fixedIVAsHex); - /* FUTURE } else if ( ivType.equalsIgnoreCase("specified")) { - // FUTURE - TODO - Create instance of specified class to use for IV generation and - // use it to create the ivBytes. (The intent is to make sure that - // 1) IVs are never repeated for cipher modes like OFB and CFB, and - // 2) to screen for weak IVs for the particular cipher algorithm. - // In meantime, use 'random' for block cipher in feedback mode. Unlikely they will - // be repeated unless you are salting SecureRandom with same value each time. Anything - // monotonically increasing should be suitable, like a counter, but need to remember - // it across JVM restarts. Was thinking of using System.currentTimeMillis(). While - // it's not perfect it probably is good enough. Could even all (advanced) developers - // to define their own class to create a unique IV to allow them some choice, but - // definitely need to provide a safe, default implementation. - */ - } else { - // TODO: Update to add 'specified' once that is supported and added above. - throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'"); - } - ivSpec = new IvParameterSpec(ivBytes); - cipherSpec.setIV(ivBytes); - encrypter.init(Cipher.ENCRYPT_MODE, encKey, ivSpec); - } else { - encrypter.init(Cipher.ENCRYPT_MODE, encKey); - } - logger.debug(Logger.EVENT_SUCCESS, "Encrypting with " + cipherSpec); - byte[] raw = encrypter.doFinal(plaintext); + if ( preferredCipherMode ) { + encKey = key; + } else { + encKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), + key, keySize, "encryption"); + } + + if ( cipherSpec.requiresIV() ) { + String ivType = ESAPI.securityConfiguration().getIVType(); + IvParameterSpec ivSpec = null; + if ( ivType.equalsIgnoreCase("random") ) { + ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize()); + } else if ( ivType.equalsIgnoreCase("fixed") ) { + String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV(); + ivBytes = Hex.decode(fixedIVAsHex); + /* FUTURE } else if ( ivType.equalsIgnoreCase("specified")) { + // FUTURE - TODO - Create instance of specified class to use for IV generation and + // use it to create the ivBytes. (The intent is to make sure that + // 1) IVs are never repeated for cipher modes like OFB and CFB, and + // 2) to screen for weak IVs for the particular cipher algorithm. + // In meantime, use 'random' for block cipher in feedback mode. Unlikely they will + // be repeated unless you are salting SecureRandom with same value each time. Anything + // monotonically increasing should be suitable, like a counter, but need to remember + // it across JVM restarts. Was thinking of using System.currentTimeMillis(). While + // it's not perfect it probably is good enough. Could even all (advanced) developers + // to define their own class to create a unique IV to allow them some choice, but + // definitely need to provide a safe, default implementation. + */ + } else { + // TODO: Update to add 'specified' once that is supported and added above. + throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'"); + } + ivSpec = new IvParameterSpec(ivBytes); + cipherSpec.setIV(ivBytes); + encrypter.init(Cipher.ENCRYPT_MODE, encKey, ivSpec); + } else { + encrypter.init(Cipher.ENCRYPT_MODE, encKey); + } + logger.debug(Logger.EVENT_SUCCESS, "Encrypting with " + cipherSpec); + byte[] raw = encrypter.doFinal(plaintext); // Convert to CipherText. CipherText ciphertext = new CipherText(cipherSpec, raw); - - // If we are using a "preferred" cipher mode--i.e., one that supports *both* confidentiality and - // authenticity, there is no point to store a separate MAC in the CipherText object. Thus we only + + // If we are using a "preferred" cipher mode--i.e., one that supports *both* confidentiality and + // authenticity, there is no point to store a separate MAC in the CipherText object. Thus we only // do this when we are not using such a cipher mode. - if ( !preferredCipherMode ) { - // Compute derived key, and then use it to compute and store separate MAC in CipherText object. - SecretKey authKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), - key, keySize, "authenticity"); - ciphertext.computeAndStoreMAC( authKey ); - } - logger.debug(Logger.EVENT_SUCCESS, "JavaEncryptor.encrypt(SecretKey,byte[],boolean,boolean) -- success!"); - success = true; // W00t!!! - return ciphertext; - } catch (InvalidKeyException ike) { - throw new EncryptionException("Encryption failure: Invalid key exception.", - "Requested key size: " + keySize + "bits greater than 128 bits. Must install unlimited strength crypto extension from Sun: " + - ike.getMessage(), ike); - } catch (ConfigurationException cex) { - throw new EncryptionException("Encryption failure: Configuration error. Details in log.", "Key size mismatch or unsupported IV method. " + - "Check encryption key size vs. ESAPI.EncryptionKeyLength or Encryptor.ChooseIVMethod property.", cex); - } catch (InvalidAlgorithmParameterException e) { - throw new EncryptionException("Encryption failure (invalid IV)", - "Encryption problem: Invalid IV spec: " + e.getMessage(), e); - } catch (IllegalBlockSizeException e) { - throw new EncryptionException("Encryption failure (no padding used; invalid input size)", - "Encryption problem: Invalid input size without padding (" + xform + "). " + e.getMessage(), e); - } catch (BadPaddingException e) { - throw new EncryptionException("Encryption failure", - "[Note: Should NEVER happen in encryption mode.] Encryption problem: " + e.getMessage(), e); - } catch (NoSuchAlgorithmException e) { - throw new EncryptionException("Encryption failure (unavailable cipher requested)", - "Encryption problem: specified algorithm in cipher xform " + xform + " not available: " + e.getMessage(), e); - } catch (NoSuchPaddingException e) { - throw new EncryptionException("Encryption failure (unavailable padding scheme requested)", - "Encryption problem: specified padding scheme in cipher xform " + xform + " not available: " + e.getMessage(), e); - } finally { - // Don't overwrite anything in the case of exceptions because they may wish to retry. - if ( success && overwritePlaintext ) { - plain.overwrite(); // Note: Same as overwriting 'plaintext' byte array. + if ( !preferredCipherMode ) { + // Compute derived key, and then use it to compute and store separate MAC in CipherText object. + SecretKey authKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), + key, keySize, "authenticity"); + ciphertext.computeAndStoreMAC( authKey ); + } + logger.debug(Logger.EVENT_SUCCESS, "JavaEncryptor.encrypt(SecretKey,byte[],boolean,boolean) -- success!"); + success = true; // W00t!!! + return ciphertext; + } catch (InvalidKeyException ike) { + throw new EncryptionException("Encryption failure: Invalid key exception.", + "Requested key size: " + keySize + "bits greater than 128 bits. Must install unlimited strength crypto extension from Sun: " + + ike.getMessage(), ike); + } catch (ConfigurationException cex) { + throw new EncryptionException("Encryption failure: Configuration error. Details in log.", "Key size mismatch or unsupported IV method. " + + "Check encryption key size vs. ESAPI.EncryptionKeyLength or Encryptor.ChooseIVMethod property.", cex); + } catch (InvalidAlgorithmParameterException e) { + throw new EncryptionException("Encryption failure (invalid IV)", + "Encryption problem: Invalid IV spec: " + e.getMessage(), e); + } catch (IllegalBlockSizeException e) { + throw new EncryptionException("Encryption failure (no padding used; invalid input size)", + "Encryption problem: Invalid input size without padding (" + xform + "). " + e.getMessage(), e); + } catch (BadPaddingException e) { + throw new EncryptionException("Encryption failure", + "[Note: Should NEVER happen in encryption mode.] Encryption problem: " + e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException("Encryption failure (unavailable cipher requested)", + "Encryption problem: specified algorithm in cipher xform " + xform + " not available: " + e.getMessage(), e); + } catch (NoSuchPaddingException e) { + throw new EncryptionException("Encryption failure (unavailable padding scheme requested)", + "Encryption problem: specified padding scheme in cipher xform " + xform + " not available: " + e.getMessage(), e); + } finally { + // Don't overwrite anything in the case of exceptions because they may wish to retry. + if ( success && overwritePlaintext ) { + plain.overwrite(); // Note: Same as overwriting 'plaintext' byte array. } - } + } } - /** - * {@inheritDoc} - */ - public PlainText decrypt(CipherText ciphertext) throws EncryptionException { - // Now more of a convenience function for using the master key. - return decrypt(secretKeySpec, ciphertext); - } - - /** - * {@inheritDoc} - */ - public PlainText decrypt(SecretKey key, CipherText ciphertext) - throws EncryptionException, IllegalArgumentException - { - long start = System.nanoTime(); // Current time in nanosecs; used to prevent timing attacks - if ( key == null ) { - throw new IllegalArgumentException("SecretKey arg may not be null"); - } - if ( ciphertext == null ) { - throw new IllegalArgumentException("Ciphertext may arg not be null"); - } - - if ( ! CryptoHelper.isAllowedCipherMode(ciphertext.getCipherMode()) ) { - // This really should be an illegal argument exception, but it could - // mean that a partner encrypted something using a cipher mode that - // you do not accept, so it's a bit more complex than that. Also - // throwing an IllegalArgumentException doesn't allow us to provide - // the two separate error messages or automatically log it. - throw new EncryptionException(DECRYPTION_FAILED, - "Invalid cipher mode " + ciphertext.getCipherMode() + - " not permitted for decryption or encryption operations."); - } - logger.debug(Logger.EVENT_SUCCESS, - "Args valid for JavaEncryptor.decrypt(SecretKey,CipherText): " + - ciphertext); - - PlainText plaintext = null; - boolean caughtException = false; - int progressMark = 0; - try { - // First we validate the MAC. - boolean valid = CryptoHelper.isCipherTextMACvalid(key, ciphertext); - if ( !valid ) { - try { - // This is going to fail, but we want the same processing - // to occur as much as possible so as to prevent timing - // attacks. We _could_ just be satisfied by the additional - // sleep in the 'finally' clause, but an attacker on the - // same server who can run something like 'ps' can tell - // CPU time versus when the process is sleeping. Hence we - // try to make this as close as possible. Since we know - // it is going to fail, we ignore the result and ignore - // the (expected) exception. - handleDecryption(key, ciphertext); // Ignore return (should fail). - } catch(Exception ex) { - ; // Ignore - } - throw new EncryptionException(DECRYPTION_FAILED, - "Decryption failed because MAC invalid for " + - ciphertext); - } - progressMark++; - // The decryption only counts if the MAC was valid. - plaintext = handleDecryption(key, ciphertext); - progressMark++; - } catch(EncryptionException ex) { - caughtException = true; - String logMsg = null; - switch( progressMark ) { - case 1: - logMsg = "Decryption failed because MAC invalid. See logged exception for details."; - break; - case 2: - logMsg = "Decryption failed because handleDecryption() failed. See logged exception for details."; - break; - default: - logMsg = "Programming error: unexpected progress mark == " + progressMark; - break; - } - logger.error(Logger.SECURITY_FAILURE, logMsg); - throw ex; // Re-throw - } - finally { - if ( caughtException ) { - // The rest of this code is to try to account for any minute differences - // in the time it might take for the various reasons that decryption fails - // in order to prevent any other possible timing attacks. Perhaps it is - // going overboard. If nothing else, if N_SECS is large enough, it might - // deter attempted repeated attacks by making them take much longer. - long now = System.nanoTime(); - long elapsed = now - start; - final long NANOSECS_IN_SEC = 1000000000L; // nanosec is 10**-9 sec - long nSecs = N_SECS * NANOSECS_IN_SEC; // N seconds in nano seconds - if ( elapsed < nSecs ) { - // Want to sleep so total time taken is N seconds. - long extraSleep = nSecs - elapsed; - - // 'extraSleep' is in nanoseconds. Need to convert to a millisec - // part and nanosec part. Nanosec is 10**-9, millsec is - // 10**-3, so divide by (10**-9 / 10**-3), or 10**6 to - // convert to from nanoseconds to milliseconds. - long millis = extraSleep / 1000000L; - long nanos = (extraSleep - (millis * 1000000L)); + /** + * {@inheritDoc} + */ + public PlainText decrypt(CipherText ciphertext) throws EncryptionException { + // Now more of a convenience function for using the master key. + return decrypt(secretKeySpec, ciphertext); + } + + /** + * {@inheritDoc} + */ + public PlainText decrypt(SecretKey key, CipherText ciphertext) + throws EncryptionException, IllegalArgumentException + { + long start = System.nanoTime(); // Current time in nanosecs; used to prevent timing attacks + if ( key == null ) { + throw new IllegalArgumentException("SecretKey arg may not be null"); + } + if ( ciphertext == null ) { + throw new IllegalArgumentException("Ciphertext may arg not be null"); + } + + if ( ! CryptoHelper.isAllowedCipherMode(ciphertext.getCipherMode()) ) { + // This really should be an illegal argument exception, but it could + // mean that a partner encrypted something using a cipher mode that + // you do not accept, so it's a bit more complex than that. Also + // throwing an IllegalArgumentException doesn't allow us to provide + // the two separate error messages or automatically log it. + throw new EncryptionException(DECRYPTION_FAILED, + "Invalid cipher mode " + ciphertext.getCipherMode() + + " not permitted for decryption or encryption operations."); + } + logger.debug(Logger.EVENT_SUCCESS, + "Args valid for JavaEncryptor.decrypt(SecretKey,CipherText): " + + ciphertext); + + PlainText plaintext = null; + boolean caughtException = false; + int progressMark = 0; + try { + // First we validate the MAC. + boolean valid = CryptoHelper.isCipherTextMACvalid(key, ciphertext); + if ( !valid ) { + try { + // This is going to fail, but we want the same processing + // to occur as much as possible so as to prevent timing + // attacks. We _could_ just be satisfied by the additional + // sleep in the 'finally' clause, but an attacker on the + // same server who can run something like 'ps' can tell + // CPU time versus when the process is sleeping. Hence we + // try to make this as close as possible. Since we know + // it is going to fail, we ignore the result and ignore + // the (expected) exception. + handleDecryption(key, ciphertext); // Ignore return (should fail). + } catch(Exception ex) { + ; // Ignore + } + throw new EncryptionException(DECRYPTION_FAILED, + "Decryption failed because MAC invalid for " + + ciphertext); + } + progressMark++; + // The decryption only counts if the MAC was valid. + plaintext = handleDecryption(key, ciphertext); + progressMark++; + } catch(EncryptionException ex) { + caughtException = true; + String logMsg = null; + switch( progressMark ) { + case 1: + logMsg = "Decryption failed because MAC invalid. See logged exception for details."; + break; + case 2: + logMsg = "Decryption failed because handleDecryption() failed. See logged exception for details."; + break; + default: + logMsg = "Programming error: unexpected progress mark == " + progressMark; + break; + } + logger.error(Logger.SECURITY_FAILURE, logMsg); + throw ex; // Re-throw + } + finally { + if ( caughtException ) { + // The rest of this code is to try to account for any minute differences + // in the time it might take for the various reasons that decryption fails + // in order to prevent any other possible timing attacks. Perhaps it is + // going overboard. If nothing else, if N_SECS is large enough, it might + // deter attempted repeated attacks by making them take much longer. + long now = System.nanoTime(); + long elapsed = now - start; + final long NANOSECS_IN_SEC = 1000000000L; // nanosec is 10**-9 sec + long nSecs = N_SECS * NANOSECS_IN_SEC; // N seconds in nano seconds + if ( elapsed < nSecs ) { + // Want to sleep so total time taken is N seconds. + long extraSleep = nSecs - elapsed; + + // 'extraSleep' is in nanoseconds. Need to convert to a millisec + // part and nanosec part. Nanosec is 10**-9, millsec is + // 10**-3, so divide by (10**-9 / 10**-3), or 10**6 to + // convert to from nanoseconds to milliseconds. + long millis = extraSleep / 1000000L; + long nanos = (extraSleep - (millis * 1000000L)); // N_SECS is hard-coded so assertion should be okay here. - assert nanos >= 0 && nanos <= Integer.MAX_VALUE : + assert nanos >= 0 && nanos <= Integer.MAX_VALUE : "Nanosecs out of bounds; nanos = " + nanos; - try { - Thread.sleep(millis, (int)nanos); - } catch(InterruptedException ex) { - ; // Ignore - } - } // Else ... time already exceeds N_SECS sec, so do not sleep. - } - } - return plaintext; - } + try { + Thread.sleep(millis, (int)nanos); + } catch(InterruptedException ex) { + ; // Ignore + } + } // Else ... time already exceeds N_SECS sec, so do not sleep. + } + } + return plaintext; + } // Handle the actual decryption portion. At this point it is assumed that // any MAC has already been validated. (But see "DISCUSS" issue, below.) @@ -677,19 +677,19 @@ private PlainText handleDecryption(SecretKey key, CipherText ciphertext) // TODO: PERFORMANCE: Calculate avg time this takes and consider caching for very short interval // (e.g., 2 to 5 sec tops). Otherwise doing lots of encryptions in a loop could take a LOT longer. // But remember Jon Bentley's "Rule #1 on performance: First make it right, then make it fast." - // This would be a security trade-off as it would leave keys in memory a bit longer, so it - // should probably be off by default and controlled via a property. - // - // TODO: Feed in some additional parms here to use as the 'context' for the - // KeyDerivationFunction...especially the KDF version. We would have to - // store that in the CipherText object. We *possibly* could make it - // transient so it would not be serialized with the CipherText object, - // otherwise we would have to implement readObject() and writeObject() - // methods there to support backward compatibility. Anyhow the intent - // is to prevent down grade attacks when we finally re-design and - // re-implement the MAC. Think about this in version 2.1.1. + // This would be a security trade-off as it would leave keys in memory a bit longer, so it + // should probably be off by default and controlled via a property. + // + // TODO: Feed in some additional parms here to use as the 'context' for the + // KeyDerivationFunction...especially the KDF version. We would have to + // store that in the CipherText object. We *possibly* could make it + // transient so it would not be serialized with the CipherText object, + // otherwise we would have to implement readObject() and writeObject() + // methods there to support backward compatibility. Anyhow the intent + // is to prevent down grade attacks when we finally re-design and + // re-implement the MAC. Think about this in version 2.1.1. encKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(), - key, keySize, "encryption"); + key, keySize, "encryption"); } if ( ciphertext.requiresIV() ) { decrypter.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(ciphertext.getIV())); @@ -713,16 +713,16 @@ private PlainText handleDecryption(SecretKey key, CipherText ciphertext) } catch (IllegalBlockSizeException e) { throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e); } catch (BadPaddingException e) { - //DISCUSS: This needs fixed. Already validated MAC in CryptoHelper.isCipherTextMACvalid() above. - //So only way we could get a padding exception is if invalid padding were used originally by - //the party doing the encryption. (This might happen with a buggy padding scheme for instance.) - //It *seems* harmless though, so will leave it for now, and technically, we need to either catch it - //or declare it in a throws class. Clearly we don't want to do the later. This should be discussed - //during a code inspection. + // DISCUSS: This needs fixed. Already validated MAC in CryptoHelper.isCipherTextMACvalid() above. + // So only way we could get a padding exception is if invalid padding were used originally by + // the party doing the encryption. (This might happen with a buggy padding scheme for instance.) + // It *seems* harmless though, so will leave it for now, and technically, we need to either catch it + // or declare it in a throws class. Clearly we don't want to do the later. This should be discussed + // during a code inspection. SecretKey authKey; try { authKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(), - key, keySize, "authenticity"); + key, keySize, "authenticity"); } catch (Exception e1) { throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem -- failed to compute derived key for authenticity: " + e1.getMessage(), e1); @@ -737,187 +737,187 @@ private PlainText handleDecryption(SecretKey key, CipherText ciphertext) } } } - - /** - * {@inheritDoc} - */ - public String sign(String data) throws EncryptionException { - try { - Signature signer = Signature.getInstance(signatureAlgorithm); - signer.initSign(privateKey); - signer.update(data.getBytes(encoding)); - byte[] bytes = signer.sign(); - return ESAPI.encoder().encodeForBase64(bytes, false); - } catch (InvalidKeyException ike) { - throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); - } catch (Exception e) { - throw new EncryptionException("Signature failure", "Can't find signature algorithm " + signatureAlgorithm, e); - } - } - - /** - * {@inheritDoc} - */ - public boolean verifySignature(String signature, String data) { - try { - byte[] bytes = ESAPI.encoder().decodeFromBase64(signature); - Signature signer = Signature.getInstance(signatureAlgorithm); - signer.initVerify(publicKey); - signer.update(data.getBytes(encoding)); - return signer.verify(bytes); - } catch (Exception e) { - // NOTE: EncryptionException constructed *only* for side-effect of causing logging. - // FindBugs complains about this and since it examines byte-code, there's no way to - // shut it up. - new EncryptionException("Invalid signature", "Problem verifying signature: " + e.getMessage(), e); - return false; - } - } - - /** - * {@inheritDoc} + + /** + * {@inheritDoc} + */ + public String sign(String data) throws EncryptionException { + try { + Signature signer = Signature.getInstance(signatureAlgorithm); + signer.initSign(privateKey); + signer.update(data.getBytes(encoding)); + byte[] bytes = signer.sign(); + return ESAPI.encoder().encodeForBase64(bytes, false); + } catch (InvalidKeyException ike) { + throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); + } catch (Exception e) { + throw new EncryptionException("Signature failure", "Can't find signature algorithm " + signatureAlgorithm, e); + } + } + + /** + * {@inheritDoc} + */ + public boolean verifySignature(String signature, String data) { + try { + byte[] bytes = ESAPI.encoder().decodeFromBase64(signature); + Signature signer = Signature.getInstance(signatureAlgorithm); + signer.initVerify(publicKey); + signer.update(data.getBytes(encoding)); + return signer.verify(bytes); + } catch (Exception e) { + // NOTE: EncryptionException constructed *only* for side-effect of causing logging. + // FindBugs complains about this and since it examines byte-code, there's no way to + // shut it up. + new EncryptionException("Invalid signature", "Problem verifying signature: " + e.getMessage(), e); + return false; + } + } + + /** + * {@inheritDoc} * * @param expiration * @throws IntegrityException */ - public String seal(String data, long expiration) throws IntegrityException { - if ( data == null ) { - throw new IllegalArgumentException("Data to be sealed may not be null."); - } - - try { - String b64data = null; + public String seal(String data, long expiration) throws IntegrityException { + if ( data == null ) { + throw new IllegalArgumentException("Data to be sealed may not be null."); + } + + try { + String b64data = null; try { b64data = ESAPI.encoder().encodeForBase64(data.getBytes("UTF-8"), false); } catch (UnsupportedEncodingException e) { ; // Ignore; should never happen since UTF-8 built into rt.jar } - // mix in some random data so even identical data and timestamp produces different seals - String nonce = ESAPI.randomizer().getRandomString(10, EncoderConstants.CHAR_ALPHANUMERICS); - String plaintext = expiration + ":" + nonce + ":" + b64data; - // add integrity check; signature is already base64 encoded. - String sig = this.sign( plaintext ); - CipherText ciphertext = this.encrypt( new PlainText(plaintext + ":" + sig) ); - String sealedData = ESAPI.encoder().encodeForBase64(ciphertext.asPortableSerializedByteArray(), false); - return sealedData; - } catch( EncryptionException e ) { - throw new IntegrityException( e.getUserMessage(), e.getLogMessage(), e ); - } - } - - /** - * {@inheritDoc} - */ - public String unseal(String seal) throws EncryptionException { - PlainText plaintext = null; - try { - byte[] encryptedBytes = ESAPI.encoder().decodeFromBase64(seal); - CipherText cipherText = null; - try { - cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes); - } catch( AssertionError e) { - // Some of the tests in EncryptorTest.testVerifySeal() are examples of - // this if assertions are enabled, but otherwise it should not + // mix in some random data so even identical data and timestamp produces different seals + String nonce = ESAPI.randomizer().getRandomString(10, EncoderConstants.CHAR_ALPHANUMERICS); + String plaintext = expiration + ":" + nonce + ":" + b64data; + // add integrity check; signature is already base64 encoded. + String sig = this.sign( plaintext ); + CipherText ciphertext = this.encrypt( new PlainText(plaintext + ":" + sig) ); + String sealedData = ESAPI.encoder().encodeForBase64(ciphertext.asPortableSerializedByteArray(), false); + return sealedData; + } catch( EncryptionException e ) { + throw new IntegrityException( e.getUserMessage(), e.getLogMessage(), e ); + } + } + + /** + * {@inheritDoc} + */ + public String unseal(String seal) throws EncryptionException { + PlainText plaintext = null; + try { + byte[] encryptedBytes = ESAPI.encoder().decodeFromBase64(seal); + CipherText cipherText = null; + try { + cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes); + } catch( AssertionError e) { + // Some of the tests in EncryptorTest.testVerifySeal() are examples of + // this if assertions are enabled, but otherwise it should not // normally happen. - throw new EncryptionException("Invalid seal", - "Seal passed garbarge data resulting in AssertionError: " + e); - } - plaintext = this.decrypt(cipherText); - - String[] parts = plaintext.toString().split(":"); - if (parts.length != 4) { - throw new EncryptionException("Invalid seal", "Seal was not formatted properly."); - } - - String timestring = parts[0]; - long now = new Date().getTime(); - long expiration = Long.parseLong(timestring); - if (now > expiration) { - throw new EncryptionException("Invalid seal", "Seal expiration date of " + new Date(expiration) + " has past."); - } - String nonce = parts[1]; - String b64data = parts[2]; - String sig = parts[3]; - if (!this.verifySignature(sig, timestring + ":" + nonce + ":" + b64data ) ) { - throw new EncryptionException("Invalid seal", "Seal integrity check failed"); - } - return new String(ESAPI.encoder().decodeFromBase64(b64data), "UTF-8"); - } catch (EncryptionException e) { - throw e; - } catch (Exception e) { - throw new EncryptionException("Invalid seal", "Invalid seal:" + e.getMessage(), e); - } - } - - - /** - * {@inheritDoc} - */ - public boolean verifySeal( String seal ) { - try { - unseal( seal ); - return true; - } catch( EncryptionException e ) { - return false; - } - } - - /** - * {@inheritDoc} - */ - public long getTimeStamp() { - return new Date().getTime(); - } - - /** - * {@inheritDoc} - */ - public long getRelativeTimeStamp( long offset ) { - return new Date().getTime() + offset; - } - - // DISCUSS: Why experimental? Would have to be added to Encryptor interface - // but only 3 things I saw wrong with this was 1) it used HMacMD5 instead - // of HMacSHA1 (see discussion below), 2) that the HMac key is the - // same one used for encryption (also see comments), and 3) it caught - // overly broad exceptions. Here it is with these specific areas - // addressed, but no unit testing has been done at this point. -kww + throw new EncryptionException("Invalid seal", + "Seal passed garbarge data resulting in AssertionError: " + e); + } + plaintext = this.decrypt(cipherText); + + String[] parts = plaintext.toString().split(":"); + if (parts.length != 4) { + throw new EncryptionException("Invalid seal", "Seal was not formatted properly."); + } + + String timestring = parts[0]; + long now = new Date().getTime(); + long expiration = Long.parseLong(timestring); + if (now > expiration) { + throw new EncryptionException("Invalid seal", "Seal expiration date of " + new Date(expiration) + " has past."); + } + String nonce = parts[1]; + String b64data = parts[2]; + String sig = parts[3]; + if (!this.verifySignature(sig, timestring + ":" + nonce + ":" + b64data ) ) { + throw new EncryptionException("Invalid seal", "Seal integrity check failed"); + } + return new String(ESAPI.encoder().decodeFromBase64(b64data), "UTF-8"); + } catch (EncryptionException e) { + throw e; + } catch (Exception e) { + throw new EncryptionException("Invalid seal", "Invalid seal:" + e.getMessage(), e); + } + } + + + /** + * {@inheritDoc} + */ + public boolean verifySeal( String seal ) { + try { + unseal( seal ); + return true; + } catch( EncryptionException e ) { + return false; + } + } + + /** + * {@inheritDoc} + */ + public long getTimeStamp() { + return new Date().getTime(); + } + + /** + * {@inheritDoc} + */ + public long getRelativeTimeStamp( long offset ) { + return new Date().getTime() + offset; + } + + // DISCUSS: Why experimental? Would have to be added to Encryptor interface + // but only 3 things I saw wrong with this was 1) it used HMacMD5 instead + // of HMacSHA1 (see discussion below), 2) that the HMac key is the + // same one used for encryption (also see comments), and 3) it caught + // overly broad exceptions. Here it is with these specific areas + // addressed, but no unit testing has been done at this point. -kww /** * Compute an HMAC for a String. Experimental. - * @param input The input for which to compute the HMac. + * @param input The input for which to compute the HMac. */ /******************** - public String computeHMAC( String input ) throws EncryptionException { - try { - Mac hmac = Mac.getInstance("HMacSHA1"); // DISCUSS: Changed to HMacSHA1. MD5 *badly* broken - // SHA1 should really be avoided, but using - // for HMAC-SHA1 is acceptable for now. Plan - // to migrate to SHA-256 or NIST replacement for - // SHA1 in not too distant future. - // DISCUSS: Also not recommended that the HMac key is the same as the one - // used for encryption (namely, Encryptor.MasterKey). If anything it - // would be better to use Encryptor.MasterSalt for the HMac key, or - // perhaps a derived key based on the master salt. (One could use - // KeyDerivationFunction.computeDerivedKey().) - // - byte[] salt = ESAPI.securityConfiguration().getMasterSalt(); - hmac.init( new SecretKeySpec(salt, "HMacSHA1") ); // Was: hmac.init(secretKeySpec) - byte[] inBytes; - try { - inBytes = input.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - logger.warning(Logger.SECURITY_FAILURE, "computeHMAC(): Can't find UTF-8 encoding; using default encoding", e); - inBytes = input.getBytes(); - } - byte[] bytes = hmac.doFinal( inBytes ); - return ESAPI.encoder().encodeForBase64(bytes, false); - } catch (InvalidKeyException ike) { - throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); - } catch (NoSuchAlgorithmException e) { - throw new EncryptionException("Could not compute HMAC", "Can't find HMacSHA1 algorithm. " + - "Problem computing HMAC for " + input, e ); - } - } + public String computeHMAC( String input ) throws EncryptionException { + try { + Mac hmac = Mac.getInstance("HMacSHA1"); // DISCUSS: Changed to HMacSHA1. MD5 *badly* broken + // SHA1 should really be avoided, but using + // for HMAC-SHA1 is acceptable for now. Plan + // to migrate to SHA-256 or NIST replacement for + // SHA1 in not too distant future. + // DISCUSS: Also not recommended that the HMac key is the same as the one + // used for encryption (namely, Encryptor.MasterKey). If anything it + // would be better to use Encryptor.MasterSalt for the HMac key, or + // perhaps a derived key based on the master salt. (One could use + // KeyDerivationFunction.computeDerivedKey().) + // + byte[] salt = ESAPI.securityConfiguration().getMasterSalt(); + hmac.init( new SecretKeySpec(salt, "HMacSHA1") ); // Was: hmac.init(secretKeySpec) + byte[] inBytes; + try { + inBytes = input.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.warning(Logger.SECURITY_FAILURE, "computeHMAC(): Can't find UTF-8 encoding; using default encoding", e); + inBytes = input.getBytes(); + } + byte[] bytes = hmac.doFinal( inBytes ); + return ESAPI.encoder().encodeForBase64(bytes, false); + } catch (InvalidKeyException ike) { + throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException("Could not compute HMAC", "Can't find HMacSHA1 algorithm. " + + "Problem computing HMAC for " + input, e ); + } + } ********************/ /** @@ -926,7 +926,7 @@ public String computeHMAC( String input ) throws EncryptionException { * may be changed via the system property * {@code ESAPI.Encryptor.warnEveryNthUse}.) In other words, we nag * them until the give in and change it. ;-) - * + * * @param where The string "encrypt" or "decrypt", corresponding to the * method that is being logged. * @param msg The message to log. @@ -949,54 +949,54 @@ private void logWarning(String where, String msg) { logger.warning(Logger.SECURITY_FAILURE, where + msg); } } - - private KeyDerivationFunction.PRF_ALGORITHMS getPRF(String name) { - String prfName = null; - if ( name == null ) { - prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); - } else { - prfName = name; - } - KeyDerivationFunction.PRF_ALGORITHMS prf = KeyDerivationFunction.convertNameToPRF(prfName); - return prf; + + private KeyDerivationFunction.PRF_ALGORITHMS getPRF(String name) { + String prfName = null; + if ( name == null ) { + prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); + } else { + prfName = name; + } + KeyDerivationFunction.PRF_ALGORITHMS prf = KeyDerivationFunction.convertNameToPRF(prfName); + return prf; } - + private KeyDerivationFunction.PRF_ALGORITHMS getDefaultPRF() { - String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); - return getPRF(prfName); + String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); + return getPRF(prfName); } - + // Private interface to call ESAPI's KDF to get key for encryption or authenticity. private SecretKey computeDerivedKey(int kdfVersion, KeyDerivationFunction.PRF_ALGORITHMS prf, - SecretKey kdk, int keySize, String purpose) - throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException + SecretKey kdk, int keySize, String purpose) + throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException { - // These really should be turned into actual runtime checks and an - // IllegalArgumentException should be thrown if they are violated. - // But this should be OK since this is a private method. Also, this method will - // be called quite often so assertions are a big win as they can be disabled or - // enabled at will. - assert prf != null : "Pseudo Random Function for KDF cannot be null"; - assert kdk != null : "Key derivation key cannot be null."; - // We would choose a larger minimum key size, but we want to be - // able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that, - // we print warning. - assert keySize >= 56 : "Key has size of " + keySize + ", which is less than absolute minimum of 56-bits."; - assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits."; + // These really should be turned into actual runtime checks and an + // IllegalArgumentException should be thrown if they are violated. + // But this should be OK since this is a private method. Also, this method will + // be called quite often so assertions are a big win as they can be disabled or + // enabled at will. + assert prf != null : "Pseudo Random Function for KDF cannot be null"; + assert kdk != null : "Key derivation key cannot be null."; + // We would choose a larger minimum key size, but we want to be + // able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that, + // we print warning. + assert keySize >= 56 : "Key has size of " + keySize + ", which is less than absolute minimum of 56-bits."; + assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits."; // However, this one we want as a runtime check because we don't have this check // in KeyDerivationFunction.computeDerivedKey() as we want that method // to be more general. - if ( !( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { + if ( !( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { String exMsg = "Programming error in ESAPI?? 'purpose' for computeDerivedKey() must be \"encryption\" or \"authenticity\"."; - throw new EncryptionException(exMsg, exMsg); + throw new EncryptionException(exMsg, exMsg); } - KeyDerivationFunction kdf = new KeyDerivationFunction(prf); - if ( kdfVersion != 0 ) { - kdf.setVersion(kdfVersion); - } - return kdf.computeDerivedKey(kdk, keySize, purpose); + KeyDerivationFunction kdf = new KeyDerivationFunction(prf); + if ( kdfVersion != 0 ) { + kdf.setVersion(kdfVersion); + } + return kdf.computeDerivedKey(kdk, keySize, purpose); } // Get all the algorithms we will be using from ESAPI.properties. @@ -1011,7 +1011,7 @@ private static void setupAlgorithms() { encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength(); signatureKeyLength = ESAPI.securityConfiguration().getDigitalSignatureKeyLength(); } - + // Set up signing key pair using the master password and salt. Called (once) // from the JavaEncryptor CTOR. private static void initKeyPair(SecureRandom prng) throws NoSuchAlgorithmException { From 197e2dbc1093d625ff3dd9930fb9073f59fd153d Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 8 Jul 2019 06:15:16 -0400 Subject: [PATCH 010/544] Close issue #256. White-space clean up. (#505) --- .../esapi/reference/crypto/JavaEncryptor.java | 1240 ++++++++--------- 1 file changed, 620 insertions(+), 620 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java index bb3dd163a..64d3e7561 100644 --- a/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java +++ b/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java @@ -6,10 +6,10 @@ * http://www.owasp.org/index.php/ESAPI. * * Copyright (c) 2007 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Jeff Williams Aspect Security * @author kevin.w.wall@gmail.com * @created 2007 @@ -41,7 +41,7 @@ import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; -// import javax.crypto.Mac; // Uncomment if computeHMAC() is included. +// import javax.crypto.Mac; // Uncomment if computeHMAC() is included. import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -70,7 +70,7 @@ * controlling the selection of this class is {@code ESAPI.Encryptor}. Most of * the other encryption related properties have property names that start with * the string "Encryptor.". - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @author kevin.w.wall@gmail.com @@ -96,126 +96,126 @@ public static Encryptor getInstance() throws EncryptionException { } private static boolean initialized = false; - + // encryption private static SecretKeySpec secretKeySpec = null; // DISCUSS: Why static? Implies one key?!? private static String encryptAlgorithm = "AES"; - private static String encoding = "UTF-8"; + private static String encoding = "UTF-8"; private static int encryptionKeyLength = 128; - + // digital signatures private static PrivateKey privateKey = null; - private static PublicKey publicKey = null; - private static String signatureAlgorithm = "SHA1withDSA"; + private static PublicKey publicKey = null; + private static String signatureAlgorithm = "SHA1withDSA"; private static String randomAlgorithm = "SHA1PRNG"; - private static int signatureKeyLength = 1024; - - // hashing - private static String hashAlgorithm = "SHA-512"; - private static int hashIterations = 1024; - - // Logging - DISCUSS: This "sticks" us with a specific logger to whatever it was when - // this class is first loaded. Is this a big limitation? Since there - // is no method to reset it, we may has well make it 'final' also. - private static Logger logger = ESAPI.getLogger("JavaEncryptor"); - // Used to print out warnings about deprecated methods. - private static int encryptCounter = 0; - private static int decryptCounter = 0; + private static int signatureKeyLength = 1024; + + // hashing + private static String hashAlgorithm = "SHA-512"; + private static int hashIterations = 1024; + + // Logging - DISCUSS: This "sticks" us with a specific logger to whatever it was when + // this class is first loaded. Is this a big limitation? Since there + // is no method to reset it, we may has well make it 'final' also. + private static Logger logger = ESAPI.getLogger("JavaEncryptor"); + // Used to print out warnings about deprecated methods. + private static int encryptCounter = 0; + private static int decryptCounter = 0; // DISCUSS: OK to not have a property for this to set the frequency? // The desire is to persuade people to move away from these - // two deprecated encrypt(String) / decrypt(String) methods, + // two deprecated encrypt(String) / decrypt(String) methods, // so perhaps the annoyance factor of not being able to // change it will help. For now, it is just hard-coded here. // We could be mean and just print a warning *every* time. - private static final int logEveryNthUse = 25; - + private static final int logEveryNthUse = 25; + // *Only* use this string for user messages for EncryptionException when // decryption fails. This is to prevent information leakage that may be // valuable in various forms of ciphertext attacks, such as the - // Padded Oracle attack described by Rizzo and Duong. + // Padded Oracle attack described by Rizzo and Duong. private static final String DECRYPTION_FAILED = "Decryption failed; see logs for details."; // # of seconds that all failed decryption attempts will take. Used to // help prevent side-channel timing attacks. private static int N_SECS = 2; - // Load the preferred JCE provider if one has been specified. - static { - try { + // Load the preferred JCE provider if one has been specified. + static { + try { SecurityProviderLoader.loadESAPIPreferredJCEProvider(); } catch (NoSuchProviderException ex) { - // Note that audit logging is done elsewhere in called method. + // Note that audit logging is done elsewhere in called method. logger.fatal(Logger.SECURITY_FAILURE, "JavaEncryptor failed to load preferred JCE provider.", ex); throw new ExceptionInInitializerError(ex); } setupAlgorithms(); - } - + } + /** * Generates a new strongly random secret key and salt that can be * copy and pasted in the ESAPI.properties file. - * + * * @param args Set first argument to "-print" to display available algorithms on standard output. - * @throws java.lang.Exception To cover a multitude of sins, mostly in configuring ESAPI.properties. + * @throws java.lang.Exception To cover a multitude of sins, mostly in configuring ESAPI.properties. */ public static void main( String[] args ) throws Exception { - System.out.println( "Generating a new secret master key" ); - - // print out available ciphers - if ( args.length == 1 && args[0].equalsIgnoreCase("-print" ) ) { - System.out.println( "AVAILABLE ALGORITHMS" ); - - Provider[] providers = Security.getProviders(); - TreeMap tm = new TreeMap(); - // DISCUSS: Note: We go through multiple providers, yet nowhere do I - // see where we print out the PROVIDER NAME. Not all providers - // will implement the same algorithms and some "partner" with - // whom we are exchanging different cryptographic messages may - // have _different_ providers in their java.security file. So - // it would be useful to know the provider name where each - // algorithm is implemented. Might be good to prepend the provider - // name to the 'key' with something like "providerName: ". Thoughts? - for (int i = 0; i != providers.length; i++) { - // DISCUSS: Print security provider name here??? - // Note: For some odd reason, Provider.keySet() returns - // Set of the property keys (which are Strings) - // contained in this provider, but Set seems - // more appropriate. But that's why we need the cast below. - System.out.println("===== Provider " + i + ":" + providers[i].getName() + " ======"); - Iterator it = providers[i].keySet().iterator(); - while (it.hasNext()) { - String key = (String)it.next(); - String value = providers[i].getProperty( key ); - tm.put(key, value); - System.out.println("\t\t " + key + " -> "+ value ); - } - } - - Set< Entry > keyValueSet = tm.entrySet(); - Iterator> it = keyValueSet.iterator(); - while( it.hasNext() ) { - Map.Entry entry = it.next(); - String key = entry.getKey(); - String value = entry.getValue(); - System.out.println( " " + key + " -> "+ value ); - } - } else { - // Used to print a similar line to use '-print' even when it was specified. - System.out.println( "\tuse '-print' to also show available crypto algorithms from all the security providers" ); - } - + System.out.println( "Generating a new secret master key" ); + + // print out available ciphers + if ( args.length == 1 && args[0].equalsIgnoreCase("-print" ) ) { + System.out.println( "AVAILABLE ALGORITHMS" ); + + Provider[] providers = Security.getProviders(); + TreeMap tm = new TreeMap(); + // DISCUSS: Note: We go through multiple providers, yet nowhere do I + // see where we print out the PROVIDER NAME. Not all providers + // will implement the same algorithms and some "partner" with + // whom we are exchanging different cryptographic messages may + // have _different_ providers in their java.security file. So + // it would be useful to know the provider name where each + // algorithm is implemented. Might be good to prepend the provider + // name to the 'key' with something like "providerName: ". Thoughts? + for (int i = 0; i != providers.length; i++) { + // DISCUSS: Print security provider name here??? + // Note: For some odd reason, Provider.keySet() returns + // Set of the property keys (which are Strings) + // contained in this provider, but Set seems + // more appropriate. But that's why we need the cast below. + System.out.println("===== Provider " + i + ":" + providers[i].getName() + " ======"); + Iterator it = providers[i].keySet().iterator(); + while (it.hasNext()) { + String key = (String)it.next(); + String value = providers[i].getProperty( key ); + tm.put(key, value); + System.out.println("\t\t " + key + " -> "+ value ); + } + } + + Set< Entry > keyValueSet = tm.entrySet(); + Iterator> it = keyValueSet.iterator(); + while( it.hasNext() ) { + Map.Entry entry = it.next(); + String key = entry.getKey(); + String value = entry.getValue(); + System.out.println( " " + key + " -> "+ value ); + } + } else { + // Used to print a similar line to use '-print' even when it was specified. + System.out.println( "\tuse '-print' to also show available crypto algorithms from all the security providers" ); + } + // setup algorithms -- Each of these have defaults if not set, although - // someone could set them to something invalid. If - // so a suitable exception will be thrown and displayed. + // someone could set them to something invalid. If + // so a suitable exception will be thrown and displayed. encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm(); - encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength(); - randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm(); + encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength(); + randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm(); - SecureRandom random = SecureRandom.getInstance(randomAlgorithm); - SecretKey secretKey = CryptoHelper.generateSecretKey(encryptAlgorithm, encryptionKeyLength); + SecureRandom random = SecureRandom.getInstance(randomAlgorithm); + SecretKey secretKey = CryptoHelper.generateSecretKey(encryptAlgorithm, encryptionKeyLength); byte[] raw = secretKey.getEncoded(); - byte[] salt = new byte[20]; // Or 160-bits; big enough for SHA1, but not SHA-256 or SHA-512. + byte[] salt = new byte[20]; // Or 160-bits; big enough for SHA1, but not SHA-256 or SHA-512. random.nextBytes( salt ); String eol = System.getProperty("line.separator", "\n"); // So it works on Windows too. System.out.println( eol + "Copy and paste these lines into your ESAPI.properties" + eol); @@ -224,12 +224,12 @@ public static void main( String[] args ) throws Exception { System.out.println( "Encryptor.MasterSalt=" + ESAPI.encoder().encodeForBase64(salt, false) ); System.out.println( "#==============================================================" + eol); } - - + + /** * Private CTOR for {@code JavaEncryptor}, called by {@code getInstance()}. * @throws EncryptionException if can't construct this object for some reason. - * Original exception will be attached as the 'cause'. + * Original exception will be attached as the 'cause'. */ private JavaEncryptor() throws EncryptionException { byte[] salt = ESAPI.securityConfiguration().getMasterSalt(); @@ -252,7 +252,7 @@ private JavaEncryptor() throws EncryptionException { throw new ConfigurationException("Encryptor.MasterKey must be at least 7 bytes. " + "Length is: " + skey.length + " bytes."); } - + // Set up secretKeySpec for use for symmetric encryption and decryption, // and set up the public/private keys for asymmetric encryption / // decryption. @@ -275,7 +275,7 @@ private JavaEncryptor() throws EncryptionException { // handle in a static initializer, it just seems to // fit better here. secretKeySpec = new SecretKeySpec(skey, encryptAlgorithm ); - + // // For asymmetric encryption (i.e., public/private key) // @@ -291,93 +291,93 @@ private JavaEncryptor() throws EncryptionException { initKeyPair(prng); } catch (Exception e) { throw new EncryptionException("Encryption failure", "Error creating Encryptor", e); - } - + } + // Mark everything as initialized. initialized = true; } } } - - /** + + /** * {@inheritDoc} - * - * Hashes the data with the supplied salt and the number of iterations specified in - * the ESAPI SecurityConfiguration. - */ - public String hash(String plaintext, String salt) throws EncryptionException { - return hash( plaintext, salt, hashIterations ); - } - - /** + * + * Hashes the data with the supplied salt and the number of iterations specified in + * the ESAPI SecurityConfiguration. + */ + public String hash(String plaintext, String salt) throws EncryptionException { + return hash( plaintext, salt, hashIterations ); + } + + /** * {@inheritDoc} - * - * Hashes the data using the specified algorithm and the Java MessageDigest class. This method - * first adds the salt, a separator (":"), and the data, and then rehashes the specified number of iterations - * in order to help strengthen weak passwords. - */ - public String hash(String plaintext, String salt, int iterations) throws EncryptionException { - byte[] bytes = null; - try { - MessageDigest digest = MessageDigest.getInstance(hashAlgorithm); - digest.reset(); - digest.update(ESAPI.securityConfiguration().getMasterSalt()); - digest.update(salt.getBytes(encoding)); - digest.update(plaintext.getBytes(encoding)); - - // rehash a number of times to help strengthen weak passwords - bytes = digest.digest(); - for (int i = 0; i < iterations; i++) { - digest.reset(); - bytes = digest.digest(bytes); - } - String encoded = ESAPI.encoder().encodeForBase64(bytes,false); - return encoded; - } catch (NoSuchAlgorithmException e) { - throw new EncryptionException("Internal error", "Can't find hash algorithm " + hashAlgorithm, e); - } catch (UnsupportedEncodingException ex) { - throw new EncryptionException("Internal error", "Can't find encoding for " + encoding, ex); - } - } - - /** - * {@inheritDoc} - */ - public CipherText encrypt(PlainText plaintext) throws EncryptionException { - // Now more of a convenience function for using the master key. - return encrypt(secretKeySpec, plaintext); - } - - /** - * {@inheritDoc} - */ - public CipherText encrypt(SecretKey key, PlainText plain) - throws EncryptionException - { - if ( key == null ) { - throw new IllegalArgumentException("(Master) encryption key arg may not be null. Is Encryptor.MasterKey set?"); - } - if ( plain == null ) { - throw new IllegalArgumentException("PlainText may arg not be null"); - } - byte[] plaintext = plain.asBytes(); - boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText(); - - boolean success = false; // Used in 'finally' clause. - String xform = null; - int keySize = key.getEncoded().length * 8; // Convert to # bits - - try { - xform = ESAPI.securityConfiguration().getCipherTransformation(); + * + * Hashes the data using the specified algorithm and the Java MessageDigest class. This method + * first adds the salt, a separator (":"), and the data, and then rehashes the specified number of iterations + * in order to help strengthen weak passwords. + */ + public String hash(String plaintext, String salt, int iterations) throws EncryptionException { + byte[] bytes = null; + try { + MessageDigest digest = MessageDigest.getInstance(hashAlgorithm); + digest.reset(); + digest.update(ESAPI.securityConfiguration().getMasterSalt()); + digest.update(salt.getBytes(encoding)); + digest.update(plaintext.getBytes(encoding)); + + // rehash a number of times to help strengthen weak passwords + bytes = digest.digest(); + for (int i = 0; i < iterations; i++) { + digest.reset(); + bytes = digest.digest(bytes); + } + String encoded = ESAPI.encoder().encodeForBase64(bytes,false); + return encoded; + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException("Internal error", "Can't find hash algorithm " + hashAlgorithm, e); + } catch (UnsupportedEncodingException ex) { + throw new EncryptionException("Internal error", "Can't find encoding for " + encoding, ex); + } + } + + /** + * {@inheritDoc} + */ + public CipherText encrypt(PlainText plaintext) throws EncryptionException { + // Now more of a convenience function for using the master key. + return encrypt(secretKeySpec, plaintext); + } + + /** + * {@inheritDoc} + */ + public CipherText encrypt(SecretKey key, PlainText plain) + throws EncryptionException + { + if ( key == null ) { + throw new IllegalArgumentException("(Master) encryption key arg may not be null. Is Encryptor.MasterKey set?"); + } + if ( plain == null ) { + throw new IllegalArgumentException("PlainText may arg not be null"); + } + byte[] plaintext = plain.asBytes(); + boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText(); + + boolean success = false; // Used in 'finally' clause. + String xform = null; + int keySize = key.getEncoded().length * 8; // Convert to # bits + + try { + xform = ESAPI.securityConfiguration().getCipherTransformation(); String[] parts = xform.split("/"); if ( parts.length != 3 ) { throw new ConfigurationException("Malformed cipher transformation: " + xform + ". Should have format of cipher_alg/cipher_mode/padding_scheme."); } String cipherMode = parts[1]; - + // This way we can prevent modes like OFB and CFB where the IV should never // be repeated with the same encryption key (at least until we support // Encryptor.ChooseIVMethod=specified and allow us to specify some mechanism @@ -389,19 +389,19 @@ public CipherText encrypt(SecretKey key, PlainText plain) "Encryption failure: Cipher transformation " + xform + " specifies invalid " + "cipher mode " + cipherMode); } - - // Note - Cipher is not thread-safe so we create one locally - // Also, we need to change this eventually so other algorithms can - // be supported. Eventually, there will be an encrypt() method that - // takes a (new class) CryptoControls, as something like this: - // public CipherText encrypt(CryptoControls ctrl, SecretKey skey, PlainText plaintext) - // and this method will just call that one. - Cipher encrypter = Cipher.getInstance(xform); - String cipherAlg = encrypter.getAlgorithm(); + + // Note - Cipher is not thread-safe so we create one locally + // Also, we need to change this eventually so other algorithms can + // be supported. Eventually, there will be an encrypt() method that + // takes a (new class) CryptoControls, as something like this: + // public CipherText encrypt(CryptoControls ctrl, SecretKey skey, PlainText plaintext) + // and this method will just call that one. + Cipher encrypter = Cipher.getInstance(xform); + String cipherAlg = encrypter.getAlgorithm(); int minKeyLen = 112; // Use for hard-coded default to support 2TDEA try { - minKeyLen = ESAPI.securityConfiguration().getIntProp("Encryptor.MinEncryptionKeyLength"); + minKeyLen = ESAPI.securityConfiguration().getIntProp("Encryptor.MinEncryptionKeyLength"); } catch( Exception ex ) { logger.warning(Logger.EVENT_FAILURE, "Property 'Encryptor.MinEncryptionKeyLength' not properly set in ESAPI.properties file; using hard coded default of 112 for min key size for encryption.", @@ -409,251 +409,251 @@ public CipherText encrypt(SecretKey key, PlainText plain) ; // Do NOT rethrow. } - if ( keySize < minKeyLen ) { + if ( keySize < minKeyLen ) { // NOTE: This used to just log a warning. It now logs an error & throws an exception. // - // ESAPI.EncryptionKeyLength defaults to 128. This means that someone wants to use ESAPI to + // ESAPI.EncryptionKeyLength defaults to 128. This means that someone wants to use ESAPI to // encrypt with something like 2-key TDES, they would have to set this to that property // to 112 bits. - logger.error(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN MINIMUM allowed " + - "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits with cipher algorithm " + cipherAlg); - throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " + - "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits."); - } - if ( keySize < 112 ) { // NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030. - // Note that 112 bits 'just happens' to be size of 2-key Triple DES! So for example, if they + logger.error(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN MINIMUM allowed " + + "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits with cipher algorithm " + cipherAlg); + throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " + + "encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits."); + } + if ( keySize < 112 ) { // NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030. + // Note that 112 bits 'just happens' to be size of 2-key Triple DES! So for example, if they // have configured ESAPI's Encryptor.EncryptionKeyLength to (say) 56 bits, we are going to // nag them like their mother! :) - logger.warning(Logger.SECURITY_FAILURE, "Potentially insecure encryption. Key size of " + keySize + "bits " + - "not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " + - "of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57."); - } - // Check if algorithm mentioned in SecretKey is same as that being used for Cipher object. - // They should be the same. If they are different, things could fail. (E.g., DES and DESede - // require keys with even parity. Even if key was sufficient size, if it didn't have the correct - // parity it could fail.) - // - String skeyAlg = key.getAlgorithm(); - if ( !( cipherAlg.startsWith( skeyAlg + "/" ) || cipherAlg.equals( skeyAlg ) ) ) { - // DISCUSS: Should we thrown a ConfigurationException here or just log a warning??? I'm game for - // either, but personally I'd prefer the squeaky wheel to the annoying throwing of - // a ConfigurationException (which is a RuntimeException). Less likely to upset - // the development community. - logger.warning(Logger.SECURITY_FAILURE, "Encryption mismatch between cipher algorithm (" + - cipherAlg + ") and SecretKey algorithm (" + skeyAlg + "). Cipher will use algorithm " + cipherAlg); - } - - byte[] ivBytes = null; - CipherSpec cipherSpec = new CipherSpec(encrypter, keySize); // Could pass the ACTUAL (intended) key size - + logger.warning(Logger.SECURITY_FAILURE, "Potentially insecure encryption. Key size of " + keySize + "bits " + + "not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " + + "of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57."); + } + // Check if algorithm mentioned in SecretKey is same as that being used for Cipher object. + // They should be the same. If they are different, things could fail. (E.g., DES and DESede + // require keys with even parity. Even if key was sufficient size, if it didn't have the correct + // parity it could fail.) + // + String skeyAlg = key.getAlgorithm(); + if ( !( cipherAlg.startsWith( skeyAlg + "/" ) || cipherAlg.equals( skeyAlg ) ) ) { + // DISCUSS: Should we thrown a ConfigurationException here or just log a warning??? I'm game for + // either, but personally I'd prefer the squeaky wheel to the annoying throwing of + // a ConfigurationException (which is a RuntimeException). Less likely to upset + // the development community. + logger.warning(Logger.SECURITY_FAILURE, "Encryption mismatch between cipher algorithm (" + + cipherAlg + ") and SecretKey algorithm (" + skeyAlg + "). Cipher will use algorithm " + cipherAlg); + } + + byte[] ivBytes = null; + CipherSpec cipherSpec = new CipherSpec(encrypter, keySize); // Could pass the ACTUAL (intended) key size + // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then // use the specified SecretKey as-is rather than computing a derived key from it. We also // don't expect a separate MAC in the specified CipherText object so therefore don't try // to validate it. boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( cipherMode ); SecretKey encKey = null; - if ( preferredCipherMode ) { - encKey = key; - } else { - encKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), - key, keySize, "encryption"); - } - - if ( cipherSpec.requiresIV() ) { - String ivType = ESAPI.securityConfiguration().getIVType(); - IvParameterSpec ivSpec = null; - if ( ivType.equalsIgnoreCase("random") ) { - ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize()); - } else if ( ivType.equalsIgnoreCase("fixed") ) { - String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV(); - ivBytes = Hex.decode(fixedIVAsHex); - /* FUTURE } else if ( ivType.equalsIgnoreCase("specified")) { - // FUTURE - TODO - Create instance of specified class to use for IV generation and - // use it to create the ivBytes. (The intent is to make sure that - // 1) IVs are never repeated for cipher modes like OFB and CFB, and - // 2) to screen for weak IVs for the particular cipher algorithm. - // In meantime, use 'random' for block cipher in feedback mode. Unlikely they will - // be repeated unless you are salting SecureRandom with same value each time. Anything - // monotonically increasing should be suitable, like a counter, but need to remember - // it across JVM restarts. Was thinking of using System.currentTimeMillis(). While - // it's not perfect it probably is good enough. Could even all (advanced) developers - // to define their own class to create a unique IV to allow them some choice, but - // definitely need to provide a safe, default implementation. - */ - } else { - // TODO: Update to add 'specified' once that is supported and added above. - throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'"); - } - ivSpec = new IvParameterSpec(ivBytes); - cipherSpec.setIV(ivBytes); - encrypter.init(Cipher.ENCRYPT_MODE, encKey, ivSpec); - } else { - encrypter.init(Cipher.ENCRYPT_MODE, encKey); - } - logger.debug(Logger.EVENT_SUCCESS, "Encrypting with " + cipherSpec); - byte[] raw = encrypter.doFinal(plaintext); + if ( preferredCipherMode ) { + encKey = key; + } else { + encKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), + key, keySize, "encryption"); + } + + if ( cipherSpec.requiresIV() ) { + String ivType = ESAPI.securityConfiguration().getIVType(); + IvParameterSpec ivSpec = null; + if ( ivType.equalsIgnoreCase("random") ) { + ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize()); + } else if ( ivType.equalsIgnoreCase("fixed") ) { + String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV(); + ivBytes = Hex.decode(fixedIVAsHex); + /* FUTURE } else if ( ivType.equalsIgnoreCase("specified")) { + // FUTURE - TODO - Create instance of specified class to use for IV generation and + // use it to create the ivBytes. (The intent is to make sure that + // 1) IVs are never repeated for cipher modes like OFB and CFB, and + // 2) to screen for weak IVs for the particular cipher algorithm. + // In meantime, use 'random' for block cipher in feedback mode. Unlikely they will + // be repeated unless you are salting SecureRandom with same value each time. Anything + // monotonically increasing should be suitable, like a counter, but need to remember + // it across JVM restarts. Was thinking of using System.currentTimeMillis(). While + // it's not perfect it probably is good enough. Could even all (advanced) developers + // to define their own class to create a unique IV to allow them some choice, but + // definitely need to provide a safe, default implementation. + */ + } else { + // TODO: Update to add 'specified' once that is supported and added above. + throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'"); + } + ivSpec = new IvParameterSpec(ivBytes); + cipherSpec.setIV(ivBytes); + encrypter.init(Cipher.ENCRYPT_MODE, encKey, ivSpec); + } else { + encrypter.init(Cipher.ENCRYPT_MODE, encKey); + } + logger.debug(Logger.EVENT_SUCCESS, "Encrypting with " + cipherSpec); + byte[] raw = encrypter.doFinal(plaintext); // Convert to CipherText. CipherText ciphertext = new CipherText(cipherSpec, raw); - - // If we are using a "preferred" cipher mode--i.e., one that supports *both* confidentiality and - // authenticity, there is no point to store a separate MAC in the CipherText object. Thus we only + + // If we are using a "preferred" cipher mode--i.e., one that supports *both* confidentiality and + // authenticity, there is no point to store a separate MAC in the CipherText object. Thus we only // do this when we are not using such a cipher mode. - if ( !preferredCipherMode ) { - // Compute derived key, and then use it to compute and store separate MAC in CipherText object. - SecretKey authKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), - key, keySize, "authenticity"); - ciphertext.computeAndStoreMAC( authKey ); - } - logger.debug(Logger.EVENT_SUCCESS, "JavaEncryptor.encrypt(SecretKey,byte[],boolean,boolean) -- success!"); - success = true; // W00t!!! - return ciphertext; - } catch (InvalidKeyException ike) { - throw new EncryptionException("Encryption failure: Invalid key exception.", - "Requested key size: " + keySize + "bits greater than 128 bits. Must install unlimited strength crypto extension from Sun: " + - ike.getMessage(), ike); - } catch (ConfigurationException cex) { - throw new EncryptionException("Encryption failure: Configuration error. Details in log.", "Key size mismatch or unsupported IV method. " + - "Check encryption key size vs. ESAPI.EncryptionKeyLength or Encryptor.ChooseIVMethod property.", cex); - } catch (InvalidAlgorithmParameterException e) { - throw new EncryptionException("Encryption failure (invalid IV)", - "Encryption problem: Invalid IV spec: " + e.getMessage(), e); - } catch (IllegalBlockSizeException e) { - throw new EncryptionException("Encryption failure (no padding used; invalid input size)", - "Encryption problem: Invalid input size without padding (" + xform + "). " + e.getMessage(), e); - } catch (BadPaddingException e) { - throw new EncryptionException("Encryption failure", - "[Note: Should NEVER happen in encryption mode.] Encryption problem: " + e.getMessage(), e); - } catch (NoSuchAlgorithmException e) { - throw new EncryptionException("Encryption failure (unavailable cipher requested)", - "Encryption problem: specified algorithm in cipher xform " + xform + " not available: " + e.getMessage(), e); - } catch (NoSuchPaddingException e) { - throw new EncryptionException("Encryption failure (unavailable padding scheme requested)", - "Encryption problem: specified padding scheme in cipher xform " + xform + " not available: " + e.getMessage(), e); - } finally { - // Don't overwrite anything in the case of exceptions because they may wish to retry. - if ( success && overwritePlaintext ) { - plain.overwrite(); // Note: Same as overwriting 'plaintext' byte array. + if ( !preferredCipherMode ) { + // Compute derived key, and then use it to compute and store separate MAC in CipherText object. + SecretKey authKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(), + key, keySize, "authenticity"); + ciphertext.computeAndStoreMAC( authKey ); + } + logger.debug(Logger.EVENT_SUCCESS, "JavaEncryptor.encrypt(SecretKey,byte[],boolean,boolean) -- success!"); + success = true; // W00t!!! + return ciphertext; + } catch (InvalidKeyException ike) { + throw new EncryptionException("Encryption failure: Invalid key exception.", + "Requested key size: " + keySize + "bits greater than 128 bits. Must install unlimited strength crypto extension from Sun: " + + ike.getMessage(), ike); + } catch (ConfigurationException cex) { + throw new EncryptionException("Encryption failure: Configuration error. Details in log.", "Key size mismatch or unsupported IV method. " + + "Check encryption key size vs. ESAPI.EncryptionKeyLength or Encryptor.ChooseIVMethod property.", cex); + } catch (InvalidAlgorithmParameterException e) { + throw new EncryptionException("Encryption failure (invalid IV)", + "Encryption problem: Invalid IV spec: " + e.getMessage(), e); + } catch (IllegalBlockSizeException e) { + throw new EncryptionException("Encryption failure (no padding used; invalid input size)", + "Encryption problem: Invalid input size without padding (" + xform + "). " + e.getMessage(), e); + } catch (BadPaddingException e) { + throw new EncryptionException("Encryption failure", + "[Note: Should NEVER happen in encryption mode.] Encryption problem: " + e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException("Encryption failure (unavailable cipher requested)", + "Encryption problem: specified algorithm in cipher xform " + xform + " not available: " + e.getMessage(), e); + } catch (NoSuchPaddingException e) { + throw new EncryptionException("Encryption failure (unavailable padding scheme requested)", + "Encryption problem: specified padding scheme in cipher xform " + xform + " not available: " + e.getMessage(), e); + } finally { + // Don't overwrite anything in the case of exceptions because they may wish to retry. + if ( success && overwritePlaintext ) { + plain.overwrite(); // Note: Same as overwriting 'plaintext' byte array. } - } + } } - /** - * {@inheritDoc} - */ - public PlainText decrypt(CipherText ciphertext) throws EncryptionException { - // Now more of a convenience function for using the master key. - return decrypt(secretKeySpec, ciphertext); - } - - /** - * {@inheritDoc} - */ - public PlainText decrypt(SecretKey key, CipherText ciphertext) - throws EncryptionException, IllegalArgumentException - { - long start = System.nanoTime(); // Current time in nanosecs; used to prevent timing attacks - if ( key == null ) { - throw new IllegalArgumentException("SecretKey arg may not be null"); - } - if ( ciphertext == null ) { - throw new IllegalArgumentException("Ciphertext may arg not be null"); - } - - if ( ! CryptoHelper.isAllowedCipherMode(ciphertext.getCipherMode()) ) { - // This really should be an illegal argument exception, but it could - // mean that a partner encrypted something using a cipher mode that - // you do not accept, so it's a bit more complex than that. Also - // throwing an IllegalArgumentException doesn't allow us to provide - // the two separate error messages or automatically log it. - throw new EncryptionException(DECRYPTION_FAILED, - "Invalid cipher mode " + ciphertext.getCipherMode() + - " not permitted for decryption or encryption operations."); - } - logger.debug(Logger.EVENT_SUCCESS, - "Args valid for JavaEncryptor.decrypt(SecretKey,CipherText): " + - ciphertext); - - PlainText plaintext = null; - boolean caughtException = false; - int progressMark = 0; - try { - // First we validate the MAC. - boolean valid = CryptoHelper.isCipherTextMACvalid(key, ciphertext); - if ( !valid ) { - try { - // This is going to fail, but we want the same processing - // to occur as much as possible so as to prevent timing - // attacks. We _could_ just be satisfied by the additional - // sleep in the 'finally' clause, but an attacker on the - // same server who can run something like 'ps' can tell - // CPU time versus when the process is sleeping. Hence we - // try to make this as close as possible. Since we know - // it is going to fail, we ignore the result and ignore - // the (expected) exception. - handleDecryption(key, ciphertext); // Ignore return (should fail). - } catch(Exception ex) { - ; // Ignore - } - throw new EncryptionException(DECRYPTION_FAILED, - "Decryption failed because MAC invalid for " + - ciphertext); - } - progressMark++; - // The decryption only counts if the MAC was valid. - plaintext = handleDecryption(key, ciphertext); - progressMark++; - } catch(EncryptionException ex) { - caughtException = true; - String logMsg = null; - switch( progressMark ) { - case 1: - logMsg = "Decryption failed because MAC invalid. See logged exception for details."; - break; - case 2: - logMsg = "Decryption failed because handleDecryption() failed. See logged exception for details."; - break; - default: - logMsg = "Programming error: unexpected progress mark == " + progressMark; - break; - } - logger.error(Logger.SECURITY_FAILURE, logMsg); - throw ex; // Re-throw - } - finally { - if ( caughtException ) { - // The rest of this code is to try to account for any minute differences - // in the time it might take for the various reasons that decryption fails - // in order to prevent any other possible timing attacks. Perhaps it is - // going overboard. If nothing else, if N_SECS is large enough, it might - // deter attempted repeated attacks by making them take much longer. - long now = System.nanoTime(); - long elapsed = now - start; - final long NANOSECS_IN_SEC = 1000000000L; // nanosec is 10**-9 sec - long nSecs = N_SECS * NANOSECS_IN_SEC; // N seconds in nano seconds - if ( elapsed < nSecs ) { - // Want to sleep so total time taken is N seconds. - long extraSleep = nSecs - elapsed; - - // 'extraSleep' is in nanoseconds. Need to convert to a millisec - // part and nanosec part. Nanosec is 10**-9, millsec is - // 10**-3, so divide by (10**-9 / 10**-3), or 10**6 to - // convert to from nanoseconds to milliseconds. - long millis = extraSleep / 1000000L; - long nanos = (extraSleep - (millis * 1000000L)); + /** + * {@inheritDoc} + */ + public PlainText decrypt(CipherText ciphertext) throws EncryptionException { + // Now more of a convenience function for using the master key. + return decrypt(secretKeySpec, ciphertext); + } + + /** + * {@inheritDoc} + */ + public PlainText decrypt(SecretKey key, CipherText ciphertext) + throws EncryptionException, IllegalArgumentException + { + long start = System.nanoTime(); // Current time in nanosecs; used to prevent timing attacks + if ( key == null ) { + throw new IllegalArgumentException("SecretKey arg may not be null"); + } + if ( ciphertext == null ) { + throw new IllegalArgumentException("Ciphertext may arg not be null"); + } + + if ( ! CryptoHelper.isAllowedCipherMode(ciphertext.getCipherMode()) ) { + // This really should be an illegal argument exception, but it could + // mean that a partner encrypted something using a cipher mode that + // you do not accept, so it's a bit more complex than that. Also + // throwing an IllegalArgumentException doesn't allow us to provide + // the two separate error messages or automatically log it. + throw new EncryptionException(DECRYPTION_FAILED, + "Invalid cipher mode " + ciphertext.getCipherMode() + + " not permitted for decryption or encryption operations."); + } + logger.debug(Logger.EVENT_SUCCESS, + "Args valid for JavaEncryptor.decrypt(SecretKey,CipherText): " + + ciphertext); + + PlainText plaintext = null; + boolean caughtException = false; + int progressMark = 0; + try { + // First we validate the MAC. + boolean valid = CryptoHelper.isCipherTextMACvalid(key, ciphertext); + if ( !valid ) { + try { + // This is going to fail, but we want the same processing + // to occur as much as possible so as to prevent timing + // attacks. We _could_ just be satisfied by the additional + // sleep in the 'finally' clause, but an attacker on the + // same server who can run something like 'ps' can tell + // CPU time versus when the process is sleeping. Hence we + // try to make this as close as possible. Since we know + // it is going to fail, we ignore the result and ignore + // the (expected) exception. + handleDecryption(key, ciphertext); // Ignore return (should fail). + } catch(Exception ex) { + ; // Ignore + } + throw new EncryptionException(DECRYPTION_FAILED, + "Decryption failed because MAC invalid for " + + ciphertext); + } + progressMark++; + // The decryption only counts if the MAC was valid. + plaintext = handleDecryption(key, ciphertext); + progressMark++; + } catch(EncryptionException ex) { + caughtException = true; + String logMsg = null; + switch( progressMark ) { + case 1: + logMsg = "Decryption failed because MAC invalid. See logged exception for details."; + break; + case 2: + logMsg = "Decryption failed because handleDecryption() failed. See logged exception for details."; + break; + default: + logMsg = "Programming error: unexpected progress mark == " + progressMark; + break; + } + logger.error(Logger.SECURITY_FAILURE, logMsg); + throw ex; // Re-throw + } + finally { + if ( caughtException ) { + // The rest of this code is to try to account for any minute differences + // in the time it might take for the various reasons that decryption fails + // in order to prevent any other possible timing attacks. Perhaps it is + // going overboard. If nothing else, if N_SECS is large enough, it might + // deter attempted repeated attacks by making them take much longer. + long now = System.nanoTime(); + long elapsed = now - start; + final long NANOSECS_IN_SEC = 1000000000L; // nanosec is 10**-9 sec + long nSecs = N_SECS * NANOSECS_IN_SEC; // N seconds in nano seconds + if ( elapsed < nSecs ) { + // Want to sleep so total time taken is N seconds. + long extraSleep = nSecs - elapsed; + + // 'extraSleep' is in nanoseconds. Need to convert to a millisec + // part and nanosec part. Nanosec is 10**-9, millsec is + // 10**-3, so divide by (10**-9 / 10**-3), or 10**6 to + // convert to from nanoseconds to milliseconds. + long millis = extraSleep / 1000000L; + long nanos = (extraSleep - (millis * 1000000L)); // N_SECS is hard-coded so assertion should be okay here. - assert nanos >= 0 && nanos <= Integer.MAX_VALUE : + assert nanos >= 0 && nanos <= Integer.MAX_VALUE : "Nanosecs out of bounds; nanos = " + nanos; - try { - Thread.sleep(millis, (int)nanos); - } catch(InterruptedException ex) { - ; // Ignore - } - } // Else ... time already exceeds N_SECS sec, so do not sleep. - } - } - return plaintext; - } + try { + Thread.sleep(millis, (int)nanos); + } catch(InterruptedException ex) { + ; // Ignore + } + } // Else ... time already exceeds N_SECS sec, so do not sleep. + } + } + return plaintext; + } // Handle the actual decryption portion. At this point it is assumed that // any MAC has already been validated. (But see "DISCUSS" issue, below.) @@ -677,19 +677,19 @@ private PlainText handleDecryption(SecretKey key, CipherText ciphertext) // TODO: PERFORMANCE: Calculate avg time this takes and consider caching for very short interval // (e.g., 2 to 5 sec tops). Otherwise doing lots of encryptions in a loop could take a LOT longer. // But remember Jon Bentley's "Rule #1 on performance: First make it right, then make it fast." - // This would be a security trade-off as it would leave keys in memory a bit longer, so it - // should probably be off by default and controlled via a property. - // - // TODO: Feed in some additional parms here to use as the 'context' for the - // KeyDerivationFunction...especially the KDF version. We would have to - // store that in the CipherText object. We *possibly* could make it - // transient so it would not be serialized with the CipherText object, - // otherwise we would have to implement readObject() and writeObject() - // methods there to support backward compatibility. Anyhow the intent - // is to prevent down grade attacks when we finally re-design and - // re-implement the MAC. Think about this in version 2.1.1. + // This would be a security trade-off as it would leave keys in memory a bit longer, so it + // should probably be off by default and controlled via a property. + // + // TODO: Feed in some additional parms here to use as the 'context' for the + // KeyDerivationFunction...especially the KDF version. We would have to + // store that in the CipherText object. We *possibly* could make it + // transient so it would not be serialized with the CipherText object, + // otherwise we would have to implement readObject() and writeObject() + // methods there to support backward compatibility. Anyhow the intent + // is to prevent down grade attacks when we finally re-design and + // re-implement the MAC. Think about this in version 2.1.1. encKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(), - key, keySize, "encryption"); + key, keySize, "encryption"); } if ( ciphertext.requiresIV() ) { decrypter.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(ciphertext.getIV())); @@ -713,16 +713,16 @@ private PlainText handleDecryption(SecretKey key, CipherText ciphertext) } catch (IllegalBlockSizeException e) { throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e); } catch (BadPaddingException e) { - //DISCUSS: This needs fixed. Already validated MAC in CryptoHelper.isCipherTextMACvalid() above. - //So only way we could get a padding exception is if invalid padding were used originally by - //the party doing the encryption. (This might happen with a buggy padding scheme for instance.) - //It *seems* harmless though, so will leave it for now, and technically, we need to either catch it - //or declare it in a throws class. Clearly we don't want to do the later. This should be discussed - //during a code inspection. + // DISCUSS: This needs fixed. Already validated MAC in CryptoHelper.isCipherTextMACvalid() above. + // So only way we could get a padding exception is if invalid padding were used originally by + // the party doing the encryption. (This might happen with a buggy padding scheme for instance.) + // It *seems* harmless though, so will leave it for now, and technically, we need to either catch it + // or declare it in a throws class. Clearly we don't want to do the later. This should be discussed + // during a code inspection. SecretKey authKey; try { authKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(), - key, keySize, "authenticity"); + key, keySize, "authenticity"); } catch (Exception e1) { throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem -- failed to compute derived key for authenticity: " + e1.getMessage(), e1); @@ -737,187 +737,187 @@ private PlainText handleDecryption(SecretKey key, CipherText ciphertext) } } } - - /** - * {@inheritDoc} - */ - public String sign(String data) throws EncryptionException { - try { - Signature signer = Signature.getInstance(signatureAlgorithm); - signer.initSign(privateKey); - signer.update(data.getBytes(encoding)); - byte[] bytes = signer.sign(); - return ESAPI.encoder().encodeForBase64(bytes, false); - } catch (InvalidKeyException ike) { - throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); - } catch (Exception e) { - throw new EncryptionException("Signature failure", "Can't find signature algorithm " + signatureAlgorithm, e); - } - } - - /** - * {@inheritDoc} - */ - public boolean verifySignature(String signature, String data) { - try { - byte[] bytes = ESAPI.encoder().decodeFromBase64(signature); - Signature signer = Signature.getInstance(signatureAlgorithm); - signer.initVerify(publicKey); - signer.update(data.getBytes(encoding)); - return signer.verify(bytes); - } catch (Exception e) { - // NOTE: EncryptionException constructed *only* for side-effect of causing logging. - // FindBugs complains about this and since it examines byte-code, there's no way to - // shut it up. - new EncryptionException("Invalid signature", "Problem verifying signature: " + e.getMessage(), e); - return false; - } - } - - /** - * {@inheritDoc} + + /** + * {@inheritDoc} + */ + public String sign(String data) throws EncryptionException { + try { + Signature signer = Signature.getInstance(signatureAlgorithm); + signer.initSign(privateKey); + signer.update(data.getBytes(encoding)); + byte[] bytes = signer.sign(); + return ESAPI.encoder().encodeForBase64(bytes, false); + } catch (InvalidKeyException ike) { + throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); + } catch (Exception e) { + throw new EncryptionException("Signature failure", "Can't find signature algorithm " + signatureAlgorithm, e); + } + } + + /** + * {@inheritDoc} + */ + public boolean verifySignature(String signature, String data) { + try { + byte[] bytes = ESAPI.encoder().decodeFromBase64(signature); + Signature signer = Signature.getInstance(signatureAlgorithm); + signer.initVerify(publicKey); + signer.update(data.getBytes(encoding)); + return signer.verify(bytes); + } catch (Exception e) { + // NOTE: EncryptionException constructed *only* for side-effect of causing logging. + // FindBugs complains about this and since it examines byte-code, there's no way to + // shut it up. + new EncryptionException("Invalid signature", "Problem verifying signature: " + e.getMessage(), e); + return false; + } + } + + /** + * {@inheritDoc} * * @param expiration * @throws IntegrityException */ - public String seal(String data, long expiration) throws IntegrityException { - if ( data == null ) { - throw new IllegalArgumentException("Data to be sealed may not be null."); - } - - try { - String b64data = null; + public String seal(String data, long expiration) throws IntegrityException { + if ( data == null ) { + throw new IllegalArgumentException("Data to be sealed may not be null."); + } + + try { + String b64data = null; try { b64data = ESAPI.encoder().encodeForBase64(data.getBytes("UTF-8"), false); } catch (UnsupportedEncodingException e) { ; // Ignore; should never happen since UTF-8 built into rt.jar } - // mix in some random data so even identical data and timestamp produces different seals - String nonce = ESAPI.randomizer().getRandomString(10, EncoderConstants.CHAR_ALPHANUMERICS); - String plaintext = expiration + ":" + nonce + ":" + b64data; - // add integrity check; signature is already base64 encoded. - String sig = this.sign( plaintext ); - CipherText ciphertext = this.encrypt( new PlainText(plaintext + ":" + sig) ); - String sealedData = ESAPI.encoder().encodeForBase64(ciphertext.asPortableSerializedByteArray(), false); - return sealedData; - } catch( EncryptionException e ) { - throw new IntegrityException( e.getUserMessage(), e.getLogMessage(), e ); - } - } - - /** - * {@inheritDoc} - */ - public String unseal(String seal) throws EncryptionException { - PlainText plaintext = null; - try { - byte[] encryptedBytes = ESAPI.encoder().decodeFromBase64(seal); - CipherText cipherText = null; - try { - cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes); - } catch( AssertionError e) { - // Some of the tests in EncryptorTest.testVerifySeal() are examples of - // this if assertions are enabled, but otherwise it should not + // mix in some random data so even identical data and timestamp produces different seals + String nonce = ESAPI.randomizer().getRandomString(10, EncoderConstants.CHAR_ALPHANUMERICS); + String plaintext = expiration + ":" + nonce + ":" + b64data; + // add integrity check; signature is already base64 encoded. + String sig = this.sign( plaintext ); + CipherText ciphertext = this.encrypt( new PlainText(plaintext + ":" + sig) ); + String sealedData = ESAPI.encoder().encodeForBase64(ciphertext.asPortableSerializedByteArray(), false); + return sealedData; + } catch( EncryptionException e ) { + throw new IntegrityException( e.getUserMessage(), e.getLogMessage(), e ); + } + } + + /** + * {@inheritDoc} + */ + public String unseal(String seal) throws EncryptionException { + PlainText plaintext = null; + try { + byte[] encryptedBytes = ESAPI.encoder().decodeFromBase64(seal); + CipherText cipherText = null; + try { + cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes); + } catch( AssertionError e) { + // Some of the tests in EncryptorTest.testVerifySeal() are examples of + // this if assertions are enabled, but otherwise it should not // normally happen. - throw new EncryptionException("Invalid seal", - "Seal passed garbarge data resulting in AssertionError: " + e); - } - plaintext = this.decrypt(cipherText); - - String[] parts = plaintext.toString().split(":"); - if (parts.length != 4) { - throw new EncryptionException("Invalid seal", "Seal was not formatted properly."); - } - - String timestring = parts[0]; - long now = new Date().getTime(); - long expiration = Long.parseLong(timestring); - if (now > expiration) { - throw new EncryptionException("Invalid seal", "Seal expiration date of " + new Date(expiration) + " has past."); - } - String nonce = parts[1]; - String b64data = parts[2]; - String sig = parts[3]; - if (!this.verifySignature(sig, timestring + ":" + nonce + ":" + b64data ) ) { - throw new EncryptionException("Invalid seal", "Seal integrity check failed"); - } - return new String(ESAPI.encoder().decodeFromBase64(b64data), "UTF-8"); - } catch (EncryptionException e) { - throw e; - } catch (Exception e) { - throw new EncryptionException("Invalid seal", "Invalid seal:" + e.getMessage(), e); - } - } - - - /** - * {@inheritDoc} - */ - public boolean verifySeal( String seal ) { - try { - unseal( seal ); - return true; - } catch( EncryptionException e ) { - return false; - } - } - - /** - * {@inheritDoc} - */ - public long getTimeStamp() { - return new Date().getTime(); - } - - /** - * {@inheritDoc} - */ - public long getRelativeTimeStamp( long offset ) { - return new Date().getTime() + offset; - } - - // DISCUSS: Why experimental? Would have to be added to Encryptor interface - // but only 3 things I saw wrong with this was 1) it used HMacMD5 instead - // of HMacSHA1 (see discussion below), 2) that the HMac key is the - // same one used for encryption (also see comments), and 3) it caught - // overly broad exceptions. Here it is with these specific areas - // addressed, but no unit testing has been done at this point. -kww + throw new EncryptionException("Invalid seal", + "Seal passed garbarge data resulting in AssertionError: " + e); + } + plaintext = this.decrypt(cipherText); + + String[] parts = plaintext.toString().split(":"); + if (parts.length != 4) { + throw new EncryptionException("Invalid seal", "Seal was not formatted properly."); + } + + String timestring = parts[0]; + long now = new Date().getTime(); + long expiration = Long.parseLong(timestring); + if (now > expiration) { + throw new EncryptionException("Invalid seal", "Seal expiration date of " + new Date(expiration) + " has past."); + } + String nonce = parts[1]; + String b64data = parts[2]; + String sig = parts[3]; + if (!this.verifySignature(sig, timestring + ":" + nonce + ":" + b64data ) ) { + throw new EncryptionException("Invalid seal", "Seal integrity check failed"); + } + return new String(ESAPI.encoder().decodeFromBase64(b64data), "UTF-8"); + } catch (EncryptionException e) { + throw e; + } catch (Exception e) { + throw new EncryptionException("Invalid seal", "Invalid seal:" + e.getMessage(), e); + } + } + + + /** + * {@inheritDoc} + */ + public boolean verifySeal( String seal ) { + try { + unseal( seal ); + return true; + } catch( EncryptionException e ) { + return false; + } + } + + /** + * {@inheritDoc} + */ + public long getTimeStamp() { + return new Date().getTime(); + } + + /** + * {@inheritDoc} + */ + public long getRelativeTimeStamp( long offset ) { + return new Date().getTime() + offset; + } + + // DISCUSS: Why experimental? Would have to be added to Encryptor interface + // but only 3 things I saw wrong with this was 1) it used HMacMD5 instead + // of HMacSHA1 (see discussion below), 2) that the HMac key is the + // same one used for encryption (also see comments), and 3) it caught + // overly broad exceptions. Here it is with these specific areas + // addressed, but no unit testing has been done at this point. -kww /** * Compute an HMAC for a String. Experimental. - * @param input The input for which to compute the HMac. + * @param input The input for which to compute the HMac. */ /******************** - public String computeHMAC( String input ) throws EncryptionException { - try { - Mac hmac = Mac.getInstance("HMacSHA1"); // DISCUSS: Changed to HMacSHA1. MD5 *badly* broken - // SHA1 should really be avoided, but using - // for HMAC-SHA1 is acceptable for now. Plan - // to migrate to SHA-256 or NIST replacement for - // SHA1 in not too distant future. - // DISCUSS: Also not recommended that the HMac key is the same as the one - // used for encryption (namely, Encryptor.MasterKey). If anything it - // would be better to use Encryptor.MasterSalt for the HMac key, or - // perhaps a derived key based on the master salt. (One could use - // KeyDerivationFunction.computeDerivedKey().) - // - byte[] salt = ESAPI.securityConfiguration().getMasterSalt(); - hmac.init( new SecretKeySpec(salt, "HMacSHA1") ); // Was: hmac.init(secretKeySpec) - byte[] inBytes; - try { - inBytes = input.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - logger.warning(Logger.SECURITY_FAILURE, "computeHMAC(): Can't find UTF-8 encoding; using default encoding", e); - inBytes = input.getBytes(); - } - byte[] bytes = hmac.doFinal( inBytes ); - return ESAPI.encoder().encodeForBase64(bytes, false); - } catch (InvalidKeyException ike) { - throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); - } catch (NoSuchAlgorithmException e) { - throw new EncryptionException("Could not compute HMAC", "Can't find HMacSHA1 algorithm. " + - "Problem computing HMAC for " + input, e ); - } - } + public String computeHMAC( String input ) throws EncryptionException { + try { + Mac hmac = Mac.getInstance("HMacSHA1"); // DISCUSS: Changed to HMacSHA1. MD5 *badly* broken + // SHA1 should really be avoided, but using + // for HMAC-SHA1 is acceptable for now. Plan + // to migrate to SHA-256 or NIST replacement for + // SHA1 in not too distant future. + // DISCUSS: Also not recommended that the HMac key is the same as the one + // used for encryption (namely, Encryptor.MasterKey). If anything it + // would be better to use Encryptor.MasterSalt for the HMac key, or + // perhaps a derived key based on the master salt. (One could use + // KeyDerivationFunction.computeDerivedKey().) + // + byte[] salt = ESAPI.securityConfiguration().getMasterSalt(); + hmac.init( new SecretKeySpec(salt, "HMacSHA1") ); // Was: hmac.init(secretKeySpec) + byte[] inBytes; + try { + inBytes = input.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.warning(Logger.SECURITY_FAILURE, "computeHMAC(): Can't find UTF-8 encoding; using default encoding", e); + inBytes = input.getBytes(); + } + byte[] bytes = hmac.doFinal( inBytes ); + return ESAPI.encoder().encodeForBase64(bytes, false); + } catch (InvalidKeyException ike) { + throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException("Could not compute HMAC", "Can't find HMacSHA1 algorithm. " + + "Problem computing HMAC for " + input, e ); + } + } ********************/ /** @@ -926,7 +926,7 @@ public String computeHMAC( String input ) throws EncryptionException { * may be changed via the system property * {@code ESAPI.Encryptor.warnEveryNthUse}.) In other words, we nag * them until the give in and change it. ;-) - * + * * @param where The string "encrypt" or "decrypt", corresponding to the * method that is being logged. * @param msg The message to log. @@ -949,54 +949,54 @@ private void logWarning(String where, String msg) { logger.warning(Logger.SECURITY_FAILURE, where + msg); } } - - private KeyDerivationFunction.PRF_ALGORITHMS getPRF(String name) { - String prfName = null; - if ( name == null ) { - prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); - } else { - prfName = name; - } - KeyDerivationFunction.PRF_ALGORITHMS prf = KeyDerivationFunction.convertNameToPRF(prfName); - return prf; + + private KeyDerivationFunction.PRF_ALGORITHMS getPRF(String name) { + String prfName = null; + if ( name == null ) { + prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); + } else { + prfName = name; + } + KeyDerivationFunction.PRF_ALGORITHMS prf = KeyDerivationFunction.convertNameToPRF(prfName); + return prf; } - + private KeyDerivationFunction.PRF_ALGORITHMS getDefaultPRF() { - String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); - return getPRF(prfName); + String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction(); + return getPRF(prfName); } - + // Private interface to call ESAPI's KDF to get key for encryption or authenticity. private SecretKey computeDerivedKey(int kdfVersion, KeyDerivationFunction.PRF_ALGORITHMS prf, - SecretKey kdk, int keySize, String purpose) - throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException + SecretKey kdk, int keySize, String purpose) + throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException { - // These really should be turned into actual runtime checks and an - // IllegalArgumentException should be thrown if they are violated. - // But this should be OK since this is a private method. Also, this method will - // be called quite often so assertions are a big win as they can be disabled or - // enabled at will. - assert prf != null : "Pseudo Random Function for KDF cannot be null"; - assert kdk != null : "Key derivation key cannot be null."; - // We would choose a larger minimum key size, but we want to be - // able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that, - // we print warning. - assert keySize >= 56 : "Key has size of " + keySize + ", which is less than absolute minimum of 56-bits."; - assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits."; + // These really should be turned into actual runtime checks and an + // IllegalArgumentException should be thrown if they are violated. + // But this should be OK since this is a private method. Also, this method will + // be called quite often so assertions are a big win as they can be disabled or + // enabled at will. + assert prf != null : "Pseudo Random Function for KDF cannot be null"; + assert kdk != null : "Key derivation key cannot be null."; + // We would choose a larger minimum key size, but we want to be + // able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that, + // we print warning. + assert keySize >= 56 : "Key has size of " + keySize + ", which is less than absolute minimum of 56-bits."; + assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits."; // However, this one we want as a runtime check because we don't have this check // in KeyDerivationFunction.computeDerivedKey() as we want that method // to be more general. - if ( !( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { + if ( !( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { String exMsg = "Programming error in ESAPI?? 'purpose' for computeDerivedKey() must be \"encryption\" or \"authenticity\"."; - throw new EncryptionException(exMsg, exMsg); + throw new EncryptionException(exMsg, exMsg); } - KeyDerivationFunction kdf = new KeyDerivationFunction(prf); - if ( kdfVersion != 0 ) { - kdf.setVersion(kdfVersion); - } - return kdf.computeDerivedKey(kdk, keySize, purpose); + KeyDerivationFunction kdf = new KeyDerivationFunction(prf); + if ( kdfVersion != 0 ) { + kdf.setVersion(kdfVersion); + } + return kdf.computeDerivedKey(kdk, keySize, purpose); } // Get all the algorithms we will be using from ESAPI.properties. @@ -1011,7 +1011,7 @@ private static void setupAlgorithms() { encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength(); signatureKeyLength = ESAPI.securityConfiguration().getDigitalSignatureKeyLength(); } - + // Set up signing key pair using the master password and salt. Called (once) // from the JavaEncryptor CTOR. private static void initKeyPair(SecureRandom prng) throws NoSuchAlgorithmException { From a27c36aaeeb9f6d95a873d28021fc99b471b5bda Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 18:42:32 -0400 Subject: [PATCH 011/544] Added KDF PRF & version to toString() method to make it complete. --- src/main/java/org/owasp/esapi/crypto/CipherText.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java index aacf488a9..fe13a3df6 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -697,9 +697,12 @@ public String toString() { int n = getRawCipherTextByteLength(); String rawCipherText = (( n > 0 ) ? "present (" + n + " bytes)" : "absent"); String mac = (( separate_mac_ != null ) ? "present" : "absent"); - sb.append("Creation time: ").append(creationTime); - sb.append(", raw ciphertext is ").append(rawCipherText); - sb.append(", MAC is ").append(mac).append("; "); + + sb.append("KDF Version: ").append( kdfVersion_ ); + sb.append(", KDF PRF: ").append( kdfPRFAsInt() ); + sb.append("; Creation time: ").append(creationTime); + sb.append("; raw ciphertext is ").append(rawCipherText); + sb.append("; MAC is ").append(mac).append("; "); sb.append( cipherSpec_.toString() ); return sb.toString(); } From 591e03441bcc5c33a5eabc20e2f656044be1fa58 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 22:34:46 -0400 Subject: [PATCH 012/544] Change required by tweak to CipherText.toString() method. --- .../java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java b/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java index cba2da0ae..5db8aeb82 100644 --- a/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java +++ b/src/test/java/org/owasp/esapi/crypto/SecurityProviderLoaderTest.java @@ -128,7 +128,7 @@ public final void testWithBouncyCastle() { // validate this, so we look at the String representation of this CipherText // object and pick it out of there. String str = ct.toString(); - assertTrue( str.matches(".*, MAC is absent;.*") ); + assertTrue( str.matches(".*; MAC is absent;.*") ); } catch (EncryptionException e) { fail("Encryption w/ Bouncy Castle failed with EncryptionException for preferred " + "cipher transformation; exception was: " + e); From 051d742c444b7e28bf4adab9d2091599c486036e Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 22:42:45 -0400 Subject: [PATCH 013/544] Add more javadoc on a private method. --- src/main/java/org/owasp/esapi/crypto/CipherText.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java index fe13a3df6..8e57f03e7 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -795,6 +795,15 @@ protected boolean canEqual(Object other) { *
      *      HMAC-SHA1(nonce, IV + plaintext)
      * 
+ * Note that only HMAC-SHA1 is used for the MAC calcuation. Unlike + * the PRF used for derived key generation in the {@code KeyDerivationFunction} + * class, the user cannot change the algorithm used to compute the MAC itself. + * One reason for that is that we don't want the MAC value to be excessively + * long; 128 bits is already quite long when only encrypting short strings. + * Also while the NSA reviewed this and were okay with it, Bellare, Canetti & Krawczyk + * proved in 1996 [see http://pssic.free.fr/Extra%20Reading/SEC+/SEC+/hmac-cb.pdf] that + * HMAC security doesn’t require that the underlying hash function be collision resistant, + * but only that it acts as a pseudo-random function, which SHA1 satisfies. * @param ciphertext The ciphertext value for which the MAC is computed. * @return The value for the MAC. */ From 363156a82f208e848e2716ac58e4ca72685fbbd6 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 22:47:22 -0400 Subject: [PATCH 014/544] Removed call to deprecated CryptoHelper.computeDerivedKey() method. --- src/test/java/org/owasp/esapi/crypto/CipherTextTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java b/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java index cb2f22b97..b3a6b5587 100644 --- a/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CipherTextTest.java @@ -191,9 +191,8 @@ public final void testMIC() { encryptor.init(Cipher.ENCRYPT_MODE, key, ivSpec); byte[] raw = encryptor.doFinal("This is my secret message!!!".getBytes("UTF8")); CipherText ciphertext = new CipherText(cipherSpec, raw); - // TODO: Replace this w/ call to KeyDerivationFunction as this is - // deprecated! Shame on me! - SecretKey authKey = CryptoHelper.computeDerivedKey(key, key.getEncoded().length * 8, "authenticity"); + KeyDerivationFunction kdf = new KeyDerivationFunction( KeyDerivationFunction.PRF_ALGORITHMS.HmacSHA1 ); + SecretKey authKey = kdf.computeDerivedKey(key, key.getEncoded().length * 8, "authenticity"); ciphertext.computeAndStoreMAC( authKey ); // System.err.println("Original ciphertext being serialized: " + ciphertext); byte[] serializedBytes = ciphertext.asPortableSerializedByteArray(); From 1ca26f5c60f7e7a4afd007665615f2a4248c27ed Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 22:49:11 -0400 Subject: [PATCH 015/544] Replaced call to deprecated CryptoHelper.computeDerivedKey(). Comment clean-up. --- .../org/owasp/esapi/crypto/CryptoHelper.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java index 4ba553911..11a6bcb9f 100644 --- a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java +++ b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java @@ -126,18 +126,23 @@ public static SecretKey generateSecretKey(String alg, int keySize) * this is thrown with the original {@code UnsupportedEncodingException} * as the cause. (NOTE: This should never happen as "UTF-8" is supposed to * be a common encoding supported by all Java implementations. Support - * for it is usually in rt.jar.) + * for it is usually in rt.jar.) This exception is also thrown if the + * requested {@code keySize} parameter exceeds the length of the number of + * bytes provded in the {@code keyDerivationKey} parameter. * @throws InvalidKeyException Likely indicates a coding error. Should not happen. * @throws EncryptionException Throw for some precondition violations. - * @deprecated Use{@code KeyDerivationFunction} instead. This method will be removed as of - * ESAPI release 2.3 so if you are using this, please change your code. + * @deprecated Use same method in {@code KeyDerivationFunction} instead. This method will be removed as of + * ESAPI release 2.3 so if you are using this, please CHANGE YOUR CODE. Note that the replacement + * is not a static method, so create your own wrapper if you wish, but this will soon disappear. */ @Deprecated public static SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, String purpose) throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException { - // These really should be turned into actual runtime checks and an - // IllegalArgumentException should be thrown if they are violated. + // Fingers cross; maybe this will help. + logger.warning(Logger.SECURITY_AUDIT, + "Your code is using the deprecated CryptoHelper.computeDerivedKey() method which will be removed next release"); + if ( keyDerivationKey == null ) { throw new IllegalArgumentException("Key derivation key cannot be null."); } @@ -159,6 +164,9 @@ public static SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySiz // DISCUSS: Should we use HmacSHA1 (what we were using) or the HMAC defined by // Encryptor.KDF.PRF instead? Either way, this is not compatible with // previous ESAPI versions. JavaEncryptor doesn't use this any longer. + // ANSWER: This is deprecated and will be removed in 2.3.0.0, so it really matter + // that much. However, Since the property Encryptor.KDF.PRF is (and has + // been) "HMacSHA256". changing this could unintentionally break code. KeyDerivationFunction kdf = new KeyDerivationFunction( KeyDerivationFunction.PRF_ALGORITHMS.HmacSHA1); return kdf.computeDerivedKey(keyDerivationKey, keySize, purpose); @@ -260,7 +268,8 @@ public static boolean isCipherTextMACvalid(SecretKey sk, CipherText ct) { if ( CryptoHelper.isMACRequired( ct ) ) { try { - SecretKey authKey = CryptoHelper.computeDerivedKey( sk, ct.getKeySize(), "authenticity"); + KeyDerivationFunction kdf = new KeyDerivationFunction( ct.getKDF_PRF() ); + SecretKey authKey = kdf.computeDerivedKey(sk, ct.getKeySize(), "authenticity"); boolean validMAC = ct.validateMAC( authKey ); return validMAC; } catch (Exception ex) { From f7ac215b2b61399a5e9c2e918bc03d8dda7076fa Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 22:56:58 -0400 Subject: [PATCH 016/544] Close GitHub issue #245. Add and clean-up some comments. Allow 'purpose' for argument in KeyDerivationFunction.computeDerivedKey() to be generalized. (I.e., removed checks to restrict it to "encryption" and "authenticity".) That allows this method to be used in a more general KDF context. --- .../esapi/crypto/KeyDerivationFunction.java | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java b/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java index ccf8a0ba0..7535eece2 100644 --- a/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java +++ b/src/main/java/org/owasp/esapi/crypto/KeyDerivationFunction.java @@ -187,7 +187,7 @@ public int getVersion() { } - // TODO: IMPORTANT NOTE: In a future release (hopefully starting in 2.1.1), + // TODO: IMPORTANT NOTE: In a future release (hopefully starting in 2.3), // we will be using the 'context' to mix in some additional things. At a // minimum, we will be using the KDF version (version_) so that downgrade version // attacks are not possible. Other candidates are the cipher xform and @@ -267,24 +267,26 @@ public String getContext() { * @param keySize The cipher's key size (in bits) for the {@code keyDerivationKey}. * Must have a minimum size of 56 bits and be an integral multiple of 8-bits. * Note: The derived key will have the same size as this. - * @param purpose The purpose for the derived key. For the ESAPI reference implementation, + * @param purpose The purpose for the derived key. IMPORTANT: For the ESAPI reference implementation, * {@code JavaEncryptor}, this must be either the string "encryption" or * "authenticity", where "encryption" is used for creating a derived key to use * for confidentiality, and "authenticity" is used for creating a derived key to * use with a MAC to ensure message authenticity. However, since parameter serves * the same purpose as the "Label" in section 5.1 of NIST SP 800-108, it really can * be set to anything other than {@code null} or an empty string when called outside - * of {@code JavaEncryptor}. + * of ESAPI's {@code JavaEncryptor} reference implementation (but you must consistent). * @return The derived {@code SecretKey} to be used according * to the specified purpose. * @throws NoSuchAlgorithmException The {@code keyDerivationKey} has an unsupported - * encryption algorithm or no current JCE provider supports - * "HmacSHA1". + * encryption algorithm or no current JCE provider supports requested + * Hmac algorithrm used for the PRF for key generation. * @throws EncryptionException If "UTF-8" is not supported as an encoding, then * this is thrown with the original {@code UnsupportedEncodingException} * as the cause. (NOTE: This should never happen as "UTF-8" is supposed to * be a common encoding supported by all Java implementations. Support - * for it is usually in rt.jar.) + * for it is usually in rt.jar.) This exception is also thrown if the + * requested {@code keySize} parameter exceeds the length of the number of + * bytes provded in the {@code keyDerivationKey} parameter. * @throws InvalidKeyException Likely indicates a coding error. Should not happen. * @throws EncryptionException Throw for some precondition violations. */ @@ -322,7 +324,29 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri throw new IllegalArgumentException("Purpose may not be null or empty."); } - keySize = calcKeySize( keySize ); // Safely convert to whole # of bytes. + // + // No longer, since we no longer wish to restrict this to use only by JavaEncryptor, so + // we no longer test for this. For details, see the javadoc for '@param purpose', above. + // + /* + * + * if ( ! ( purpose.equals("encryption") || purpose.equals("authenticity") ) ) { + * throw new IllegalArgumentException("Purpose must be \"encryption\" or \"authenticity\"."); + * } + * + */ + + int providedKeyLen = 8 * keyDerivationKey.getEncoded().length; + // assert providedKeyLen >= 56 : "Coding error? Length of keyDerivationKey < 56 bits!"; // Ugh. DES compatible. + + if ( providedKeyLen < keySize ) { + throw new EncryptionException("KeyDerivationFunction.computeDerivedKey() not intended for key stretching: " + + "provided key too short (" + providedKeyLen + " bits) to provide " + keySize + " bits.", + "Key stretching not supported: Provided key, keyDerivationKey, has insufficient entropy (" + + providedKeyLen + " bits) to generate key of requested size of " + keySize + " bits."); + } + + keySize = calcKeySize( keySize ); // Safely convert from bits to a whole # of bytes. byte[] derivedKey = new byte[ keySize ]; byte[] label; // Same purpose as NIST SP 800-108's "label" in section 5.1. byte[] context; // See setContext() for details. @@ -344,20 +368,20 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri // under the hood to create 'sk' so that it is 20 bytes. I cannot vouch // for how secure this key-stretching is. Worse, it might not be specified // as to *how* it is done and left to each JCE provider. - SecretKey sk = new SecretKeySpec(keyDerivationKey.getEncoded(), "HmacSHA1"); + SecretKey sk = new SecretKeySpec(keyDerivationKey.getEncoded(), prfAlg_ ); Mac mac = null; try { - mac = Mac.getInstance("HmacSHA1"); + mac = Mac.getInstance( prfAlg_ ); mac.init(sk); } catch( InvalidKeyException ex ) { logger.error(Logger.SECURITY_FAILURE, - "Created HmacSHA1 Mac but SecretKey sk has alg " + + "Created " + prfAlg_ + " Mac but SecretKey sk has alg " + sk.getAlgorithm(), ex); throw ex; } - // Repeatedly call of HmacSHA1 hash until we've collected enough bits + // Repeatedly call of PRF Hmac until we've collected enough bits // for the derived key. The first time through, we calculate the HmacSHA1 // on the "purpose" string, but subsequent calculations are performed // on the previous result. @@ -415,6 +439,15 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri // Don't leave remnants of the partial key in memory. (Note: we could // not do this if tmpKey were declared in the do-while loop. + // Of course, in reality, trying to stomp these bits out is probably not + // realistic because the JIT is likely toing to be smart enough to + // optimze this loop away. We probably could try to outsmart it, by + // (say) writing out the overwritten bits to /dev/null, but then even + // then we'd still probably have to overwrite with random bits rather + // than all null chars. How much is enough? Who knows? But it does point + // to a serious limitation in Java and many other languages that one + // cannot arbitrarily disable the optimizer either at compile time or + // run time because of security reasons. Sigh. At least we've tried. for ( int i = 0; i < tmpKey.length; i++ ) { tmpKey[i] = '\0'; } From efe07c30155cb6b393019998d1c6b6ebaaa7bb3b Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 23:23:55 -0400 Subject: [PATCH 017/544] Minor documentation corrections. --- ...java-core-2.0-ciphertext-serialization.pdf | Bin 136825 -> 63163 bytes ...java-core-2.0-ciphertext-serialization.xls | Bin 16384 -> 16384 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf b/documentation/esapi4java-core-2.0-ciphertext-serialization.pdf index 9d1dc3399c7d7aea90b7c3213fdc6c853f691615..37d68a46736fa7ead64c8919c59b53e201fcd062 100644 GIT binary patch literal 63163 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 literal 136825 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%0Alhizm0V?GU?ecRlj- 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 diff --git a/documentation/esapi4java-core-2.0-ciphertext-serialization.xls b/documentation/esapi4java-core-2.0-ciphertext-serialization.xls index 7f3106ea0b58caf2a32cd2342918e6e14cea83f4..86b7de7a00da69dd9970a277e9bfee3a4167aa14 100644 GIT binary patch delta 575 zcmYjO&ubG=5T4nMY22jE(pD5}lD7>xg>Guo#zsVqCT*mb(x3%zOWok=nuN`!5F}*r zBKQm8A$aj39t01TJ`fK=3*J1owFFwkKcIqw7omca7{NDu%zX0=Gw;pwJoG$V+x1I7 zQFQz}=r=2-W7uLna7)H*9tp+qJL z^1nfW5%L;b=9^O+M*@-x8n2}z!LFk9lqLQk?F^|D1C3Y7XwAy>JzURxm+%AsF&o!B z%F#4-h@ zS42V?7#M;!yYpUR1PV#zlM=%a-2Zu=IsLa7=g@bLe-l^g|BfjN=<$vD>J!Y z?lz;t?1(i3N7WSIO*znhU^bDKde6TcHs<}U*mgD{X*WN?`*WW1jn#8&|G11JA8F5*)K zIsj+~gDMa*GjFalX=0qb!K7r9*#{=ZBa=T`C@XdV<+*@(3J`Mx@d6+Qd2|gB^8)dX z$)=XtlU*!j1VJVln=vpr0BHu*=ksJIw_3_`FmxSNXWo07aq|{S7iLDy%|C7G85zH9 JuC!}s1OQnkWOM)k From 4ccc558ec941c4d7770207f6f9f62187628db59c Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 14 Jul 2019 23:24:43 -0400 Subject: [PATCH 018/544] New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class. --- .../crypto/KeyDerivationFunctionTest.java | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/crypto/KeyDerivationFunctionTest.java diff --git a/src/test/java/org/owasp/esapi/crypto/KeyDerivationFunctionTest.java b/src/test/java/org/owasp/esapi/crypto/KeyDerivationFunctionTest.java new file mode 100644 index 000000000..3fdf7c8f4 --- /dev/null +++ b/src/test/java/org/owasp/esapi/crypto/KeyDerivationFunctionTest.java @@ -0,0 +1,209 @@ +package org.owasp.esapi.crypto; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; + +import static org.junit.Assert.*; +import org.junit.BeforeClass; +import org.junit.Before; +import org.junit.Test; + +import junit.framework.JUnit4TestAdapter; + +import org.owasp.esapi.crypto.KeyDerivationFunction; +import org.owasp.esapi.crypto.CryptoHelper; +import org.owasp.esapi.errors.EncryptionException; + +public class KeyDerivationFunctionTest { + + private static SecretKey desKey; + private static SecretKey tdes2key; + private static SecretKey tdes3key; + private static SecretKey aes128key; + private static SecretKey aes192key; + private static SecretKey aes256key; + private static SecretKey shortKey; + + private KeyDerivationFunction kdfSha1; + private KeyDerivationFunction kdfSha256; + + @BeforeClass + public static void setupStatic() { + try { + desKey = CryptoHelper.generateSecretKey("DES", 56); + tdes2key = CryptoHelper.generateSecretKey("DESede", 112); + tdes3key = CryptoHelper.generateSecretKey("DESede", 168); + aes128key = CryptoHelper.generateSecretKey("AES", 128); + aes128key = CryptoHelper.generateSecretKey("AES", 128); + aes192key = CryptoHelper.generateSecretKey("AES", 192); + aes256key = CryptoHelper.generateSecretKey("AES", 256); + + shortKey = new SecretKeySpec(desKey.getEncoded(), 0, 5, "Blowfish"); // 40-bits. Blowfish has var key size + } catch (EncryptionException e) { + fail("Caught unexpected EncryptionException while generating keys; msg was " + + e.getMessage()); + } + } + + @Before + public void setup() { + kdfSha1 = new KeyDerivationFunction( KeyDerivationFunction.PRF_ALGORITHMS.HmacSHA1 ); + kdfSha256 = new KeyDerivationFunction( KeyDerivationFunction.PRF_ALGORITHMS.HmacSHA256 ); + } + + @Test(expected = EncryptionException.class) + public void testKeyTooShort() throws EncryptionException { + // System.out.println("testKeyTooShort"); + try { + SecretKey key = kdfSha1.computeDerivedKey( shortKey, 128, "encryption" ); + fail("testKeyTooShort: Expected IllegalArgumentException to be thrown."); + } catch ( NoSuchAlgorithmException | InvalidKeyException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testKeySizeTooShort() { + // System.out.println("testKeySizeTooShort"); + try { + SecretKey key = kdfSha1.computeDerivedKey( aes128key, 40, "encryption" ); // Min size is 56 bits + fail("testKeySizeTooShort: Expected IllegalArgumentException to be thrown."); + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testNullKey() { + // System.out.println("testNullKey"); + try { + SecretKey key = kdfSha1.computeDerivedKey( null, 56, "encryption" ); // Null key disallowed + assertTrue(key == null); // Not reached! + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testKeySizeNotEvenNumberOfBytes() { + // System.out.println("testKeySizeNotEvenNumberOfBytes"); + try { + SecretKey key = kdfSha1.computeDerivedKey( aes128key, 60, "encryption" ); // 60 % 8 == 4 + assertTrue(key == null); // Not reached! + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testPurposeNull() { + // System.out.println("testPurposeNull"); + try { + SecretKey key = kdfSha1.computeDerivedKey( aes128key, 128, null ); // purpose is null + assertTrue(key == null); // Not reached! + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testPurposeEmpty() { + // System.out.println("testPurposeEmpty"); + try { + SecretKey key = kdfSha1.computeDerivedKey( aes128key, 128, "" ); // purpose is empty string + assertTrue(key == null); // Not reached! + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test + public void testSunnyDay() { + // System.out.println("testSunnyDay"); + try { + SecretKey key1 = kdfSha1.computeDerivedKey( aes128key, 128, "encryption" ); + assertTrue(key1 != null); + assertTrue( key1.getEncoded().length == 128 / 8 ); + + SecretKey key2 = kdfSha1.computeDerivedKey( aes128key, 128, "authenticity" ); + assertTrue(key2 != null); + assertTrue( key2.getEncoded().length == 128 / 8 ); + + SecretKey key1b = kdfSha1.computeDerivedKey( aes128key, 128, "encryption" ); + + assertTrue( java.security.MessageDigest.isEqual( key1.getEncoded(), key1b.getEncoded() ) ); + assertFalse( java.security.MessageDigest.isEqual( key1.getEncoded(), key2.getEncoded() ) ); + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test + public void testSunnyDay2() { // Two sunny day tests in a row!? This inevitably will fail if run in Columbus, OH. + // System.out.println("testSunnyDay2"); + try { + SecretKey key1 = kdfSha256.computeDerivedKey( aes256key, 192, "Why am I here?" ); + assertTrue(key1 != null); + assertTrue( key1.getEncoded().length == 192 / 8 ); + + // Be honest. You thought I was goint to say "42", didn't you? + SecretKey key2 = kdfSha256.computeDerivedKey( aes256key, 192, "No doubt, to annoy people." ); + assertTrue(key2 != null); + assertTrue( key2.getEncoded().length == 192 / 8 ); + + // Should be different because different purpose given for each. + assertFalse( java.security.MessageDigest.isEqual( key1.getEncoded(), key2.getEncoded() ) ); + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test + public void testSetContext() { + // System.out.println("testSetContext"); + try { + SecretKey key1 = kdfSha256.computeDerivedKey( aes128key, 128, "encryption" ); + assertTrue(key1 != null); + assertTrue( key1.getEncoded().length == 128 / 8 ); + + SecretKey key2 = kdfSha1.computeDerivedKey( aes128key, 128, "encryption" ); + + // Should be false because one uses HmacSHA256 and the other uses HmacSHA1 + assertFalse( java.security.MessageDigest.isEqual( key1.getEncoded(), key2.getEncoded() ) ); + + kdfSha256.setContext( "plugh xyzzy" ); // Change context. Originally it is empty string. + SecretKey key1b = kdfSha256.computeDerivedKey( aes128key, 128, "encryption" ); + assertTrue(key1b != null); + assertTrue( key1b.getEncoded().length == 128 / 8 ); + + // Should be false because different contexts used. + assertFalse( java.security.MessageDigest.isEqual( key1.getEncoded(), key1b.getEncoded() ) ); + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testSetContextToNull() { + // System.out.println("testSetContextToNull"); + try { + SecretKey key1 = kdfSha256.computeDerivedKey( aes128key, 128, "encryption" ); + kdfSha256.setContext( null ); // Throws IllegalArgumentExeption + + fail("testSetContextToNull: Expected IllegalArgumentException to be thrown."); + } catch ( NoSuchAlgorithmException | InvalidKeyException | EncryptionException e ) { + fail("Caught unexpected exception " + e.getClass().getName() + ": exception msg: " + e); + } + } + + /** + * Run all the test cases in this suite. This is to allow running from + * {@code org.owasp.esapi.AllTests} which uses a JUnit 3 test runner. + */ + public static junit.framework.Test suite() { + // System.out.println("In suite()"); + return new JUnit4TestAdapter(KeyDerivationFunctionTest.class); + } +} From d86930362d703c790a18453882db1541c23f5c16 Mon Sep 17 00:00:00 2001 From: mzilu Date: Thu, 1 Aug 2019 20:06:53 -0500 Subject: [PATCH 019/544] Resolve #509 - Properly throw exception when HTML fails --- .../validation/HTMLValidationRule.java | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java index ef450f263..9d9f08065 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * This file is part of the Open Web Application Security Project (OWASP) * Enterprise Security API (ESAPI) project. For details, please see * http://www.owasp.org/index.php/ESAPI. * * Copyright (c) 2007 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Jeff Williams Aspect Security * @created 2007 */ @@ -35,18 +35,18 @@ /** * A validator performs syntax and possibly semantic validation of a single * piece of data from an untrusted source. - * + * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 * @see org.owasp.esapi.Validator */ public class HTMLValidationRule extends StringValidationRule { - + /** OWASP AntiSamy markup verification policy */ private static Policy antiSamyPolicy = null; - private static final Logger LOGGER = ESAPI.getLogger( "HTMLValidationRule" ); - + private static final Logger LOGGER = ESAPI.getLogger( "HTMLValidationRule" ); + static { InputStream resourceStream = null; try { @@ -66,7 +66,7 @@ public class HTMLValidationRule extends StringValidationRule { public HTMLValidationRule( String typeName ) { super( typeName ); } - + public HTMLValidationRule( String typeName, Encoder encoder ) { super( typeName, encoder ); } @@ -74,7 +74,7 @@ public HTMLValidationRule( String typeName, Encoder encoder ) { public HTMLValidationRule( String typeName, Encoder encoder, String whitelistPattern ) { super( typeName, encoder, whitelistPattern ); } - + /** * {@inheritDoc} */ @@ -82,7 +82,7 @@ public HTMLValidationRule( String typeName, Encoder encoder, String whitelistPat public String getValid( String context, String input ) throws ValidationException { return invokeAntiSamy( context, input ); } - + /** * {@inheritDoc} */ @@ -105,20 +105,27 @@ private String invokeAntiSamy( String context, String input ) throws ValidationE } throw new ValidationException( context + " is required", "AntiSamy validation error: context=" + context + ", input=" + input, context ); } - + String canonical = super.getValid( context, input ); try { AntiSamy as = new AntiSamy(); CleanResults test = as.scan(canonical, antiSamyPolicy); - + List errors = test.getErrorMessages(); if ( !errors.isEmpty() ) { - LOGGER.info( Logger.SECURITY_FAILURE, "Cleaned up invalid HTML input: " + errors ); + StringBuilder sb = new StringBuilder(); + for ( int i = 0; i < errors.size(); i++ ) { + sb.append(errors.get(i)); + if ( i != errors.size() - 1 ) { + sb.append(","); + } + } + throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + sb.toString()); } - + return test.getCleanHTML().trim(); - + } catch (ScanException e) { throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input: context=" + context + " error=" + e.getMessage(), e, context ); } catch (PolicyException e) { From f9564e9ab40c40b880dd1ad6abffefb3210cbe9d Mon Sep 17 00:00:00 2001 From: Michael Ziluck Date: Fri, 2 Aug 2019 11:24:06 +0000 Subject: [PATCH 020/544] Resolves #226 - Corrected docs for the bounded, numeric, random methods (#508) Added the documentation for the method referenced in #226 Also added documentation for getRandomReal(float,float) so it is not left ambiguous --- src/main/java/org/owasp/esapi/Randomizer.java | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Randomizer.java b/src/main/java/org/owasp/esapi/Randomizer.java index ae144b04f..e1a0b811f 100644 --- a/src/main/java/org/owasp/esapi/Randomizer.java +++ b/src/main/java/org/owasp/esapi/Randomizer.java @@ -1,15 +1,15 @@ /** * OWASP Enterprise Security API (ESAPI) - * + * * This file is part of the Open Web Application Security Project (OWASP) * Enterprise Security API (ESAPI) project. For details, please see * http://www.owasp.org/index.php/ESAPI. * * Copyright (c) 2007 - The OWASP Foundation - * + * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. - * + * * @author Jeff Williams Aspect Security * @created 2007 */ @@ -32,91 +32,91 @@ public interface Randomizer { /** * Gets a random string of a desired length and character set. The use of java.security.SecureRandom - * is recommended because it provides a cryptographically strong pseudo-random number generator. - * If SecureRandom is not used, the pseudo-random number generator used should comply with the + * is recommended because it provides a cryptographically strong pseudo-random number generator. + * If SecureRandom is not used, the pseudo-random number generator used should comply with the * statistical random number generator tests specified in * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. - * - * @param length + * + * @param length * the length of the string - * @param characterSet + * @param characterSet * the set of characters to include in the created random string - * - * @return + * + * @return * the random string of the desired length and character set */ String getRandomString(int length, char[] characterSet); /** * Returns a random boolean. The use of java.security.SecureRandom - * is recommended because it provides a cryptographically strong pseudo-random number generator. - * If SecureRandom is not used, the pseudo-random number generator used should comply with the + * is recommended because it provides a cryptographically strong pseudo-random number generator. + * If SecureRandom is not used, the pseudo-random number generator used should comply with the * statistical random number generator tests specified in * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. - * - * @return + * + * @return * true or false, randomly */ boolean getRandomBoolean(); - + /** - * Gets the random integer. The use of java.security.SecureRandom - * is recommended because it provides a cryptographically strong pseudo-random number generator. - * If SecureRandom is not used, the pseudo-random number generator used should comply with the + * Gets the random integer in the range of [min, max). The use of java.security.SecureRandom + * is recommended because it provides a cryptographically strong pseudo-random number generator. + * If SecureRandom is not used, the pseudo-random number generator used should comply with the * statistical random number generator tests specified in * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. - * - * @param min - * the minimum integer that will be returned - * @param max - * the maximum integer that will be returned - * - * @return + * + * @param min + * the minimum integer that will be returned, inclusive + * @param max + * the maximum integer that will be returned, exclusive + * + * @return * the random integer */ int getRandomInteger(int min, int max); - + /** * Gets the random long. The use of java.security.SecureRandom - * is recommended because it provides a cryptographically strong pseudo-random number generator. - * If SecureRandom is not used, the pseudo-random number generator used should comply with the + * is recommended because it provides a cryptographically strong pseudo-random number generator. + * If SecureRandom is not used, the pseudo-random number generator used should comply with the * statistical random number generator tests specified in * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. - * - * @return + * + * @return * the random long */ long getRandomLong(); - - + + /** * Returns an unguessable random filename with the specified extension. This method could call * getRandomString(length, charset) from this Class with the desired length and alphanumerics as the charset * then merely append "." + extension. - * - * @param extension + * + * @param extension * extension to add to the random filename - * - * @return + * + * @return * a random unguessable filename ending with the specified extension */ String getRandomFilename( String extension ); - - + + /** - * Gets the random real. The use of java.security.SecureRandom - * is recommended because it provides a cryptographically strong pseudo-random number generator. - * If SecureRandom is not used, the pseudo-random number generator used should comply with the + * Gets the random real in the range of [min, max]. The use of java.security.SecureRandom + * is recommended because it provides a cryptographically strong pseudo-random number generator. + * If SecureRandom is not used, the pseudo-random number generator used should comply with the * statistical random number generator tests specified in * FIPS 140-2, Security Requirements for Cryptographic Modules, section 4.9.1. - * - * @param min - * the minimum real number that will be returned - * @param max - * the maximum real number that will be returned - * - * @return + * + * @param min + * the minimum real number that will be returned, inclusive + * @param max + * the maximum real number that will be returned, inclusive + * + * @return * the random real */ float getRandomReal(float min, float max); @@ -129,19 +129,19 @@ public interface Randomizer { * For more information including algorithms used to create UUIDs, * see the Internet-Draft UUIDs and GUIDs * or the standards body definition at ISO/IEC 11578:1996. - * @return + * @return * the GUID - * - * @throws - * EncryptionException if hashing or encryption fails + * + * @throws + * EncryptionException if hashing or encryption fails */ String getRandomGUID() throws EncryptionException; - + /** * Generates a specified number of random bytes. * @param n The requested number of random bytes. * @return The {@code n} random bytes are returned. */ byte[] getRandomBytes(int n); - + } From 135950c75470faf0143cef6017f049cc1432e1b6 Mon Sep 17 00:00:00 2001 From: mzilu Date: Fri, 2 Aug 2019 08:33:38 -0500 Subject: [PATCH 021/544] Use existing toString method rather than a StringBuilder Lists will compile its contents when toString() is called against it, making the StringBuilder and iteration unnecessary. This change was made as a result of the review from GitHub PR #510 --- .../esapi/reference/validation/HTMLValidationRule.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java index 9d9f08065..f0196fc04 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java @@ -114,14 +114,7 @@ private String invokeAntiSamy( String context, String input ) throws ValidationE List errors = test.getErrorMessages(); if ( !errors.isEmpty() ) { - StringBuilder sb = new StringBuilder(); - for ( int i = 0; i < errors.size(); i++ ) { - sb.append(errors.get(i)); - if ( i != errors.size() - 1 ) { - sb.append(","); - } - } - throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + sb.toString()); + throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + errors.toString()); } return test.getCleanHTML().trim(); From b76f726ecfb001793e7bff3724a7cd8c93f633ed Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Wed, 14 Aug 2019 22:49:08 -0400 Subject: [PATCH 022/544] Update README.md to fix mailing list subscription links. Thanks and hat tip to Marc Anderson for noticing this. They were correct in the pom.xml, but broken here. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5371fccca..e8780a7a9 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Webchat http://webchat.freenode.net/ *Mailing lists:* As of 2019-03-25, ESAPI's 2 mailing lists were officially moved OFF of their Mailman mailing lists to a new home on Google Groups. -The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to after you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/forum/#!forum/esapi-project-users/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/forum/#!forum/esapi-project-dev/join)" respectively. +The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to _after_ you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-dev/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-users/join)" respectively. Old archives for the old Mailman mailing lists for ESAPI-Users and ESAPI-Dev are still available at https://lists.owasp.org/pipermail/esapi-users/ and https://lists.owasp.org/pipermail/esapi-dev/ respectively. From 694c97bcc1152b3aa2eaef5c130c05085ab0c7e9 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 26 Aug 2019 22:30:59 -0400 Subject: [PATCH 023/544] Close issue #512 by updating to 1.9.4 of Commons Beans Util. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ab3973fde..eeabf7401 100644 --- a/pom.xml +++ b/pom.xml @@ -171,8 +171,8 @@ commons-beanutils commons-beanutils - - 1.9.3 + + 1.9.4 From b25d166639e643ea60932e95c1645148f5c3ea10 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Tue, 10 Dec 2019 19:12:20 -0600 Subject: [PATCH 070/544] Cleanup Log4JLogFactory Removing cruft from the static block that was not required. --- .../esapi/logging/log4j/Log4JLogFactory.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java index 8d67c6ff9..7e9881941 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java @@ -14,17 +14,11 @@ */ package org.owasp.esapi.logging.log4j; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.log4j.LogManager; -import org.apache.log4j.PropertyConfigurator; import org.owasp.esapi.ESAPI; import org.owasp.esapi.LogFactory; import org.owasp.esapi.Logger; @@ -35,7 +29,6 @@ import org.owasp.esapi.logging.cleaning.CompositeLogScrubber; import org.owasp.esapi.logging.cleaning.LogScrubber; import org.owasp.esapi.logging.cleaning.NewlineLogScrubber; -import org.owasp.esapi.logging.java.JavaLogFactory; import org.owasp.esapi.reference.DefaultSecurityConfiguration; /** * LogFactory implementation which creates Log4J supporting Loggers. @@ -74,22 +67,6 @@ public class Log4JLogFactory implements LogFactory { //LEVEL.OFF not used. If it's off why would we try to log it? LOG_BRIDGE = new Log4JLogBridgeImpl(Log4J_LOG_APPENDER, Log4J_LOG_SCRUBBER, levelLookup); - - try (InputStream stream = Log4JLogFactory.class.getClassLoader(). - getResourceAsStream("log4j.xml")) { - PropertyConfigurator.configure(stream); - } catch (IOException ioe) { - System.err.print(new IOException("Failed to load log4j.xml.", ioe)); - } - - OutputStream nullOutputStream = new OutputStream() { - @Override - public void write(int b) throws IOException { - //No Op - } - }; - - System.setOut(new PrintStream(nullOutputStream)); } /** From 2d895849c35ededc119b9a425f8a671a832f8489 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Tue, 10 Dec 2019 19:13:16 -0600 Subject: [PATCH 071/544] Deprecating Log4J Implementation Staging implementation to be phased out in future releases. --- .../java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java | 1 + .../org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java | 1 + .../java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java | 1 + .../org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java | 1 + .../org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java | 1 + src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java | 3 ++- .../org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java | 1 + src/test/resources/esapi/ESAPI.properties | 4 ++-- 8 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java index d69662eba..c56585068 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java @@ -20,6 +20,7 @@ * Contract for translating an ESAPI log event into an Log4J log event. * */ +@Deprecated public interface Log4JLogBridge { /** * Translation for the provided ESAPI level, type, and message to the specified Log4J Logger. diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java index d982ead49..cb5e6f5ee 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java @@ -27,6 +27,7 @@ * Implementation which is intended to bridge the ESAPI Logging API into LOG4J supported Object structures. * */ +@Deprecated public class Log4JLogBridgeImpl implements Log4JLogBridge { /** Configuration providing associations between esapi log levels and LOG4J levels.*/ private final Map esapiSlfLevelMap; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java index 7e9881941..6db3b3ea5 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java @@ -34,6 +34,7 @@ * LogFactory implementation which creates Log4J supporting Loggers. * */ +@Deprecated public class Log4JLogFactory implements LogFactory { /** Immune characters for the codec log scrubber for JAVA context.*/ private static final char[] IMMUNE_LOG4J_HTML = {',', '.', '-', '_', ' ' }; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java index e73e717e1..4ac3ae8e0 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java @@ -23,6 +23,7 @@ * @see Log4JLogBridgeImpl * */ +@Deprecated interface Log4JLogLevelHandler { /** Check if the logging level is enabled for the specified logger.*/ boolean isEnabled(Logger logger); diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java index 19e54c696..7cb3f93b2 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java @@ -23,6 +23,7 @@ * Enumeration capturing the propagation of Log4J level events. * */ +@Deprecated public enum Log4JLogLevelHandlers implements Log4JLogLevelHandler { FATAL(Level.FATAL), ERROR(Level.ERROR), diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java index 9196abbd3..b83bfc6b9 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java @@ -16,8 +16,9 @@ import org.owasp.esapi.Logger; /** - * ESAPI Logger implementation which relays events to an SLF4J delegate. + * ESAPI Logger implementation which relays events to an Log4j delegate. */ +@Deprecated public class Log4JLogger implements org.owasp.esapi.Logger { /** Delegate Logger.*/ private final org.apache.log4j.Logger delegate; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java index a2b743edf..880fb8ea4 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java @@ -32,6 +32,7 @@ * @see org.owasp.esapi.logging.log4j.Log4JLogFactory * @see org.owasp.esapi.reference.Log4JLogger */ +@Deprecated public class Log4JLoggerFactory implements LoggerFactory { /** Log appender instance.*/ private static LogAppender LOG4J_LOG_APPENDER; diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 57dac9ecc..1437fae9a 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -97,8 +97,8 @@ ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector # Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html -#ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory -ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory +ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory +#ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory #ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory # To use the new SLF4J logger in ESAPI (see GitHub issue #129), set # ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory From 978e4092d9330c4461d949d567f4fd79ad4aa69e Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 07:43:29 -0600 Subject: [PATCH 072/544] License Updates: Fixed Created Date Updating copy/pasta mistake of created date. --- src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java | 2 +- .../java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java | 2 +- src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java | 2 +- .../java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java | 2 +- src/main/java/org/owasp/esapi/logging/java/JavaLogger.java | 2 +- src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java | 2 +- .../java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java | 2 +- .../java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java | 2 +- .../org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java | 2 +- .../org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java | 2 +- src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java | 2 +- .../org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java | 2 +- .../java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java | 2 +- .../org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java | 2 +- src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java | 2 +- .../org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java | 2 +- .../java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java | 2 +- .../owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java | 2 +- .../java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java index 7e25ee94c..3537f6b3c 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java index 9b3e32f6a..6f06c4a1e 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java index d640d6725..d5445133b 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java index 367e9e5e7..5dd181e35 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java index a028924ba..db41a5970 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java index c56585068..70234cec2 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java index cb5e6f5ee..f52610396 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java index 6db3b3ea5..7d7fccc07 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java index 4ac3ae8e0..fe2a7a28c 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java index 7cb3f93b2..7cbea76e9 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java index b83bfc6b9..cca51d73e 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java index 2989a71d6..05fdeb858 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java index efd5278dd..6ed916be1 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java index b42f790bd..e14edd874 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java index 4c5c57834..770bdd42d 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.java; diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java index 23138f56a..ab52bc6dc 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java index a983e1bc5..555a9750b 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java index 9f2cefbc7..972c2868d 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java index 36982fb0f..2e77efd58 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java @@ -10,7 +10,7 @@ * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @created 2018 + * @created 2019 */ package org.owasp.esapi.logging.log4j; From 0d79d985bd74ffc43ee944c540506e1267c3f8d5 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 07:50:45 -0600 Subject: [PATCH 073/544] File Rename JavaLoggerLevel -> ESAPIErrorJavaLevel Trying to update the name to be more representative to what it is. --- .../logging/java/ESAPIErrorJavaLevel.java | 44 +++++++++++++++++++ .../logging/java/JavaLogLevelHandlers.java | 2 +- .../esapi/logging/java/JavaLoggerLevel.java | 29 ------------ .../java/JavaLogLevelHandlersTest.java | 2 +- 4 files changed, 46 insertions(+), 31 deletions(-) create mode 100644 src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java delete mode 100644 src/main/java/org/owasp/esapi/logging/java/JavaLoggerLevel.java diff --git a/src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java b/src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java new file mode 100644 index 000000000..7e24b5d58 --- /dev/null +++ b/src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java @@ -0,0 +1,44 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @created 2019 + */ + +package org.owasp.esapi.logging.java; + +import java.util.logging.Level; + +/** + * A custom logging level defined between Level.SEVERE and Level.WARNING in logger. + */ +public class ESAPIErrorJavaLevel extends Level { + + protected static final long serialVersionUID = 1L; + + /** + * Defines a custom error level below SEVERE but above WARNING since this level isn't defined directly + * by java.util.Logger already. + */ + public static final Level ERROR_LEVEL = new ESAPIErrorJavaLevel( "ERROR", Level.SEVERE.intValue() - 1); + + /** + * Constructs an instance of a JavaLoggerLevel which essentially provides a mapping between the name of + * the defined level and its numeric value. + * + * @param name The name of the JavaLoggerLevel + * @param value The associated numeric value + */ + private ESAPIErrorJavaLevel(String name, int value) { + super(name, value); + } +} + \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java index 04ed82043..1ac832c1f 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java @@ -13,7 +13,7 @@ public enum JavaLogLevelHandlers implements JavaLogLevelHandler { FINER(Level.FINER), FINEST(Level.FINEST), ALL(Level.ALL), - ERROR(JavaLoggerLevel.ERROR_LEVEL); + ERROR(ESAPIErrorJavaLevel.ERROR_LEVEL); private final Level level; diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLoggerLevel.java b/src/main/java/org/owasp/esapi/logging/java/JavaLoggerLevel.java deleted file mode 100644 index 7d967f347..000000000 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLoggerLevel.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.owasp.esapi.logging.java; - -import java.util.logging.Level; - -/** - * A custom logging level defined between Level.SEVERE and Level.WARNING in logger. - */ -public class JavaLoggerLevel extends Level { - - protected static final long serialVersionUID = 1L; - - /** - * Defines a custom error level below SEVERE but above WARNING since this level isn't defined directly - * by java.util.Logger already. - */ - public static final Level ERROR_LEVEL = new JavaLoggerLevel( "ERROR", Level.SEVERE.intValue() - 1); - - /** - * Constructs an instance of a JavaLoggerLevel which essentially provides a mapping between the name of - * the defined level and its numeric value. - * - * @param name The name of the JavaLoggerLevel - * @param value The associated numeric value - */ - private JavaLoggerLevel(String name, int value) { - super(name, value); - } -} - \ No newline at end of file diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java index e14edd874..279e51faa 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java @@ -36,7 +36,7 @@ public void testErrorDelegation() { JavaLogLevelHandlers.ERROR.log(mockLogger, testName.getMethodName()); JavaLogLevelHandlers.ERROR.log(mockLogger, testName.getMethodName(), testException); - Level expectedJavaLevel = JavaLoggerLevel.ERROR_LEVEL; + Level expectedJavaLevel = ESAPIErrorJavaLevel.ERROR_LEVEL; Mockito.verify(mockLogger, Mockito.times(1)).isLoggable(expectedJavaLevel); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName()); From aa453c696ccf74c0fd2b34282608f88c17a5ed38 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 07:55:11 -0600 Subject: [PATCH 074/544] Documentation Update Updating class docs for implementation. --- .../esapi/logging/log4j/Log4JLoggerFactory.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java index 880fb8ea4..644180a63 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java @@ -23,14 +23,11 @@ import org.owasp.esapi.reference.DefaultSecurityConfiguration; /** - * Implementation of the LoggerFactory interface. This implementation has been - * overridden to return instances of org.owasp.esapi.reference.Log4JLogger. - * - * @author August Detlefsen (augustd at codemagi dot com) - * CodeMagi, Inc. - * @since October 15, 2010 - * @see org.owasp.esapi.logging.log4j.Log4JLogFactory - * @see org.owasp.esapi.reference.Log4JLogger + * Service Provider Interface implementation that can be provided as the org.apache.log4j.spi.LoggerFactory reference in a Log4J configuration. + *
+ * + * <loggerFactory class="org.owasp.esapi.logging.log4j.Log4JLoggerFactory"/> + * */ @Deprecated public class Log4JLoggerFactory implements LoggerFactory { From 99b46409aa6d9d29d3ac30d936c6babd91d1778b Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 07:56:34 -0600 Subject: [PATCH 075/544] Code Cleanup Removing commented content --- .../java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java index 644180a63..0d0595ef9 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java @@ -59,7 +59,6 @@ public Log4JLoggerFactory() {} * @return org.owasp.esapi.reference.Log4JLogger */ public org.apache.log4j.Logger makeNewLoggerInstance(String name) { - //return new org.owasp.esapi.reference.Log4JLogger(name); return new EsapiLog4JWrapper(name); } From 36b2b780528a51ee2226cba7c9691e9f1e2a33c9 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:00:49 -0600 Subject: [PATCH 076/544] Whitespace cleanup. Removing tabs. --- .../logging/appender/ClientInfoSupplier.java | 114 +++++++++--------- .../appender/EventTypeLogSupplier.java | 28 ++--- .../esapi/logging/appender/LogAppender.java | 16 +-- .../logging/appender/LogPrefixAppender.java | 74 ++++++------ .../logging/appender/ServerInfoSupplier.java | 96 +++++++-------- 5 files changed, 164 insertions(+), 164 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java index b21aa0629..f8cbaf719 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java @@ -28,66 +28,66 @@ * information. */ public class ClientInfoSupplier implements Supplier { - /** Default UserName string if the Authenticated user is null.*/ - private static final String DEFAULT_USERNAME = "#ANONYMOUS#"; - /** Default Last Host string if the Authenticated user is null.*/ - private static final String DEFAULT_LAST_HOST = "#UNKNOWN_HOST#"; - /** Session Attribute containing the ESAPI Session id. */ - private static final String ESAPI_SESSION_ATTR = "ESAPI_SESSION"; - /** - * Minimum value for generating a random session value if one is not defined. - */ - private static final int ESAPI_SESSION_RAND_MIN = 0; - /** - * Maximum value for generating a random session value if one is not defined. - */ - private static final int ESAPI_SESSION_RAND_MAX = 1000000; + /** Default UserName string if the Authenticated user is null.*/ + private static final String DEFAULT_USERNAME = "#ANONYMOUS#"; + /** Default Last Host string if the Authenticated user is null.*/ + private static final String DEFAULT_LAST_HOST = "#UNKNOWN_HOST#"; + /** Session Attribute containing the ESAPI Session id. */ + private static final String ESAPI_SESSION_ATTR = "ESAPI_SESSION"; + /** + * Minimum value for generating a random session value if one is not defined. + */ + private static final int ESAPI_SESSION_RAND_MIN = 0; + /** + * Maximum value for generating a random session value if one is not defined. + */ + private static final int ESAPI_SESSION_RAND_MAX = 1000000; - /** Format for supplier output. */ - private static final String USER_INFO_FORMAT = "%s:%s@%s"; // USER_NAME, SID, USER_HOST_ADDRESS + /** Format for supplier output. */ + private static final String USER_INFO_FORMAT = "%s:%s@%s"; // USER_NAME, SID, USER_HOST_ADDRESS - /** Whether to log the user info from this instance. */ - private boolean logUserInfo = true; + /** Whether to log the user info from this instance. */ + private boolean logUserInfo = true; - @Override - public String get() { - String userInfo = ""; - - if (logUserInfo) { - HttpServletRequest request = ESAPI.currentRequest(); - // create a random session number for the user to represent the user's - // 'session', if it doesn't exist already - String sid = ""; - if (request != null) { - HttpSession session = request.getSession(false); - if (session != null) { - sid = (String) session.getAttribute(ESAPI_SESSION_ATTR); - // if there is no session ID for the user yet, we create one and store it in the - // user's session - if (sid == null) { - sid = "" + ESAPI.randomizer().getRandomInteger(ESAPI_SESSION_RAND_MIN, ESAPI_SESSION_RAND_MAX); - session.setAttribute(ESAPI_SESSION_ATTR, sid); - } - } - } - // log user information - username:session@ipaddr - User user = ESAPI.authenticator().getCurrentUser(); - if (user == null) { - userInfo = String.format(USER_INFO_FORMAT, DEFAULT_USERNAME, sid, DEFAULT_LAST_HOST); - } else { - userInfo = String.format(USER_INFO_FORMAT, user.getAccountName(), sid, user.getLastHostAddress()); - } - } - return userInfo; - } + @Override + public String get() { + String userInfo = ""; - /** - * Specify whether the instance should record the client info. - * - * @param log {@code true} to record - */ - public void setLogUserInfo(boolean log) { - this.logUserInfo = log; - } + if (logUserInfo) { + HttpServletRequest request = ESAPI.currentRequest(); + // create a random session number for the user to represent the user's + // 'session', if it doesn't exist already + String sid = ""; + if (request != null) { + HttpSession session = request.getSession(false); + if (session != null) { + sid = (String) session.getAttribute(ESAPI_SESSION_ATTR); + // if there is no session ID for the user yet, we create one and store it in the + // user's session + if (sid == null) { + sid = "" + ESAPI.randomizer().getRandomInteger(ESAPI_SESSION_RAND_MIN, ESAPI_SESSION_RAND_MAX); + session.setAttribute(ESAPI_SESSION_ATTR, sid); + } + } + } + // log user information - username:session@ipaddr + User user = ESAPI.authenticator().getCurrentUser(); + if (user == null) { + userInfo = String.format(USER_INFO_FORMAT, DEFAULT_USERNAME, sid, DEFAULT_LAST_HOST); + } else { + userInfo = String.format(USER_INFO_FORMAT, user.getAccountName(), sid, user.getLastHostAddress()); + } + } + return userInfo; + } + + /** + * Specify whether the instance should record the client info. + * + * @param log {@code true} to record + */ + public void setLogUserInfo(boolean log) { + this.logUserInfo = log; + } } diff --git a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java index 2780550ec..447c7a6e5 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java @@ -25,20 +25,20 @@ * */ public class EventTypeLogSupplier implements Supplier { - /** EventType reference to supply log representation of. */ - private final EventType eventType; + /** EventType reference to supply log representation of. */ + private final EventType eventType; - /** - * Ctr - * - * @param evtyp EventType reference to supply log representation for - */ - public EventTypeLogSupplier(EventType evtyp) { - this.eventType = evtyp; - } + /** + * Ctr + * + * @param evtyp EventType reference to supply log representation for + */ + public EventTypeLogSupplier(EventType evtyp) { + this.eventType = evtyp; + } - @Override - public String get() { - return eventType == null ? "" : eventType.toString(); - } + @Override + public String get() { + return eventType == null ? "" : eventType.toString(); + } } diff --git a/src/main/java/org/owasp/esapi/logging/appender/LogAppender.java b/src/main/java/org/owasp/esapi/logging/appender/LogAppender.java index cc4917049..b3aba0534 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/LogAppender.java +++ b/src/main/java/org/owasp/esapi/logging/appender/LogAppender.java @@ -23,13 +23,13 @@ */ public interface LogAppender { - /** - * Creates a replacement Log Message and returns it to the caller. - * @param logName name of the logger. - * @param eventType EventType of the log event being processed. - * @param message The original message. - * @return Updated replacement message. - */ - public String appendTo(String logName, EventType eventType, String message); + /** + * Creates a replacement Log Message and returns it to the caller. + * @param logName name of the logger. + * @param eventType EventType of the log event being processed. + * @param message The original message. + * @return Updated replacement message. + */ + public String appendTo(String logName, EventType eventType, String message); } diff --git a/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java b/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java index a31492eb6..7c24b0bcf 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java +++ b/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java @@ -22,48 +22,48 @@ * EventType, Client data, and server data. */ public class LogPrefixAppender implements LogAppender { - /** Output format used to assemble return values. */ - private static final String RESULT_FORMAT = "[%s %s -> %s] %s";// EVENT_TYPE, CLIENT_INFO, SERVER_INFO, messageBody + /** Output format used to assemble return values. */ + private static final String RESULT_FORMAT = "[%s %s -> %s] %s";// EVENT_TYPE, CLIENT_INFO, SERVER_INFO, messageBody - /** Whether or not to record client information. */ - private final boolean logClientInfo; - /** Whether or not to record server ip information. */ - private final boolean logServerIp; - /** Whether or not to record application name. */ - private final boolean logApplicationName; - /** Application Name to record. */ - private final String appName; + /** Whether or not to record client information. */ + private final boolean logClientInfo; + /** Whether or not to record server ip information. */ + private final boolean logServerIp; + /** Whether or not to record application name. */ + private final boolean logApplicationName; + /** Application Name to record. */ + private final String appName; - /** - * Ctr. - * - * @param logClientInfo Whether or not to record client information - * @param logServerIp Whether or not to record server ip information - * @param logApplicationName Whether or not to record application name - * @param appName Application Name to record. - */ - public LogPrefixAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { - this.logClientInfo = logClientInfo; - this.logServerIp = logServerIp; - this.logApplicationName = logApplicationName; - this.appName = appName; - } + /** + * Ctr. + * + * @param logClientInfo Whether or not to record client information + * @param logServerIp Whether or not to record server ip information + * @param logApplicationName Whether or not to record application name + * @param appName Application Name to record. + */ + public LogPrefixAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { + this.logClientInfo = logClientInfo; + this.logServerIp = logServerIp; + this.logApplicationName = logApplicationName; + this.appName = appName; + } - @Override - public String appendTo(String logName, EventType eventType, String message) { - EventTypeLogSupplier eventTypeSupplier = new EventTypeLogSupplier(eventType); + @Override + public String appendTo(String logName, EventType eventType, String message) { + EventTypeLogSupplier eventTypeSupplier = new EventTypeLogSupplier(eventType); - ClientInfoSupplier clientInfoSupplier = new ClientInfoSupplier(); - clientInfoSupplier.setLogUserInfo(logClientInfo); + ClientInfoSupplier clientInfoSupplier = new ClientInfoSupplier(); + clientInfoSupplier.setLogUserInfo(logClientInfo); - ServerInfoSupplier serverInfoSupplier = new ServerInfoSupplier(logName); - serverInfoSupplier.setLogServerIp(logServerIp); - serverInfoSupplier.setLogApplicationName(logApplicationName, appName); + ServerInfoSupplier serverInfoSupplier = new ServerInfoSupplier(logName); + serverInfoSupplier.setLogServerIp(logServerIp); + serverInfoSupplier.setLogApplicationName(logApplicationName, appName); - String eventTypeMsg = eventTypeSupplier.get(); - String clientInfoMsg = clientInfoSupplier.get(); - String serverInfoMsg = serverInfoSupplier.get(); + String eventTypeMsg = eventTypeSupplier.get(); + String clientInfoMsg = clientInfoSupplier.get(); + String serverInfoMsg = serverInfoSupplier.get(); - return String.format(RESULT_FORMAT, eventTypeMsg, clientInfoMsg, serverInfoMsg, message); - } + return String.format(RESULT_FORMAT, eventTypeMsg, clientInfoMsg, serverInfoMsg, message); + } } diff --git a/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java index 8d0d49dd4..ca9b4bbd8 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java @@ -26,58 +26,58 @@ * information. */ public class ServerInfoSupplier implements Supplier { - /** Whether to log the server connection info. */ - private boolean logServerIP = true; - /** Whether to log the application name. */ - private boolean logAppName = true; - /** The application name to log. */ - private String applicationName = ""; + /** Whether to log the server connection info. */ + private boolean logServerIP = true; + /** Whether to log the application name. */ + private boolean logAppName = true; + /** The application name to log. */ + private String applicationName = ""; - /** Reference to the associated logname/module name. */ - private final String logName; + /** Reference to the associated logname/module name. */ + private final String logName; - /** - * Ctr. - * - * @param logName Reference to the logName to record as the module information - */ - public ServerInfoSupplier(String logName) { - this.logName = logName; - } + /** + * Ctr. + * + * @param logName Reference to the logName to record as the module information + */ + public ServerInfoSupplier(String logName) { + this.logName = logName; + } - @Override - public String get() { - // log server, port, app name, module name -- server:80/app/module - StringBuilder appInfo = new StringBuilder(); - HttpServletRequest request = ESAPI.currentRequest(); - if (request != null && logServerIP) { - appInfo.append(request.getLocalAddr()).append(":").append(request.getLocalPort()); - } - if (logAppName) { - appInfo.append("/").append(applicationName); - } - appInfo.append("/").append(logName); + @Override + public String get() { + // log server, port, app name, module name -- server:80/app/module + StringBuilder appInfo = new StringBuilder(); + HttpServletRequest request = ESAPI.currentRequest(); + if (request != null && logServerIP) { + appInfo.append(request.getLocalAddr()).append(":").append(request.getLocalPort()); + } + if (logAppName) { + appInfo.append("/").append(applicationName); + } + appInfo.append("/").append(logName); - return appInfo.toString(); - } + return appInfo.toString(); + } - /** - * Specify whether the instance should record the server connection info. - * - * @param log {@code true} to record - */ - public void setLogServerIp(boolean log) { - this.logServerIP = log; - } + /** + * Specify whether the instance should record the server connection info. + * + * @param log {@code true} to record + */ + public void setLogServerIp(boolean log) { + this.logServerIP = log; + } - /** - * Specify whether the instance should record the application name - * - * @param log {@code true} to record - * @param appName String to record as the application name - */ - public void setLogApplicationName(boolean log, String appName) { - this.logAppName = log; - this.applicationName = appName; - } + /** + * Specify whether the instance should record the application name + * + * @param log {@code true} to record + * @param appName String to record as the application name + */ + public void setLogApplicationName(boolean log, String appName) { + this.logAppName = log; + this.applicationName = appName; + } } From b10a9196373621c83de2d183064718cc29931326 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:02:46 -0600 Subject: [PATCH 077/544] Adding License Info --- .../esapi/logging/java/JavaLogLevelHandlers.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java index 1ac832c1f..4b223d176 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandlers.java @@ -1,3 +1,17 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @created 2019 + */ package org.owasp.esapi.logging.java; import java.util.logging.Level; From 19d8251c9a6dbd92d3e7583bc28616714284acb7 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:03:29 -0600 Subject: [PATCH 078/544] Whitespace cleanup --- .../logging/java/ESAPIErrorJavaLevel.java | 31 ++++++------ .../esapi/logging/java/JavaLogBridge.java | 2 +- .../esapi/logging/java/JavaLogBridgeImpl.java | 8 ++-- .../esapi/logging/java/JavaLogFactory.java | 48 +++++++++---------- .../logging/java/JavaLogLevelHandler.java | 4 +- .../owasp/esapi/logging/java/JavaLogger.java | 38 +++++++-------- 6 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java b/src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java index 7e24b5d58..7f4c595aa 100644 --- a/src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java +++ b/src/main/java/org/owasp/esapi/logging/java/ESAPIErrorJavaLevel.java @@ -25,20 +25,19 @@ public class ESAPIErrorJavaLevel extends Level { protected static final long serialVersionUID = 1L; /** - * Defines a custom error level below SEVERE but above WARNING since this level isn't defined directly - * by java.util.Logger already. - */ - public static final Level ERROR_LEVEL = new ESAPIErrorJavaLevel( "ERROR", Level.SEVERE.intValue() - 1); - - /** - * Constructs an instance of a JavaLoggerLevel which essentially provides a mapping between the name of - * the defined level and its numeric value. - * - * @param name The name of the JavaLoggerLevel - * @param value The associated numeric value - */ - private ESAPIErrorJavaLevel(String name, int value) { - super(name, value); - } + * Defines a custom error level below SEVERE but above WARNING since this level isn't defined directly + * by java.util.Logger already. + */ + public static final Level ERROR_LEVEL = new ESAPIErrorJavaLevel( "ERROR", Level.SEVERE.intValue() - 1); + + /** + * Constructs an instance of a JavaLoggerLevel which essentially provides a mapping between the name of + * the defined level and its numeric value. + * + * @param name The name of the JavaLoggerLevel + * @param value The associated numeric value + */ + private ESAPIErrorJavaLevel(String name, int value) { + super(name, value); + } } - \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java index 3537f6b3c..c085766dc 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridge.java @@ -40,5 +40,5 @@ public interface JavaLogBridge { * @param throwable ESAPI event Throwable content */ void log(Logger logger, int esapiLevel, EventType type, String message, Throwable throwable) ; - + } diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java index 6f06c4a1e..df599eaf1 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogBridgeImpl.java @@ -34,7 +34,7 @@ public class JavaLogBridgeImpl implements JavaLogBridge { private final LogScrubber scrubber; /** Appender used for assembling default message content for all logs.*/ private final LogAppender appender; - + /** * Constructor. * @param logScrubber Log message cleaner. @@ -53,9 +53,9 @@ public void log(Logger logger, int esapiLevel, EventType type, String message) { throw new IllegalArgumentException("Unable to lookup Java level mapping for esapi value of " + esapiLevel); } if (handler.isEnabled(logger)) { - String fullMessage = appender.appendTo(logger.getName(), type, message); + String fullMessage = appender.appendTo(logger.getName(), type, message); String cleanString = scrubber.cleanMessage(fullMessage); - + handler.log(logger, cleanString); } } @@ -66,7 +66,7 @@ public void log(Logger logger, int esapiLevel, EventType type, String message, T throw new IllegalArgumentException("Unable to lookup Java level mapping for esapi value of " + esapiLevel); } if (handler.isEnabled(logger)) { - String fullMessage = appender.appendTo(logger.getName(), type, message); + String fullMessage = appender.appendTo(logger.getName(), type, message); String cleanString = scrubber.cleanMessage(fullMessage); handler.log(logger, cleanString, throwable); diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java index d5445133b..a83e8c1de 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java @@ -48,17 +48,17 @@ public class JavaLogFactory implements LogFactory { private static LogScrubber JAVA_LOG_SCRUBBER; /** Bridge class for mapping esapi -> java log levels.*/ private static JavaLogBridge LOG_BRIDGE; - + static { boolean encodeLog = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_ENCODING_REQUIRED); JAVA_LOG_SCRUBBER = createLogScrubber(encodeLog); - - boolean logClientInfo = true; - boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); - String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); - boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); + + boolean logClientInfo = true; + boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); + String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); + boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); JAVA_LOG_APPENDER = createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); - + Map levelLookup = new HashMap<>(); levelLookup.put(Logger.ALL, JavaLogLevelHandlers.ALL); levelLookup.put(Logger.TRACE, JavaLogLevelHandlers.FINEST); @@ -68,28 +68,28 @@ public class JavaLogFactory implements LogFactory { levelLookup.put(Logger.WARNING, JavaLogLevelHandlers.WARNING); levelLookup.put(Logger.FATAL, JavaLogLevelHandlers.SEVERE); //LEVEL.OFF not used. If it's off why would we try to log it? - + LOG_BRIDGE = new JavaLogBridgeImpl(JAVA_LOG_APPENDER, JAVA_LOG_SCRUBBER, levelLookup); - + readLoggerConfiguration(LogManager.getLogManager()); } - + /** * Attempts to load the expected property file path into the provided LogManager reference. * @param logManager LogManager which is being configured. */ /*package*/ static void readLoggerConfiguration(LogManager logManager) { - /* + /* * This will load the logging properties file to control the format of the output for Java logs. */ try (InputStream stream = JavaLogFactory.class.getClassLoader(). - getResourceAsStream("esapi-java-logging.properties")) { - logManager.readConfiguration(stream); + getResourceAsStream("esapi-java-logging.properties")) { + logManager.readConfiguration(stream); } catch (IOException ioe) { - System.err.print(new IOException("Failed to load esapi-java-logging.properties.", ioe)); + System.err.print(new IOException("Failed to load esapi-java-logging.properties.", ioe)); } } - + /** * Populates the default log scrubber for use in factory-created loggers. * @param requiresEncoding {@code true} if encoding is required for log content. @@ -98,15 +98,15 @@ public class JavaLogFactory implements LogFactory { /*package*/ static LogScrubber createLogScrubber(boolean requiresEncoding) { List messageScrubber = new ArrayList<>(); messageScrubber.add(new NewlineLogScrubber()); - + if (requiresEncoding) { messageScrubber.add(new CodecLogScrubber(HTML_CODEC, IMMUNE_JAVA_HTML)); } - + return new CompositeLogScrubber(messageScrubber); - + } - + /** * Populates the default log appender for use in factory-created loggers. * @param appName @@ -117,19 +117,19 @@ public class JavaLogFactory implements LogFactory { * @return LogAppender instance. */ /*package*/ static LogAppender createLogAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { - return new LogPrefixAppender(logClientInfo, logServerIp, logApplicationName, appName); + return new LogPrefixAppender(logClientInfo, logServerIp, logApplicationName, appName); } - - + + @Override public Logger getLogger(String moduleName) { - java.util.logging.Logger javaLogger = java.util.logging.Logger.getLogger(moduleName); + java.util.logging.Logger javaLogger = java.util.logging.Logger.getLogger(moduleName); return new JavaLogger(javaLogger, LOG_BRIDGE, Logger.ALL); } @Override public Logger getLogger(@SuppressWarnings("rawtypes") Class clazz) { - java.util.logging.Logger javaLogger = java.util.logging.Logger.getLogger(clazz.getName()); + java.util.logging.Logger javaLogger = java.util.logging.Logger.getLogger(clazz.getName()); return new JavaLogger(javaLogger, LOG_BRIDGE, Logger.ALL); } diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java index 5dd181e35..ee28a1a4f 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogLevelHandler.java @@ -23,8 +23,8 @@ * @see JavaLogBridgeImpl * */ - interface JavaLogLevelHandler { - /** Check if the logging level is enabled for the specified logger.*/ +interface JavaLogLevelHandler { + /** Check if the logging level is enabled for the specified logger.*/ boolean isEnabled(Logger logger); /** * Calls the appropriate log level event on the specified logger. diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java index db41a5970..889d5f0bb 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogger.java @@ -25,7 +25,7 @@ public class JavaLogger implements org.owasp.esapi.Logger { private final JavaLogBridge logBridge; /** Maximum log level that will be forwarded to Java from the ESAPI context.*/ private int maxLogLevel; - + /** * Constructs a new instance. * @param JavaLogger Delegate Java logger. @@ -37,25 +37,25 @@ public JavaLogger(java.util.logging.Logger JavaLogger, JavaLogBridge bridge, int this.logBridge = bridge; maxLogLevel = defaultEsapiLevel; } - + private void log(int esapiLevel, EventType type, String message) { if (isEnabled(esapiLevel)) { logBridge.log(delegate, esapiLevel, type, message); } } - + private void log(int esapiLevel, EventType type, String message, Throwable throwable) { if (isEnabled(esapiLevel)) { logBridge.log(delegate, esapiLevel, type, message, throwable); } } - + private boolean isEnabled(int esapiLevel) { //Are Logger.OFF and Logger.ALL reversed? This should be simply the less than or equal to check... return (esapiLevel <= maxLogLevel && maxLogLevel != Logger.OFF) || maxLogLevel == Logger.ALL; } - + @Override public void always(EventType type, String message) { log (Logger.ALL, type, message); @@ -70,7 +70,7 @@ public void always(EventType type, String message, Throwable throwable) { public void trace(EventType type, String message) { log (Logger.TRACE, type, message); } - + @Override public void trace(EventType type, String message, Throwable throwable) { log (Logger.TRACE, type, message, throwable); @@ -85,22 +85,22 @@ public void debug(EventType type, String message) { public void debug(EventType type, String message, Throwable throwable) { log (Logger.DEBUG, type, message, throwable); } - + @Override public void info(EventType type, String message) { log (Logger.INFO, type, message); } - + @Override public void info(EventType type, String message, Throwable throwable) { log (Logger.INFO, type, message, throwable); } - + @Override public void warning(EventType type, String message) { log (Logger.WARNING, type, message); } - + @Override public void warning(EventType type, String message, Throwable throwable) { log (Logger.WARNING, type, message, throwable); @@ -125,17 +125,17 @@ public void fatal(EventType type, String message) { public void fatal(EventType type, String message, Throwable throwable) { log (Logger.FATAL, type, message, throwable); } - + @Override public int getESAPILevel() { return maxLogLevel; } - + @Override public boolean isTraceEnabled() { return isEnabled(Logger.TRACE); } - + @Override public boolean isDebugEnabled() { return isEnabled(Logger.DEBUG); @@ -148,21 +148,21 @@ public boolean isInfoEnabled() { public boolean isWarningEnabled() { return isEnabled(Logger.WARNING); } - + @Override public boolean isErrorEnabled() { return isEnabled(Logger.ERROR); } - + @Override public boolean isFatalEnabled() { - return isEnabled(Logger.FATAL); + return isEnabled(Logger.FATAL); } - + @Override public void setLevel(int level) { - maxLogLevel = level; + maxLogLevel = level; } - + } From 55f5002624a82178073bdd2fe30974dbe3b68203 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:04:32 -0600 Subject: [PATCH 079/544] Whitespace cleanup --- .../esapi/logging/log4j/Log4JLogBridge.java | 2 +- .../logging/log4j/Log4JLogBridgeImpl.java | 8 +- .../esapi/logging/log4j/Log4JLogFactory.java | 38 ++++----- .../logging/log4j/Log4JLogLevelHandler.java | 4 +- .../logging/log4j/Log4JLogLevelHandlers.java | 56 ++++++------- .../esapi/logging/log4j/Log4JLogger.java | 38 ++++----- .../logging/log4j/Log4JLoggerFactory.java | 80 +++++++++---------- 7 files changed, 113 insertions(+), 113 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java index 70234cec2..b89acb81e 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridge.java @@ -39,5 +39,5 @@ public interface Log4JLogBridge { * @param throwable ESAPI event Throwable content */ void log(Logger logger, int esapiLevel, EventType type, String message, Throwable throwable) ; - + } diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java index f52610396..58be15beb 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImpl.java @@ -35,7 +35,7 @@ public class Log4JLogBridgeImpl implements Log4JLogBridge { private final LogScrubber scrubber; /** Appender used for assembling default message content for all logs.*/ private final LogAppender appender; - + /** * Constructor. * @param logScrubber Log message cleaner. @@ -54,9 +54,9 @@ public void log(Logger logger, int esapiLevel, EventType type, String message) { throw new IllegalArgumentException("Unable to lookup LOG4J level mapping for esapi value of " + esapiLevel); } if (handler.isEnabled(logger)) { - String fullMessage = appender.appendTo(logger.getName(), type, message); + String fullMessage = appender.appendTo(logger.getName(), type, message); String cleanString = scrubber.cleanMessage(fullMessage); - + handler.log(logger, cleanString); } } @@ -67,7 +67,7 @@ public void log(Logger logger, int esapiLevel, EventType type, String message, T throw new IllegalArgumentException("Unable to lookup LOG4J level mapping for esapi value of " + esapiLevel); } if (handler.isEnabled(logger)) { - String fullMessage = appender.appendTo(logger.getName(), type, message); + String fullMessage = appender.appendTo(logger.getName(), type, message); String cleanString = scrubber.cleanMessage(fullMessage); handler.log(logger, cleanString, throwable); diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java index 7d7fccc07..fcf42f350 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java @@ -36,7 +36,7 @@ */ @Deprecated public class Log4JLogFactory implements LogFactory { - /** Immune characters for the codec log scrubber for JAVA context.*/ + /** Immune characters for the codec log scrubber for JAVA context.*/ private static final char[] IMMUNE_LOG4J_HTML = {',', '.', '-', '_', ' ' }; /** Codec being used to clean messages for logging.*/ private static final HTMLEntityCodec HTML_CODEC = new HTMLEntityCodec(); @@ -46,17 +46,17 @@ public class Log4JLogFactory implements LogFactory { private static LogScrubber Log4J_LOG_SCRUBBER; /** Bridge class for mapping esapi -> log4j log levels.*/ private static Log4JLogBridge LOG_BRIDGE; - + static { boolean encodeLog = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_ENCODING_REQUIRED); Log4J_LOG_SCRUBBER = createLogScrubber(encodeLog); - - boolean logClientInfo = true; - boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); - String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); - boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); + + boolean logClientInfo = true; + boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); + String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); + boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); Log4J_LOG_APPENDER = createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); - + Map levelLookup = new HashMap<>(); levelLookup.put(Logger.ALL, Log4JLogLevelHandlers.TRACE); levelLookup.put(Logger.TRACE, Log4JLogLevelHandlers.TRACE); @@ -66,10 +66,10 @@ public class Log4JLogFactory implements LogFactory { levelLookup.put(Logger.WARNING, Log4JLogLevelHandlers.WARN); levelLookup.put(Logger.FATAL, Log4JLogLevelHandlers.FATAL); //LEVEL.OFF not used. If it's off why would we try to log it? - + LOG_BRIDGE = new Log4JLogBridgeImpl(Log4J_LOG_APPENDER, Log4J_LOG_SCRUBBER, levelLookup); } - + /** * Populates the default log scrubber for use in factory-created loggers. * @param requiresEncoding {@code true} if encoding is required for log content. @@ -78,15 +78,15 @@ public class Log4JLogFactory implements LogFactory { /*package*/ static LogScrubber createLogScrubber(boolean requiresEncoding) { List messageScrubber = new ArrayList<>(); messageScrubber.add(new NewlineLogScrubber()); - + if (requiresEncoding) { messageScrubber.add(new CodecLogScrubber(HTML_CODEC, IMMUNE_LOG4J_HTML)); } - + return new CompositeLogScrubber(messageScrubber); - + } - + /** * Populates the default log appender for use in factory-created loggers. * @param appName @@ -97,19 +97,19 @@ public class Log4JLogFactory implements LogFactory { * @return LogAppender instance. */ /*package*/ static LogAppender createLogAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { - return new LogPrefixAppender(logClientInfo, logServerIp, logApplicationName, appName); + return new LogPrefixAppender(logClientInfo, logServerIp, logApplicationName, appName); } - - + + @Override public Logger getLogger(String moduleName) { - org.apache.log4j.Logger log4JLogger = org.apache.log4j.Logger.getLogger(moduleName); + org.apache.log4j.Logger log4JLogger = org.apache.log4j.Logger.getLogger(moduleName); return new Log4JLogger(log4JLogger, LOG_BRIDGE, Logger.ALL); } @Override public Logger getLogger(@SuppressWarnings("rawtypes") Class clazz) { - org.apache.log4j.Logger log4JLogger = org.apache.log4j.Logger.getLogger(clazz); + org.apache.log4j.Logger log4JLogger = org.apache.log4j.Logger.getLogger(clazz); return new Log4JLogger(log4JLogger, LOG_BRIDGE, Logger.ALL); } diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java index fe2a7a28c..7b6b57ee9 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandler.java @@ -24,8 +24,8 @@ * */ @Deprecated - interface Log4JLogLevelHandler { - /** Check if the logging level is enabled for the specified logger.*/ +interface Log4JLogLevelHandler { + /** Check if the logging level is enabled for the specified logger.*/ boolean isEnabled(Logger logger); /** * Calls the appropriate log level event on the specified logger. diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java index 7cbea76e9..e6ee6a48c 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlers.java @@ -25,32 +25,32 @@ */ @Deprecated public enum Log4JLogLevelHandlers implements Log4JLogLevelHandler { - FATAL(Level.FATAL), - ERROR(Level.ERROR), - WARN(Level.WARN), - INFO(Level.INFO), - DEBUG(Level.DEBUG), - TRACE(Level.TRACE), - ALL(Level.ALL); - - private final Level level; - - private Log4JLogLevelHandlers(Level lvl) { - this.level = lvl; - } - - @Override - public boolean isEnabled(Logger logger) { - return logger.isEnabledFor(level); - } - - @Override - public void log(Logger logger, String msg) { - logger.log(level, msg); - } - - @Override - public void log(Logger logger, String msg, Throwable th) { - logger.log(level, msg, th); - } + FATAL(Level.FATAL), + ERROR(Level.ERROR), + WARN(Level.WARN), + INFO(Level.INFO), + DEBUG(Level.DEBUG), + TRACE(Level.TRACE), + ALL(Level.ALL); + + private final Level level; + + private Log4JLogLevelHandlers(Level lvl) { + this.level = lvl; + } + + @Override + public boolean isEnabled(Logger logger) { + return logger.isEnabledFor(level); + } + + @Override + public void log(Logger logger, String msg) { + logger.log(level, msg); + } + + @Override + public void log(Logger logger, String msg, Throwable th) { + logger.log(level, msg, th); + } } diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java index cca51d73e..b24a7448c 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogger.java @@ -26,7 +26,7 @@ public class Log4JLogger implements org.owasp.esapi.Logger { private final Log4JLogBridge logBridge; /** Maximum log level that will be forwarded to SLF4J from the ESAPI context.*/ private int maxLogLevel; - + /** * Constructs a new instance. * @param slf4JLogger Delegate SLF4J logger. @@ -38,25 +38,25 @@ public Log4JLogger(org.apache.log4j.Logger slf4JLogger, Log4JLogBridge bridge, i this.logBridge = bridge; maxLogLevel = defaultEsapiLevel; } - + private void log(int esapiLevel, EventType type, String message) { if (isEnabled(esapiLevel)) { logBridge.log(delegate, esapiLevel, type, message); } } - + private void log(int esapiLevel, EventType type, String message, Throwable throwable) { if (isEnabled(esapiLevel)) { logBridge.log(delegate, esapiLevel, type, message, throwable); } } - + private boolean isEnabled(int esapiLevel) { //Are Logger.OFF and Logger.ALL reversed? This should be simply the less than or equal to check... return (esapiLevel <= maxLogLevel && maxLogLevel != Logger.OFF) || maxLogLevel == Logger.ALL; } - + @Override public void always(EventType type, String message) { log (Logger.ALL, type, message); @@ -71,7 +71,7 @@ public void always(EventType type, String message, Throwable throwable) { public void trace(EventType type, String message) { log (Logger.TRACE, type, message); } - + @Override public void trace(EventType type, String message, Throwable throwable) { log (Logger.TRACE, type, message, throwable); @@ -86,22 +86,22 @@ public void debug(EventType type, String message) { public void debug(EventType type, String message, Throwable throwable) { log (Logger.DEBUG, type, message, throwable); } - + @Override public void info(EventType type, String message) { log (Logger.INFO, type, message); } - + @Override public void info(EventType type, String message, Throwable throwable) { log (Logger.INFO, type, message, throwable); } - + @Override public void warning(EventType type, String message) { log (Logger.WARNING, type, message); } - + @Override public void warning(EventType type, String message, Throwable throwable) { log (Logger.WARNING, type, message, throwable); @@ -126,17 +126,17 @@ public void fatal(EventType type, String message) { public void fatal(EventType type, String message, Throwable throwable) { log (Logger.FATAL, type, message, throwable); } - + @Override public int getESAPILevel() { return maxLogLevel; } - + @Override public boolean isTraceEnabled() { return isEnabled(Logger.TRACE); } - + @Override public boolean isDebugEnabled() { return isEnabled(Logger.DEBUG); @@ -149,21 +149,21 @@ public boolean isInfoEnabled() { public boolean isWarningEnabled() { return isEnabled(Logger.WARNING); } - + @Override public boolean isErrorEnabled() { return isEnabled(Logger.ERROR); } - + @Override public boolean isFatalEnabled() { - return isEnabled(Logger.FATAL); + return isEnabled(Logger.FATAL); } - + @Override public void setLevel(int level) { - maxLogLevel = level; + maxLogLevel = level; } - + } diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java index 0d0595ef9..5123e20b5 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java @@ -31,53 +31,53 @@ */ @Deprecated public class Log4JLoggerFactory implements LoggerFactory { - /** Log appender instance.*/ + /** Log appender instance.*/ private static LogAppender LOG4J_LOG_APPENDER; /** Log cleaner instance.*/ private static LogScrubber LOG4J_LOG_SCRUBBER; - + static { boolean encodeLog = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_ENCODING_REQUIRED); LOG4J_LOG_SCRUBBER = Log4JLogFactory.createLogScrubber(encodeLog); - - boolean logClientInfo = true; - boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); - String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); - boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); - LOG4J_LOG_APPENDER = Log4JLogFactory.createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); + + boolean logClientInfo = true; + boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); + String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); + boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); + LOG4J_LOG_APPENDER = Log4JLogFactory.createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); + } + + /** + * This constructor must be public so it can be accessed from within log4j + */ + public Log4JLoggerFactory() {} + + /** + * Overridden to return instances of org.owasp.esapi.reference.Log4JLogger. + * + * @param name The class name to return a logger for. + * @return org.owasp.esapi.reference.Log4JLogger + */ + public org.apache.log4j.Logger makeNewLoggerInstance(String name) { + return new EsapiLog4JWrapper(name); } - - /** - * This constructor must be public so it can be accessed from within log4j - */ - public Log4JLoggerFactory() {} - /** - * Overridden to return instances of org.owasp.esapi.reference.Log4JLogger. - * - * @param name The class name to return a logger for. - * @return org.owasp.esapi.reference.Log4JLogger - */ - public org.apache.log4j.Logger makeNewLoggerInstance(String name) { - return new EsapiLog4JWrapper(name); - } - - - public static class EsapiLog4JWrapper extends org.apache.log4j.Logger { - protected EsapiLog4JWrapper(String name) { - super(name); - } - - @Override - protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) { - String toClean = message.toString(); - - String fullMessage = LOG4J_LOG_APPENDER.appendTo(getName(), null, toClean); - String cleanMsg = LOG4J_LOG_SCRUBBER.cleanMessage(fullMessage); - - super.forcedLog(fqcn, level, cleanMsg, t); - } - - } + public static class EsapiLog4JWrapper extends org.apache.log4j.Logger { + + protected EsapiLog4JWrapper(String name) { + super(name); + } + + @Override + protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) { + String toClean = message.toString(); + + String fullMessage = LOG4J_LOG_APPENDER.appendTo(getName(), null, toClean); + String cleanMsg = LOG4J_LOG_SCRUBBER.cleanMessage(fullMessage); + + super.forcedLog(fqcn, level, cleanMsg, t); + } + + } } From 38af35bb2d99bd40ff9d128077fa8e2faddeb1a1 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:05:41 -0600 Subject: [PATCH 080/544] Whitespace cleanup --- .../logging/java/JavaLogBridgeImplTest.java | 26 ++-- .../logging/java/JavaLogFactoryTest.java | 136 +++++++++--------- .../java/JavaLogLevelHandlersTest.java | 10 +- .../esapi/logging/java/JavaLoggerTest.java | 92 ++++++------ 4 files changed, 132 insertions(+), 132 deletions(-) diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java index 05fdeb858..3b7e4ebc5 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogBridgeImplTest.java @@ -48,7 +48,7 @@ public class JavaLogBridgeImplTest { public void setup() { Map levelLookup = new HashMap<>(); levelLookup.put(Logger.ALL, mockHandler); - + java.util.logging.Logger wrappedLogger = java.util.logging.Logger.getLogger(testName.getMethodName()); javaLogSpy = Mockito.spy(wrappedLogger); bridge = new JavaLogBridgeImpl(mockAppender, mockScrubber, levelLookup); @@ -61,7 +61,7 @@ public void testLogMessageWithUnmappedEsapiLevelThrowsException() { Map emptyMap = Collections.emptyMap(); new JavaLogBridgeImpl(mockAppender, mockScrubber, emptyMap).log(javaLogSpy, 0, Logger.EVENT_UNSPECIFIED, "This Should fail"); } - + @Test public void testLogMessageAndExceptionWithUnmappedEsapiLevelThrowsException() { exEx.expect(IllegalArgumentException.class); @@ -69,13 +69,13 @@ public void testLogMessageAndExceptionWithUnmappedEsapiLevelThrowsException() { Map emptyMap = Collections.emptyMap(); new JavaLogBridgeImpl(mockAppender, mockScrubber, emptyMap).log(javaLogSpy, 0, Logger.EVENT_UNSPECIFIED, "This Should fail", testEx); } - + @Test public void testLogMessage() { - EventType eventType = Logger.EVENT_UNSPECIFIED; - String loggerName = testName.getMethodName() + "-LOGGER"; - String orignMsg = testName.getMethodName(); - String appendMsg = "[APPEND] " + orignMsg; + EventType eventType = Logger.EVENT_UNSPECIFIED; + String loggerName = testName.getMethodName() + "-LOGGER"; + String orignMsg = testName.getMethodName(); + String appendMsg = "[APPEND] " + orignMsg; String cleanMsg = appendMsg + " [CLEANED]"; //Setup for Appender @@ -94,16 +94,16 @@ public void testLogMessage() { Mockito.verify(mockHandler, Mockito.times(1)).isEnabled(javaLogSpy); Mockito.verify(mockHandler, Mockito.times(0)).log(ArgumentMatchers.any(java.util.logging.Logger.class), ArgumentMatchers.any(String.class), ArgumentMatchers.any(Throwable.class)); Mockito.verify(mockHandler, Mockito.times(1)).log(ArgumentMatchers.same(javaLogSpy), ArgumentMatchers.eq(cleanMsg)); - + Mockito.verifyNoMoreInteractions(javaLogSpy, mockAppender, mockScrubber,mockHandler); } @Test public void testLogErrorMessageWithException() { - EventType eventType = Logger.EVENT_UNSPECIFIED; - String loggerName = testName.getMethodName() + "-LOGGER"; - String orignMsg = testName.getMethodName(); - String appendMsg = "[APPEND] " + orignMsg; + EventType eventType = Logger.EVENT_UNSPECIFIED; + String loggerName = testName.getMethodName() + "-LOGGER"; + String orignMsg = testName.getMethodName(); + String appendMsg = "[APPEND] " + orignMsg; String cleanMsg = appendMsg + " [CLEANED]"; //Setup for Appender @@ -123,7 +123,7 @@ public void testLogErrorMessageWithException() { Mockito.verify(mockHandler, Mockito.times(0)).log(ArgumentMatchers.any(java.util.logging.Logger.class), ArgumentMatchers.any(String.class)); Mockito.verify(mockHandler, Mockito.times(1)).log(ArgumentMatchers.same(javaLogSpy), ArgumentMatchers.eq(cleanMsg), ArgumentMatchers.same(testEx)); - + Mockito.verifyNoMoreInteractions(javaLogSpy, mockAppender, mockScrubber,mockHandler); } diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java index 6ed916be1..52e38b566 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java @@ -45,109 +45,109 @@ @RunWith(PowerMockRunner.class) @PrepareForTest (JavaLogFactory.class) public class JavaLogFactoryTest { - @Rule + @Rule public TestName testName = new TestName(); - @Test - public void testIOExceptionOnMissingConfiguration() throws Exception { - final IOException originException = new IOException(testName.getMethodName()); - - LogManager testLogManager = new LogManager() { - @Override - public void readConfiguration(InputStream ins) throws IOException, SecurityException { - throw originException; - } - }; - - OutputStream nullOutputStream = new OutputStream() { - @Override - public void write(int b) throws IOException { - //No Op - } - }; - - ArgumentCaptor stdErrOut = ArgumentCaptor.forClass(Object.class); - PrintStream orig = System.err; - try (PrintStream errPrinter = new PrintStream(nullOutputStream)) { - PrintStream spyPrinter = PowerMockito.spy(errPrinter); - Mockito.doCallRealMethod().when(spyPrinter).print(stdErrOut.capture()); - System.setErr(spyPrinter); - - JavaLogFactory.readLoggerConfiguration(testLogManager); - - Object writeData = stdErrOut.getValue(); - assertTrue(writeData instanceof IOException); - IOException actual = (IOException) writeData; - assertEquals(originException, actual.getCause()); - assertEquals("Failed to load esapi-java-logging.properties.", actual.getMessage()); - } finally { - System.setErr(orig); - } - - } + @Test + public void testIOExceptionOnMissingConfiguration() throws Exception { + final IOException originException = new IOException(testName.getMethodName()); + + LogManager testLogManager = new LogManager() { + @Override + public void readConfiguration(InputStream ins) throws IOException, SecurityException { + throw originException; + } + }; + + OutputStream nullOutputStream = new OutputStream() { + @Override + public void write(int b) throws IOException { + //No Op + } + }; + + ArgumentCaptor stdErrOut = ArgumentCaptor.forClass(Object.class); + PrintStream orig = System.err; + try (PrintStream errPrinter = new PrintStream(nullOutputStream)) { + PrintStream spyPrinter = PowerMockito.spy(errPrinter); + Mockito.doCallRealMethod().when(spyPrinter).print(stdErrOut.capture()); + System.setErr(spyPrinter); + + JavaLogFactory.readLoggerConfiguration(testLogManager); + + Object writeData = stdErrOut.getValue(); + assertTrue(writeData instanceof IOException); + IOException actual = (IOException) writeData; + assertEquals(originException, actual.getCause()); + assertEquals("Failed to load esapi-java-logging.properties.", actual.getMessage()); + } finally { + System.setErr(orig); + } + + } @Test public void testCreateLoggerByString() { Logger logger = new JavaLogFactory().getLogger("test"); Assert.assertTrue(logger instanceof JavaLogger); } - + @Test public void testCreateLoggerByClass() { Logger logger = new JavaLogFactory().getLogger(JavaLogBridgeImplTest.class); Assert.assertTrue(logger instanceof JavaLogger); } - + @Test public void checkScrubberWithEncoding() throws Exception { ArgumentCaptor delegates = ArgumentCaptor.forClass(List.class); PowerMockito.whenNew(CompositeLogScrubber.class).withArguments(delegates.capture()).thenReturn(null); - + //Call to invoke the constructor capture JavaLogFactory.createLogScrubber(true); - + List scrubbers = delegates.getValue(); Assert.assertEquals(2, scrubbers.size()); Assert.assertTrue(scrubbers.get(0) instanceof NewlineLogScrubber); Assert.assertTrue(scrubbers.get(1) instanceof CodecLogScrubber); } - + @Test public void checkScrubberWithoutEncoding() throws Exception { ArgumentCaptor delegates = ArgumentCaptor.forClass(List.class); PowerMockito.whenNew(CompositeLogScrubber.class).withArguments(delegates.capture()).thenReturn(null); - + //Call to invoke the constructor capture JavaLogFactory.createLogScrubber(false); - + List scrubbers = delegates.getValue(); Assert.assertEquals(1, scrubbers.size()); Assert.assertTrue(scrubbers.get(0) instanceof NewlineLogScrubber); } - + /** - * At this time there are no special considerations or handling for the appender - * creation in this scope. It is expected that the arguments to the internal - * creation method are passed directly to the constructor of the - * LogPrefixAppender with no mutation or additional validation. - */ + * At this time there are no special considerations or handling for the appender + * creation in this scope. It is expected that the arguments to the internal + * creation method are passed directly to the constructor of the + * LogPrefixAppender with no mutation or additional validation. + */ @Test public void checkPassthroughAppenderConstruct() throws Exception { - LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, ""); - ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); - - PowerMockito.whenNew(LogPrefixAppender.class).withArguments(clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); - - LogAppender appender = JavaLogFactory.createLogAppender(true, false, true, testName.getMethodName()); - - Assert.assertEquals(stubAppender, appender); - Assert.assertTrue(clientInfoCapture.getValue()); - Assert.assertFalse(serverInfoCapture.getValue()); - Assert.assertTrue(logAppNameCapture.getValue()); - Assert.assertEquals(testName.getMethodName(), appNameCapture.getValue()); + LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, ""); + ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); + + PowerMockito.whenNew(LogPrefixAppender.class).withArguments(clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); + + LogAppender appender = JavaLogFactory.createLogAppender(true, false, true, testName.getMethodName()); + + Assert.assertEquals(stubAppender, appender); + Assert.assertTrue(clientInfoCapture.getValue()); + Assert.assertFalse(serverInfoCapture.getValue()); + Assert.assertTrue(logAppNameCapture.getValue()); + Assert.assertEquals(testName.getMethodName(), appNameCapture.getValue()); } - - + + } diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java index 279e51faa..a5a7c201f 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogLevelHandlersTest.java @@ -37,7 +37,7 @@ public void testErrorDelegation() { JavaLogLevelHandlers.ERROR.log(mockLogger, testName.getMethodName(), testException); Level expectedJavaLevel = ESAPIErrorJavaLevel.ERROR_LEVEL; - + Mockito.verify(mockLogger, Mockito.times(1)).isLoggable(expectedJavaLevel); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName()); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName(), testException); @@ -51,7 +51,7 @@ public void testWarnDelegation() { JavaLogLevelHandlers.WARNING.log(mockLogger, testName.getMethodName(), testException); Level expectedJavaLevel = Level.WARNING; - + Mockito.verify(mockLogger, Mockito.times(1)).isLoggable(expectedJavaLevel); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName()); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName(), testException); @@ -64,7 +64,7 @@ public void testInfoDelegation() { JavaLogLevelHandlers.INFO.log(mockLogger, testName.getMethodName(), testException); Level expectedJavaLevel = Level.INFO; - + Mockito.verify(mockLogger, Mockito.times(1)).isLoggable(expectedJavaLevel); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName()); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName(), testException); @@ -78,7 +78,7 @@ public void testDebugDelegation() { JavaLogLevelHandlers.FINE.log(mockLogger, testName.getMethodName(), testException); Level expectedJavaLevel = Level.FINE; - + Mockito.verify(mockLogger, Mockito.times(1)).isLoggable(expectedJavaLevel); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName()); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName(), testException); @@ -92,7 +92,7 @@ public void testTraceDelegation() { JavaLogLevelHandlers.FINEST.log(mockLogger, testName.getMethodName(), testException); Level expectedJavaLevel = Level.FINEST; - + Mockito.verify(mockLogger, Mockito.times(1)).isLoggable(expectedJavaLevel); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName()); Mockito.verify(mockLogger, Mockito.times(1)).log(expectedJavaLevel, testName.getMethodName(), testException); diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java index 770bdd42d..2d9c24347 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLoggerTest.java @@ -24,42 +24,42 @@ import org.owasp.esapi.Logger; public class JavaLoggerTest { - @Rule + @Rule public TestName testName = new TestName(); - + private static final String MSG = JavaLoggerTest.class.getSimpleName(); - + private JavaLogBridge mockBridge = Mockito.mock(JavaLogBridge.class); private java.util.logging.Logger javaLogSpy; - + private Throwable testEx = new Throwable(MSG + "_Exception"); private Logger testLogger; @Before public void setup() { - java.util.logging.Logger wrappedLogger = java.util.logging.Logger.getLogger(testName.getMethodName()); - javaLogSpy = Mockito.spy(wrappedLogger); - testLogger = new JavaLogger(javaLogSpy, mockBridge, Logger.ALL); + java.util.logging.Logger wrappedLogger = java.util.logging.Logger.getLogger(testName.getMethodName()); + javaLogSpy = Mockito.spy(wrappedLogger); + testLogger = new JavaLogger(javaLogSpy, mockBridge, Logger.ALL); } - + @Test public void testLevelEnablement() { testLogger.setLevel(Logger.INFO); - + Assert.assertFalse(testLogger.isFatalEnabled()); Assert.assertFalse(testLogger.isErrorEnabled()); Assert.assertFalse(testLogger.isWarningEnabled()); Assert.assertTrue(testLogger.isInfoEnabled()); Assert.assertTrue(testLogger.isDebugEnabled()); Assert.assertTrue(testLogger.isTraceEnabled()); - + Assert.assertEquals(Logger.INFO, testLogger.getESAPILevel()); } - + @Test public void testAllLevelEnablement() { testLogger.setLevel(Logger.ALL); - + Assert.assertTrue(testLogger.isFatalEnabled()); Assert.assertTrue(testLogger.isErrorEnabled()); Assert.assertTrue(testLogger.isWarningEnabled()); @@ -71,7 +71,7 @@ public void testAllLevelEnablement() { @Test public void testOffLevelEnablement() { testLogger.setLevel(Logger.OFF); - + Assert.assertFalse(testLogger.isFatalEnabled()); Assert.assertFalse(testLogger.isErrorEnabled()); Assert.assertFalse(testLogger.isWarningEnabled()); @@ -82,23 +82,23 @@ public void testOffLevelEnablement() { @Test public void testFatalWithMessage() { testLogger.fatal(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @Test public void testFatalWithMessageAndThrowable() { testLogger.fatal(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testFatalWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.fatal(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @@ -109,27 +109,27 @@ public void testFatalWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testErrorWithMessage() { testLogger.error(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @Test public void testErrorWithMessageAndThrowable() { testLogger.error(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testErrorWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.error(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @@ -140,27 +140,27 @@ public void testErrorWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testWarnWithMessage() { testLogger.warning(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @Test public void testWarnWithMessageAndThrowable() { testLogger.warning(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testWarnWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.warning(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @@ -171,27 +171,27 @@ public void testWarnWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testInfoWithMessage() { testLogger.info(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.INFO, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @Test public void testInfoWithMessageAndThrowable() { testLogger.info(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.INFO, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testInfoWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.info(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.INFO, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @@ -205,23 +205,23 @@ public void testInfoWithMessageAndThrowableDisabled() { @Test public void testDebugWithMessage() { testLogger.debug(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @Test public void testDebugWithMessageAndThrowable() { testLogger.debug(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testDebugWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.debug(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @@ -232,27 +232,27 @@ public void testDebugWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testTraceWithMessage() { testLogger.trace(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @Test public void testTraceWithMessageAndThrowable() { testLogger.trace(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testTraceWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.trace(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @@ -263,27 +263,27 @@ public void testTraceWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testAlwaysWithMessage() { testLogger.always(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.ALL, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } @Test public void testAlwaysWithMessageAndThrowable() { testLogger.always(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(javaLogSpy, Logger.ALL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } - + @Test public void testAlwaysWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.always(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(javaLogSpy, Logger.ALL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, javaLogSpy); } From 5454c14bfb2b526fc9025aad419a896859dcfd84 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:06:17 -0600 Subject: [PATCH 081/544] Whitespace cleanup --- .../logging/log4j/Log4JLogBridgeImplTest.java | 26 +++--- .../logging/log4j/Log4JLogFactoryTest.java | 64 +++++++-------- .../log4j/Log4JLogLevelHandlersTest.java | 2 +- .../esapi/logging/log4j/Log4JLoggerTest.java | 82 +++++++++---------- 4 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java index ab52bc6dc..f3bf41582 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogBridgeImplTest.java @@ -48,7 +48,7 @@ public class Log4JLogBridgeImplTest { public void setup() { Map levelLookup = new HashMap<>(); levelLookup.put(Logger.ALL, mockHandler); - + org.apache.log4j.Logger wrappedLogger =org.apache.log4j.Logger.getLogger(testName.getMethodName()); log4JSpy = Mockito.spy(wrappedLogger); bridge = new Log4JLogBridgeImpl(mockAppender, mockScrubber, levelLookup); @@ -61,7 +61,7 @@ public void testLogMessageWithUnmappedEsapiLevelThrowsException() { Map emptyMap = Collections.emptyMap(); new Log4JLogBridgeImpl(mockAppender, mockScrubber, emptyMap).log(log4JSpy, 0, Logger.EVENT_UNSPECIFIED, "This Should fail"); } - + @Test public void testLogMessageAndExceptionWithUnmappedEsapiLevelThrowsException() { exEx.expect(IllegalArgumentException.class); @@ -69,13 +69,13 @@ public void testLogMessageAndExceptionWithUnmappedEsapiLevelThrowsException() { Map emptyMap = Collections.emptyMap(); new Log4JLogBridgeImpl(mockAppender, mockScrubber, emptyMap).log(log4JSpy, 0, Logger.EVENT_UNSPECIFIED, "This Should fail", testEx); } - + @Test public void testLogMessage() { - EventType eventType = Logger.EVENT_UNSPECIFIED; - String loggerName = testName.getMethodName(); - String orignMsg = testName.getMethodName(); - String appendMsg = "[APPEND] " + orignMsg; + EventType eventType = Logger.EVENT_UNSPECIFIED; + String loggerName = testName.getMethodName(); + String orignMsg = testName.getMethodName(); + String appendMsg = "[APPEND] " + orignMsg; String cleanMsg = appendMsg + " [CLEANED]"; //Setup for Appender @@ -92,16 +92,16 @@ public void testLogMessage() { Mockito.verify(mockHandler, Mockito.times(1)).isEnabled(log4JSpy); Mockito.verify(mockHandler, Mockito.times(0)).log(ArgumentMatchers.any(org.apache.log4j.Logger.class), ArgumentMatchers.any(String.class), ArgumentMatchers.any(Throwable.class)); Mockito.verify(mockHandler, Mockito.times(1)).log(ArgumentMatchers.same(log4JSpy), ArgumentMatchers.eq(cleanMsg)); - + Mockito.verifyNoMoreInteractions(log4JSpy, mockAppender, mockScrubber,mockHandler); } @Test public void testLogErrorMessageWithException() { - EventType eventType = Logger.EVENT_UNSPECIFIED; - String loggerName = testName.getMethodName(); - String orignMsg = testName.getMethodName(); - String appendMsg = "[APPEND] " + orignMsg; + EventType eventType = Logger.EVENT_UNSPECIFIED; + String loggerName = testName.getMethodName(); + String orignMsg = testName.getMethodName(); + String appendMsg = "[APPEND] " + orignMsg; String cleanMsg = appendMsg + " [CLEANED]"; //Setup for Appender @@ -119,7 +119,7 @@ public void testLogErrorMessageWithException() { Mockito.verify(mockHandler, Mockito.times(0)).log(ArgumentMatchers.any(org.apache.log4j.Logger.class), ArgumentMatchers.any(String.class)); Mockito.verify(mockHandler, Mockito.times(1)).log(ArgumentMatchers.same(log4JSpy), ArgumentMatchers.eq(cleanMsg), ArgumentMatchers.same(testEx)); - + Mockito.verifyNoMoreInteractions(log4JSpy, mockAppender, mockScrubber,mockHandler); } diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java index 555a9750b..6b9f55379 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java @@ -38,71 +38,71 @@ @RunWith(PowerMockRunner.class) @PrepareForTest (Log4JLogFactory.class) public class Log4JLogFactoryTest { - @Rule + @Rule public TestName testName = new TestName(); - + @Test public void testCreateLoggerByString() { Logger logger = new Log4JLogFactory().getLogger("test"); Assert.assertTrue(logger instanceof Log4JLogger); } - + @Test public void testCreateLoggerByClass() { Logger logger = new Log4JLogFactory().getLogger(Log4JLogBridgeImplTest.class); Assert.assertTrue(logger instanceof Log4JLogger); } - + @Test public void checkScrubberWithEncoding() throws Exception { ArgumentCaptor delegates = ArgumentCaptor.forClass(List.class); PowerMockito.whenNew(CompositeLogScrubber.class).withArguments(delegates.capture()).thenReturn(null); - + //Call to invoke the constructor capture Log4JLogFactory.createLogScrubber(true); - + List scrubbers = delegates.getValue(); Assert.assertEquals(2, scrubbers.size()); Assert.assertTrue(scrubbers.get(0) instanceof NewlineLogScrubber); Assert.assertTrue(scrubbers.get(1) instanceof CodecLogScrubber); } - + @Test public void checkScrubberWithoutEncoding() throws Exception { ArgumentCaptor delegates = ArgumentCaptor.forClass(List.class); PowerMockito.whenNew(CompositeLogScrubber.class).withArguments(delegates.capture()).thenReturn(null); - + //Call to invoke the constructor capture Log4JLogFactory.createLogScrubber(false); - + List scrubbers = delegates.getValue(); Assert.assertEquals(1, scrubbers.size()); Assert.assertTrue(scrubbers.get(0) instanceof NewlineLogScrubber); } - + /** - * At this time there are no special considerations or handling for the appender - * creation in this scope. It is expected that the arguments to the internal - * creation method are passed directly to the constructor of the - * LogPrefixAppender with no mutation or additional validation. - */ + * At this time there are no special considerations or handling for the appender + * creation in this scope. It is expected that the arguments to the internal + * creation method are passed directly to the constructor of the + * LogPrefixAppender with no mutation or additional validation. + */ @Test public void checkPassthroughAppenderConstruct() throws Exception { - LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, ""); - ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); - - PowerMockito.whenNew(LogPrefixAppender.class).withArguments(clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); - - LogAppender appender = Log4JLogFactory.createLogAppender(true, false, true, testName.getMethodName()); - - Assert.assertEquals(stubAppender, appender); - Assert.assertTrue(clientInfoCapture.getValue()); - Assert.assertFalse(serverInfoCapture.getValue()); - Assert.assertTrue(logAppNameCapture.getValue()); - Assert.assertEquals(testName.getMethodName(), appNameCapture.getValue()); + LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, ""); + ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); + + PowerMockito.whenNew(LogPrefixAppender.class).withArguments(clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); + + LogAppender appender = Log4JLogFactory.createLogAppender(true, false, true, testName.getMethodName()); + + Assert.assertEquals(stubAppender, appender); + Assert.assertTrue(clientInfoCapture.getValue()); + Assert.assertFalse(serverInfoCapture.getValue()); + Assert.assertTrue(logAppNameCapture.getValue()); + Assert.assertEquals(testName.getMethodName(), appNameCapture.getValue()); } - - + + } diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java index 972c2868d..149617f91 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogLevelHandlersTest.java @@ -42,7 +42,7 @@ public void testFatalDelegation() { Mockito.verifyNoMoreInteractions(mockLogger); } - + @Test public void testErrorDelegation() { Log4JLogLevelHandlers.ERROR.isEnabled(mockLogger); diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java index 2e77efd58..ebea57ac2 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerTest.java @@ -23,33 +23,33 @@ import org.owasp.esapi.logging.log4j.Log4JLogger; public class Log4JLoggerTest { - + private static final String MSG = Log4JLoggerTest.class.getSimpleName(); - + private Log4JLogBridge mockBridge = Mockito.mock(Log4JLogBridge.class); private org.apache.log4j.Logger mockLogDelegate = Mockito.mock(org.apache.log4j.Logger.class); - + private Throwable testEx = new Throwable(MSG + "_Exception"); private Logger testLogger = new Log4JLogger(mockLogDelegate, mockBridge, Logger.ALL); @Test public void testLevelEnablement() { testLogger.setLevel(Logger.INFO); - + Assert.assertFalse(testLogger.isFatalEnabled()); Assert.assertFalse(testLogger.isErrorEnabled()); Assert.assertFalse(testLogger.isWarningEnabled()); Assert.assertTrue(testLogger.isInfoEnabled()); Assert.assertTrue(testLogger.isDebugEnabled()); Assert.assertTrue(testLogger.isTraceEnabled()); - + Assert.assertEquals(Logger.INFO, testLogger.getESAPILevel()); } - + @Test public void testAllLevelEnablement() { testLogger.setLevel(Logger.ALL); - + Assert.assertTrue(testLogger.isFatalEnabled()); Assert.assertTrue(testLogger.isErrorEnabled()); Assert.assertTrue(testLogger.isWarningEnabled()); @@ -61,7 +61,7 @@ public void testAllLevelEnablement() { @Test public void testOffLevelEnablement() { testLogger.setLevel(Logger.OFF); - + Assert.assertFalse(testLogger.isFatalEnabled()); Assert.assertFalse(testLogger.isErrorEnabled()); Assert.assertFalse(testLogger.isWarningEnabled()); @@ -72,23 +72,23 @@ public void testOffLevelEnablement() { @Test public void testFatalWithMessage() { testLogger.fatal(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @Test public void testFatalWithMessageAndThrowable() { testLogger.fatal(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testFatalWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.fatal(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @@ -99,27 +99,27 @@ public void testFatalWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.FATAL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testErrorWithMessage() { testLogger.error(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @Test public void testErrorWithMessageAndThrowable() { testLogger.error(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testErrorWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.error(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @@ -130,27 +130,27 @@ public void testErrorWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.ERROR, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testWarnWithMessage() { testLogger.warning(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @Test public void testWarnWithMessageAndThrowable() { testLogger.warning(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testWarnWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.warning(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @@ -161,27 +161,27 @@ public void testWarnWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.WARNING, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testInfoWithMessage() { testLogger.info(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.INFO, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @Test public void testInfoWithMessageAndThrowable() { testLogger.info(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.INFO, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testInfoWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.info(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.INFO, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @@ -195,23 +195,23 @@ public void testInfoWithMessageAndThrowableDisabled() { @Test public void testDebugWithMessage() { testLogger.debug(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @Test public void testDebugWithMessageAndThrowable() { testLogger.debug(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testDebugWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.debug(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @@ -222,27 +222,27 @@ public void testDebugWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.DEBUG, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testTraceWithMessage() { testLogger.trace(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @Test public void testTraceWithMessageAndThrowable() { testLogger.trace(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testTraceWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.trace(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @@ -253,27 +253,27 @@ public void testTraceWithMessageAndThrowableDisabled() { Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.TRACE, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testAlwaysWithMessage() { testLogger.always(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.ALL, Logger.EVENT_UNSPECIFIED, MSG); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } @Test public void testAlwaysWithMessageAndThrowable() { testLogger.always(Logger.EVENT_UNSPECIFIED, MSG, testEx); - + Mockito.verify(mockBridge, Mockito.times(1)).log(mockLogDelegate, Logger.ALL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } - + @Test public void testAlwaysWithMessageDisabled() { testLogger.setLevel(Logger.OFF); testLogger.always(Logger.EVENT_UNSPECIFIED, MSG); - + Mockito.verify(mockBridge, Mockito.times(0)).log(mockLogDelegate, Logger.ALL, Logger.EVENT_UNSPECIFIED, MSG, testEx); Mockito.verifyNoMoreInteractions(mockBridge, mockLogDelegate); } From a904dab581bf61dbbf1ea2202f481b937cb9e1b5 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:10:05 -0600 Subject: [PATCH 082/544] Log4J SPI (Test-Scope) Uncommenting xml configuration for SPI LoggerFactory --- src/test/resources/log4j.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/resources/log4j.xml b/src/test/resources/log4j.xml index 0abc8dd4c..70351b47d 100644 --- a/src/test/resources/log4j.xml +++ b/src/test/resources/log4j.xml @@ -50,8 +50,6 @@ - From 1e930c446e8276e9050e0ffc2ed56fe062c899f7 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 21 Dec 2019 08:40:20 -0600 Subject: [PATCH 083/544] Log4J SPI Factory Tests Basic test implementation asserting that the appender/scrubber utils are applied when log content is passed through the Logger implementations. --- .../logging/log4j/Log4JLoggerFactoryTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactoryTest.java diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactoryTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactoryTest.java new file mode 100644 index 000000000..5d73047d3 --- /dev/null +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactoryTest.java @@ -0,0 +1,97 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2019 + */ +package org.owasp.esapi.logging.log4j; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.spi.LoggerRepository; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.owasp.esapi.logging.appender.LogAppender; +import org.owasp.esapi.logging.cleaning.LogScrubber; +import org.powermock.reflect.Whitebox; + +/** + * Basic test implementation that verifies that the {@link LogAppender} and + * {@link LogScrubber} references are applied by the {@link Logger} instances + * created by the {@link Log4JLoggerFactory}. + * + */ +@RunWith(MockitoJUnitRunner.class) +public class Log4JLoggerFactoryTest { + @Mock + private LogScrubber scrubber; + @Mock + private LogAppender appender; + @Rule + public TestName testName = new TestName(); + + private String logMsg = testName.getMethodName() + "-MESSAGE"; + private Logger logger; + + @Before + public void configureStaticFactoryState() { + Log4JLoggerFactory factory = new Log4JLoggerFactory(); + Whitebox.setInternalState(Log4JLoggerFactory.class, "LOG4J_LOG_SCRUBBER", scrubber); + Whitebox.setInternalState(Log4JLoggerFactory.class, "LOG4J_LOG_APPENDER", appender); + + Mockito.when(scrubber.cleanMessage(logMsg)).thenReturn(logMsg); + Mockito.when(appender.appendTo(testName.getMethodName(), null, logMsg)).thenReturn(logMsg); + + LoggerRepository mockRepo = Mockito.mock(LoggerRepository.class); + Mockito.when(mockRepo.isDisabled(ArgumentMatchers.anyInt())).thenReturn(false); + + logger = factory.makeNewLoggerInstance(testName.getMethodName()); + Whitebox.setInternalState(logger, "repository", mockRepo); + logger.setLevel(Level.ALL); + } + + @Test + public void testLogDebug() { + logger.debug(logMsg); + Mockito.verify(scrubber, Mockito.times(1)).cleanMessage(logMsg); + Mockito.verify(appender, Mockito.times(1)).appendTo(testName.getMethodName(), null, logMsg); + } + + @Test + public void testLogInfo() { + logger.info(logMsg); + Mockito.verify(scrubber, Mockito.times(1)).cleanMessage(logMsg); + Mockito.verify(appender, Mockito.times(1)).appendTo(testName.getMethodName(), null, logMsg); + } + + @Test + public void testLogWarn() { + logger.warn(logMsg); + Mockito.verify(scrubber, Mockito.times(1)).cleanMessage(logMsg); + Mockito.verify(appender, Mockito.times(1)).appendTo(testName.getMethodName(), null, logMsg); + } + + @Test + public void testLogError() { + logger.error(logMsg); + Mockito.verify(scrubber, Mockito.times(1)).cleanMessage(logMsg); + Mockito.verify(appender, Mockito.times(1)).appendTo(testName.getMethodName(), null, logMsg); + } + +} From f4921e35d05056838ba173c2e442a521049795a8 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sun, 29 Dec 2019 03:43:52 -0600 Subject: [PATCH 084/544] Adding properties for logging user/client info Taking property implementation from cristiantm in PR #529 for issue #527. Adding properties for whether to log the username information and/or the client connection information to the DefaultSecurityConfiguration. Applying the default TRUE state to ESAPI.properties in both test and configuration scope. --- configuration/esapi/ESAPI.properties | 5 ++++- .../owasp/esapi/reference/DefaultSecurityConfiguration.java | 2 ++ src/test/resources/esapi/ESAPI.properties | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 1e3019534..922dcfa0e 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -391,7 +391,10 @@ Logger.LogServerIP=true Logger.LogFileName=ESAPI_logging_file # MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) Logger.MaxLogFileSize=10000000 - +# Determines whether ESAPI should log the user info. +Logger.UserInfo=true +# Determines whether ESAPI should log the app info. +Logger.AppInfo=true #=========================================================================== # ESAPI Intrusion Detection diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index 094a92a84..c15ad45ec 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -152,6 +152,8 @@ public static SecurityConfiguration getInstance() { public static final String LOG_ENCODING_REQUIRED = "Logger.LogEncodingRequired"; public static final String LOG_APPLICATION_NAME = "Logger.LogApplicationName"; public static final String LOG_SERVER_IP = "Logger.LogServerIP"; + public static final String LOG_USER_INFO = "Logger.UserInfo"; + public static final String LOG_APP_INFO = "Logger.AppInfo"; public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile"; public static final String VALIDATION_PROPERTIES_MULTIVALUED = "Validator.ConfigurationFile.MultiValued"; public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates"; diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 1437fae9a..e11f96c99 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -424,7 +424,10 @@ Logger.LogServerIP=true Logger.LogFileName=ESAPI_logging_file # MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) Logger.MaxLogFileSize=10000000 - +# Determines whether ESAPI should log the user info. +Logger.UserInfo=true +# Determines whether ESAPI should log the app info. +Logger.AppInfo=true #=========================================================================== # ESAPI Intrusion Detection From 617f3735e3a79a8d1ae343e9a920e0eb905b642a Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sun, 29 Dec 2019 03:45:15 -0600 Subject: [PATCH 085/544] Splitting User info from Client Supplier Breaking the user-specific information out of the ClientInfoSupplier. --- .../logging/appender/UserInfoSupplier.java | 59 ++++++++++++ .../appender/UserInfoSupplierTest.java | 95 +++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java create mode 100644 src/test/java/org/owasp/esapi/logging/appender/UserInfoSupplierTest.java diff --git a/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java new file mode 100644 index 000000000..e9c34e31a --- /dev/null +++ b/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java @@ -0,0 +1,59 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @created 2019 + */ + +package org.owasp.esapi.logging.appender; + +import java.util.function.Supplier; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.User; + +/** + * Supplier which can provide a String representing the client-side connection + * information. + */ +public class UserInfoSupplier implements Supplier { + /** Default UserName string if the Authenticated user is null.*/ + private static final String DEFAULT_USERNAME = "#ANONYMOUS#"; + + /** Whether to log the user info from this instance. */ + private boolean logUserInfo = true; + + @Override + public String get() { + // log user information - username:session@ipaddr + User user = ESAPI.authenticator().getCurrentUser(); + + String userInfo = ""; + if (logUserInfo) { + if (user == null) { + userInfo = DEFAULT_USERNAME; + } else { + userInfo = user.getAccountName(); + } + } + + return userInfo; + } + + /** + * Specify whether the instance should record the client info. + * + * @param log {@code true} to record + */ + public void setLogUserInfo(boolean log) { + this.logUserInfo = log; + } +} diff --git a/src/test/java/org/owasp/esapi/logging/appender/UserInfoSupplierTest.java b/src/test/java/org/owasp/esapi/logging/appender/UserInfoSupplierTest.java new file mode 100644 index 000000000..8ae9decef --- /dev/null +++ b/src/test/java/org/owasp/esapi/logging/appender/UserInfoSupplierTest.java @@ -0,0 +1,95 @@ +package org.owasp.esapi.logging.appender; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.owasp.esapi.Authenticator; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Randomizer; +import org.owasp.esapi.User; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({ESAPI.class}) +@PowerMockIgnore("javax.security.*") //Required since User extends javax.security.Principal +public class UserInfoSupplierTest { + private static final String ESAPI_SESSION_ATTR = "ESAPI_SESSION"; + + @Rule + public TestName testName = new TestName(); + + private Authenticator mockAuth; + private User mockUser; + + @Before + public void before() throws Exception { + mockAuth =mock(Authenticator.class); + mockUser =mock(User.class); + + mockStatic(ESAPI.class); + when(ESAPI.class, "authenticator").thenReturn(mockAuth); + + when(mockUser.getAccountName()).thenReturn(testName.getMethodName() + "-USER"); + + + when(mockAuth.getCurrentUser()).thenReturn(mockUser); + } + + @Test + public void testHappyPath() throws Exception { + UserInfoSupplier uis = new UserInfoSupplier(); + uis.setLogUserInfo(true); + String result = uis.get(); + + assertEquals(testName.getMethodName() + "-USER", result); + + verify(mockAuth,times(1)).getCurrentUser(); + verify(mockUser,times(1)).getAccountName(); + + verifyNoMoreInteractions(mockAuth, mockUser); + } + + @Test + public void testLogUserOff() { + UserInfoSupplier uis = new UserInfoSupplier(); + uis.setLogUserInfo(false); + String result = uis.get(); + + assertTrue(result.isEmpty()); + verify(mockAuth,times(1)).getCurrentUser(); + + verifyNoMoreInteractions(mockAuth, mockUser); + } + + @Test + public void testLogUserNull() { + when(mockAuth.getCurrentUser()).thenReturn(null); + UserInfoSupplier uis = new UserInfoSupplier(); + uis.setLogUserInfo(true); + String result = uis.get(); + + assertEquals("#ANONYMOUS#", result); + + verify(mockAuth,times(1)).getCurrentUser(); + + verifyNoMoreInteractions(mockAuth, mockUser); + } + +} \ No newline at end of file From 47b8929e518c9c45a04cde33fb967a0f06c13923 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sun, 29 Dec 2019 03:46:17 -0600 Subject: [PATCH 086/544] Splitting User/Client Info & Impl Propagation Completing the split of the user/client info from the appender package. Includes updates to the LogPrefixAppender to accept the configurations as two distinct boolean values. The constructor updates of the LogPrefixAppender have been propagated back to the implemented *LogFactory implementations. Related test updates. --- .../logging/appender/ClientInfoSupplier.java | 20 ++++++------- .../logging/appender/LogPrefixAppender.java | 20 +++++++++---- .../esapi/logging/java/JavaLogFactory.java | 10 ++++--- .../esapi/logging/log4j/Log4JLogFactory.java | 10 ++++--- .../logging/log4j/Log4JLoggerFactory.java | 5 ++-- .../esapi/logging/slf4j/Slf4JLogFactory.java | 10 ++++--- .../appender/ClientInfoSupplierTest.java | 27 +++++++---------- .../appender/LogPrefixAppenderTest.java | 29 +++++++++++++------ .../logging/java/JavaLogFactoryTest.java | 8 +++-- .../logging/log4j/Log4JLogFactoryTest.java | 10 +++---- .../logging/slf4j/Slf4JLogFactoryTest.java | 20 +++++++------ 11 files changed, 96 insertions(+), 73 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java index f8cbaf719..2ff07a19a 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java @@ -28,8 +28,6 @@ * information. */ public class ClientInfoSupplier implements Supplier { - /** Default UserName string if the Authenticated user is null.*/ - private static final String DEFAULT_USERNAME = "#ANONYMOUS#"; /** Default Last Host string if the Authenticated user is null.*/ private static final String DEFAULT_LAST_HOST = "#UNKNOWN_HOST#"; /** Session Attribute containing the ESAPI Session id. */ @@ -44,16 +42,16 @@ public class ClientInfoSupplier implements Supplier { private static final int ESAPI_SESSION_RAND_MAX = 1000000; /** Format for supplier output. */ - private static final String USER_INFO_FORMAT = "%s:%s@%s"; // USER_NAME, SID, USER_HOST_ADDRESS + private static final String USER_INFO_FORMAT = "%s@%s"; // SID, USER_HOST_ADDRESS /** Whether to log the user info from this instance. */ - private boolean logUserInfo = true; + private boolean logClientInfo = true; @Override public String get() { - String userInfo = ""; + String clientInfo = ""; - if (logUserInfo) { + if (logClientInfo) { HttpServletRequest request = ESAPI.currentRequest(); // create a random session number for the user to represent the user's // 'session', if it doesn't exist already @@ -73,12 +71,12 @@ public String get() { // log user information - username:session@ipaddr User user = ESAPI.authenticator().getCurrentUser(); if (user == null) { - userInfo = String.format(USER_INFO_FORMAT, DEFAULT_USERNAME, sid, DEFAULT_LAST_HOST); + clientInfo = String.format(USER_INFO_FORMAT, sid, DEFAULT_LAST_HOST); } else { - userInfo = String.format(USER_INFO_FORMAT, user.getAccountName(), sid, user.getLastHostAddress()); + clientInfo = String.format(USER_INFO_FORMAT, sid, user.getLastHostAddress()); } } - return userInfo; + return clientInfo; } /** @@ -86,8 +84,8 @@ public String get() { * * @param log {@code true} to record */ - public void setLogUserInfo(boolean log) { - this.logUserInfo = log; + public void setLogClientInfo(boolean log) { + this.logClientInfo = log; } } diff --git a/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java b/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java index 7c24b0bcf..0dc4750a7 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java +++ b/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java @@ -23,8 +23,10 @@ */ public class LogPrefixAppender implements LogAppender { /** Output format used to assemble return values. */ - private static final String RESULT_FORMAT = "[%s %s -> %s] %s";// EVENT_TYPE, CLIENT_INFO, SERVER_INFO, messageBody + private static final String RESULT_FORMAT = "[%s %s:%s -> %s] %s";// EVENT_TYPE, CLIENT_INFO, SERVER_INFO, messageBody + /** Whether or not to record user information. */ + private final boolean logUserInfo; /** Whether or not to record client information. */ private final boolean logClientInfo; /** Whether or not to record server ip information. */ @@ -37,12 +39,14 @@ public class LogPrefixAppender implements LogAppender { /** * Ctr. * + * @param logUserInfo Whether or not to record user information * @param logClientInfo Whether or not to record client information * @param logServerIp Whether or not to record server ip information * @param logApplicationName Whether or not to record application name * @param appName Application Name to record. */ - public LogPrefixAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { + public LogPrefixAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { + this.logUserInfo = logUserInfo; this.logClientInfo = logClientInfo; this.logServerIp = logServerIp; this.logApplicationName = logApplicationName; @@ -53,17 +57,21 @@ public LogPrefixAppender(boolean logClientInfo, boolean logServerIp, boolean log public String appendTo(String logName, EventType eventType, String message) { EventTypeLogSupplier eventTypeSupplier = new EventTypeLogSupplier(eventType); + UserInfoSupplier userInfoSupplier = new UserInfoSupplier(); + userInfoSupplier.setLogUserInfo(logUserInfo); + ClientInfoSupplier clientInfoSupplier = new ClientInfoSupplier(); - clientInfoSupplier.setLogUserInfo(logClientInfo); - - ServerInfoSupplier serverInfoSupplier = new ServerInfoSupplier(logName); + clientInfoSupplier.setLogClientInfo(logClientInfo); + + ServerInfoSupplier serverInfoSupplier = new ServerInfoSupplier(logName); serverInfoSupplier.setLogServerIp(logServerIp); serverInfoSupplier.setLogApplicationName(logApplicationName, appName); String eventTypeMsg = eventTypeSupplier.get(); + String userInfoMsg = userInfoSupplier.get(); String clientInfoMsg = clientInfoSupplier.get(); String serverInfoMsg = serverInfoSupplier.get(); - return String.format(RESULT_FORMAT, eventTypeMsg, clientInfoMsg, serverInfoMsg, message); + return String.format(RESULT_FORMAT, eventTypeMsg, userInfoMsg, clientInfoMsg, serverInfoMsg, message); } } diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java index a83e8c1de..2c0f2176c 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java @@ -53,11 +53,13 @@ public class JavaLogFactory implements LogFactory { boolean encodeLog = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_ENCODING_REQUIRED); JAVA_LOG_SCRUBBER = createLogScrubber(encodeLog); - boolean logClientInfo = true; + + boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); - JAVA_LOG_APPENDER = createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); + JAVA_LOG_APPENDER = createLogAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName); Map levelLookup = new HashMap<>(); levelLookup.put(Logger.ALL, JavaLogLevelHandlers.ALL); @@ -116,8 +118,8 @@ public class JavaLogFactory implements LogFactory { * * @return LogAppender instance. */ - /*package*/ static LogAppender createLogAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { - return new LogPrefixAppender(logClientInfo, logServerIp, logApplicationName, appName); + /*package*/ static LogAppender createLogAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { + return new LogPrefixAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName); } diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java index fcf42f350..cdcdbc3b8 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java @@ -51,11 +51,13 @@ public class Log4JLogFactory implements LogFactory { boolean encodeLog = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_ENCODING_REQUIRED); Log4J_LOG_SCRUBBER = createLogScrubber(encodeLog); - boolean logClientInfo = true; + + boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); - Log4J_LOG_APPENDER = createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); + Log4J_LOG_APPENDER = createLogAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName); Map levelLookup = new HashMap<>(); levelLookup.put(Logger.ALL, Log4JLogLevelHandlers.TRACE); @@ -96,8 +98,8 @@ public class Log4JLogFactory implements LogFactory { * * @return LogAppender instance. */ - /*package*/ static LogAppender createLogAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { - return new LogPrefixAppender(logClientInfo, logServerIp, logApplicationName, appName); + /*package*/ static LogAppender createLogAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { + return new LogPrefixAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName); } diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java index 5123e20b5..3e032baef 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java @@ -40,11 +40,12 @@ public class Log4JLoggerFactory implements LoggerFactory { boolean encodeLog = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_ENCODING_REQUIRED); LOG4J_LOG_SCRUBBER = Log4JLogFactory.createLogScrubber(encodeLog); - boolean logClientInfo = true; + boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); - LOG4J_LOG_APPENDER = Log4JLogFactory.createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); + LOG4J_LOG_APPENDER = Log4JLogFactory.createLogAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName); } /** diff --git a/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java index ce91a61ea..3b670745e 100644 --- a/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java @@ -57,11 +57,13 @@ public class Slf4JLogFactory implements LogFactory { boolean encodeLog = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_ENCODING_REQUIRED); SLF4J_LOG_SCRUBBER = createLogScrubber(encodeLog); - boolean logClientInfo = true; + + boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); - SLF4J_LOG_APPENDER = createLogAppender(logClientInfo, logServerIp, logApplicationName, appName); + SLF4J_LOG_APPENDER = createLogAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName); Map levelLookup = new HashMap<>(); levelLookup.put(Logger.ALL, Slf4JLogLevelHandlers.TRACE); @@ -102,8 +104,8 @@ public class Slf4JLogFactory implements LogFactory { * * @return LogAppender instance. */ - /*package*/ static LogAppender createLogAppender(boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { - return new LogPrefixAppender(logClientInfo, logServerIp, logApplicationName, appName); + /*package*/ static LogAppender createLogAppender(boolean logUserInfo, boolean logClientInfo, boolean logServerIp, boolean logApplicationName, String appName) { + return new LogPrefixAppender(logUserInfo, logClientInfo, logServerIp, logApplicationName, appName); } diff --git a/src/test/java/org/owasp/esapi/logging/appender/ClientInfoSupplierTest.java b/src/test/java/org/owasp/esapi/logging/appender/ClientInfoSupplierTest.java index b021ab06e..61c0fba6e 100644 --- a/src/test/java/org/owasp/esapi/logging/appender/ClientInfoSupplierTest.java +++ b/src/test/java/org/owasp/esapi/logging/appender/ClientInfoSupplierTest.java @@ -60,7 +60,6 @@ public void before() throws Exception { //Session value generation when(mockRand.getRandomInteger(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())).thenReturn(55555); - when(mockUser.getAccountName()).thenReturn(testName.getMethodName() + "-USER"); when(mockUser.getLastHostAddress()).thenReturn(testName.getMethodName() + "-HOST_ADDR"); @@ -70,15 +69,14 @@ public void before() throws Exception { @Test public void testHappyPath() throws Exception { ClientInfoSupplier cis = new ClientInfoSupplier(); - cis.setLogUserInfo(true); + cis.setLogClientInfo(true); String result = cis.get(); - assertEquals(testName.getMethodName() + "-USER:"+ testName.getMethodName() + "-SESSION@"+testName.getMethodName() + "-HOST_ADDR", result); + assertEquals(testName.getMethodName() + "-SESSION@"+testName.getMethodName() + "-HOST_ADDR", result); verify(mockAuth,times(1)).getCurrentUser(); verify(mockRequest,times(1)).getSession(false); verify(mockSession,times(1)).getAttribute(ESAPI_SESSION_ATTR); - verify(mockUser,times(1)).getAccountName(); verify(mockUser,times(1)).getLastHostAddress(); verifyNoMoreInteractions(mockAuth, mockRand, mockRequest, mockSession, mockUser); @@ -87,7 +85,7 @@ public void testHappyPath() throws Exception { @Test public void testLogUserOff() { ClientInfoSupplier cis = new ClientInfoSupplier(); - cis.setLogUserInfo(false); + cis.setLogClientInfo(false); String result = cis.get(); assertTrue(result.isEmpty()); @@ -99,10 +97,10 @@ public void testLogUserOff() { public void testLogUserNull() { when(mockAuth.getCurrentUser()).thenReturn(null); ClientInfoSupplier cis = new ClientInfoSupplier(); - cis.setLogUserInfo(true); + cis.setLogClientInfo(true); String result = cis.get(); - assertEquals("#ANONYMOUS#:"+testName.getMethodName()+ "-SESSION@#UNKNOWN_HOST#", result); + assertEquals(testName.getMethodName()+ "-SESSION@#UNKNOWN_HOST#", result); verify(mockAuth,times(1)).getCurrentUser(); verify(mockRequest,times(1)).getSession(false); @@ -115,14 +113,13 @@ public void testLogUserNull() { public void testNullRequest() throws Exception { when(ESAPI.class, "currentRequest").thenReturn(null); ClientInfoSupplier cis = new ClientInfoSupplier(); - cis.setLogUserInfo(true); + cis.setLogClientInfo(true); String result = cis.get(); //sid is empty when request is null - assertEquals(testName.getMethodName() + "-USER:@"+testName.getMethodName() + "-HOST_ADDR", result); + assertEquals("@"+testName.getMethodName() + "-HOST_ADDR", result); verify(mockAuth,times(1)).getCurrentUser(); - verify(mockUser,times(1)).getAccountName(); verify(mockUser,times(1)).getLastHostAddress(); verifyNoMoreInteractions(mockAuth, mockRand, mockRequest, mockSession, mockUser); @@ -132,16 +129,15 @@ public void testNullRequest() throws Exception { public void testNullSession() throws Exception { when(mockRequest.getSession(false)).thenReturn(null); ClientInfoSupplier cis = new ClientInfoSupplier(); - cis.setLogUserInfo(true); + cis.setLogClientInfo(true); String result = cis.get(); //sid is empty when session is null - assertEquals(testName.getMethodName() + "-USER:@"+testName.getMethodName() + "-HOST_ADDR", result); + assertEquals("@"+testName.getMethodName() + "-HOST_ADDR", result); verify(mockAuth,times(1)).getCurrentUser(); verify(mockRequest,times(1)).getSession(false); - verify(mockUser,times(1)).getAccountName(); verify(mockUser,times(1)).getLastHostAddress(); @@ -154,18 +150,17 @@ public void testNullSession() throws Exception { public void testNullEsapiSession() throws Exception { when(mockSession.getAttribute(ESAPI_SESSION_ATTR)).thenReturn(null); ClientInfoSupplier cis = new ClientInfoSupplier(); - cis.setLogUserInfo(true); + cis.setLogClientInfo(true); String result = cis.get(); //sid is empty when session is null - assertEquals(testName.getMethodName() + "-USER:55555@"+testName.getMethodName() + "-HOST_ADDR", result); + assertEquals("55555@"+testName.getMethodName() + "-HOST_ADDR", result); verify(mockAuth,times(1)).getCurrentUser(); verify(mockRequest,times(1)).getSession(false); verify(mockSession,times(1)).getAttribute(ESAPI_SESSION_ATTR); verify(mockSession, times(1)).setAttribute(ESAPI_SESSION_ATTR, (""+55555)); verify(mockRand, times(1)).getRandomInteger(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()); - verify(mockUser,times(1)).getAccountName(); verify(mockUser,times(1)).getLastHostAddress(); verifyNoMoreInteractions(mockAuth, mockRand, mockRequest, mockSession, mockUser); diff --git a/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java b/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java index c8bfd0c70..08bd34519 100644 --- a/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java +++ b/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java @@ -29,7 +29,10 @@ public class LogPrefixAppenderTest { private String etlsSpyGet = "EVENT_TYPE"; private ClientInfoSupplier cisSpy; - private String cisSpyGet = "CLIENT_INFO"; + private String cisSpyGet = "CLIENT_INFO"; + + private UserInfoSupplier uisSpy; + private String uisSpyGet = "USER_INFO"; private ServerInfoSupplier sisSpy; private String sisSpyGet = "SERVER_INFO"; @@ -37,10 +40,12 @@ public class LogPrefixAppenderTest { @Before public void buildSupplierSpies() { etlsSpy = spy(new EventTypeLogSupplier(Logger.EVENT_UNSPECIFIED)); + uisSpy = spy(new UserInfoSupplier()); cisSpy = spy(new ClientInfoSupplier()); sisSpy = spy(new ServerInfoSupplier(testName.getMethodName())); when(etlsSpy.get()).thenReturn(etlsSpyGet); + when(uisSpy.get()).thenReturn(uisSpyGet); when(cisSpy.get()).thenReturn(cisSpyGet); when(sisSpy.get()).thenReturn(sisSpyGet); } @@ -50,28 +55,31 @@ public void verifyDelegatePassthroughCreation() throws Exception { ArgumentCaptor eventTypeCapture = ArgumentCaptor.forClass(EventType.class); ArgumentCaptor logNameCapture = ArgumentCaptor.forClass(String.class); whenNew(EventTypeLogSupplier.class).withArguments(eventTypeCapture.capture()).thenReturn(etlsSpy); + whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); whenNew(ServerInfoSupplier.class).withArguments(logNameCapture.capture()).thenReturn(sisSpy); - LogPrefixAppender lpa = new LogPrefixAppender(true, true, true, testName.getMethodName() + "-APPLICATION"); + LogPrefixAppender lpa = new LogPrefixAppender(true, true, true, true, testName.getMethodName() + "-APPLICATION"); String result = lpa.appendTo(testName.getMethodName() + "-LOGGER", Logger.EVENT_UNSPECIFIED, testName.getMethodName() + "-MESSAGE"); // Based on the forced returns in the before block - assertEquals("[EVENT_TYPE CLIENT_INFO -> SERVER_INFO] " + testName.getMethodName() + "-MESSAGE", result); + assertEquals("[EVENT_TYPE USER_INFO:CLIENT_INFO -> SERVER_INFO] " + testName.getMethodName() + "-MESSAGE", result); assertEquals(Logger.EVENT_UNSPECIFIED, eventTypeCapture.getValue()); assertEquals(testName.getMethodName() + "-LOGGER", logNameCapture.getValue()); verify(etlsSpy, times(1)).get(); + verify(uisSpy, times(1)).get(); verify(cisSpy, times(1)).get(); verify(sisSpy, times(1)).get(); - verify(cisSpy, times(1)).setLogUserInfo(true); + verify(uisSpy, times(1)).setLogUserInfo(true); + verify(cisSpy, times(1)).setLogClientInfo(true); verify(sisSpy, times(1)).setLogServerIp(true); verify(sisSpy, times(1)).setLogApplicationName(true, testName.getMethodName() + "-APPLICATION"); - verifyNoMoreInteractions(etlsSpy, cisSpy, sisSpy); + verifyNoMoreInteractions(etlsSpy, uisSpy, cisSpy, sisSpy); } @Test @@ -79,28 +87,31 @@ public void verifyDelegatePassthroughCreation2() throws Exception { ArgumentCaptor eventTypeCapture = ArgumentCaptor.forClass(EventType.class); ArgumentCaptor logNameCapture = ArgumentCaptor.forClass(String.class); whenNew(EventTypeLogSupplier.class).withArguments(eventTypeCapture.capture()).thenReturn(etlsSpy); + whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); whenNew(ServerInfoSupplier.class).withArguments(logNameCapture.capture()).thenReturn(sisSpy); - LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, null); + LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, false, null); String result = lpa.appendTo(testName.getMethodName() + "-LOGGER", Logger.EVENT_UNSPECIFIED, testName.getMethodName() + "-MESSAGE"); // Based on the forced returns in the before block - assertEquals("[EVENT_TYPE CLIENT_INFO -> SERVER_INFO] " + testName.getMethodName() + "-MESSAGE", result); + assertEquals("[EVENT_TYPE USER_INFO:CLIENT_INFO -> SERVER_INFO] " + testName.getMethodName() + "-MESSAGE", result); assertEquals(Logger.EVENT_UNSPECIFIED, eventTypeCapture.getValue()); assertEquals(testName.getMethodName() + "-LOGGER", logNameCapture.getValue()); verify(etlsSpy, times(1)).get(); + verify(uisSpy, times(1)).get(); verify(cisSpy, times(1)).get(); verify(sisSpy, times(1)).get(); - verify(cisSpy, times(1)).setLogUserInfo(false); + verify(uisSpy, times(1)).setLogUserInfo(false); + verify(cisSpy, times(1)).setLogClientInfo(false); verify(sisSpy, times(1)).setLogServerIp(false); verify(sisSpy, times(1)).setLogApplicationName(false, null); - verifyNoMoreInteractions(etlsSpy, cisSpy, sisSpy); + verifyNoMoreInteractions(etlsSpy, uisSpy, cisSpy, sisSpy); } } diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java index 52e38b566..c0713f239 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java @@ -132,17 +132,19 @@ public void checkScrubberWithoutEncoding() throws Exception { */ @Test public void checkPassthroughAppenderConstruct() throws Exception { - LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, ""); + LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, true, ""); + ArgumentCaptor userInfoCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); - PowerMockito.whenNew(LogPrefixAppender.class).withArguments(clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); + PowerMockito.whenNew(LogPrefixAppender.class).withArguments(userInfoCapture.capture(), clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); - LogAppender appender = JavaLogFactory.createLogAppender(true, false, true, testName.getMethodName()); + LogAppender appender = JavaLogFactory.createLogAppender(true, true, false, true, testName.getMethodName()); Assert.assertEquals(stubAppender, appender); + Assert.assertTrue(userInfoCapture.getValue()); Assert.assertTrue(clientInfoCapture.getValue()); Assert.assertFalse(serverInfoCapture.getValue()); Assert.assertTrue(logAppNameCapture.getValue()); diff --git a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java index 6b9f55379..3b7378996 100644 --- a/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/log4j/Log4JLogFactoryTest.java @@ -29,8 +29,6 @@ import org.owasp.esapi.logging.cleaning.CompositeLogScrubber; import org.owasp.esapi.logging.cleaning.LogScrubber; import org.owasp.esapi.logging.cleaning.NewlineLogScrubber; -import org.owasp.esapi.logging.log4j.Log4JLogFactory; -import org.owasp.esapi.logging.log4j.Log4JLogger; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -87,17 +85,19 @@ public void checkScrubberWithoutEncoding() throws Exception { */ @Test public void checkPassthroughAppenderConstruct() throws Exception { - LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, ""); + LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, true, ""); + ArgumentCaptor userInfoCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); - PowerMockito.whenNew(LogPrefixAppender.class).withArguments(clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); + PowerMockito.whenNew(LogPrefixAppender.class).withArguments(userInfoCapture.capture(), clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); - LogAppender appender = Log4JLogFactory.createLogAppender(true, false, true, testName.getMethodName()); + LogAppender appender = Log4JLogFactory.createLogAppender(true, true, false, true, testName.getMethodName()); Assert.assertEquals(stubAppender, appender); + Assert.assertTrue(userInfoCapture.getValue()); Assert.assertTrue(clientInfoCapture.getValue()); Assert.assertFalse(serverInfoCapture.getValue()); Assert.assertTrue(logAppNameCapture.getValue()); diff --git a/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactoryTest.java index 6b4c8f972..c392f1d23 100644 --- a/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactoryTest.java @@ -85,17 +85,19 @@ public void checkScrubberWithoutEncoding() throws Exception { */ @Test public void checkPassthroughAppenderConstruct() throws Exception { - LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, ""); - ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); - - PowerMockito.whenNew(LogPrefixAppender.class).withArguments(clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); - - LogAppender appender = Slf4JLogFactory.createLogAppender(true, false, true, testName.getMethodName()); + LogPrefixAppender stubAppender = new LogPrefixAppender(true, true, true, true, ""); + ArgumentCaptor userInfoCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor clientInfoCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor serverInfoCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor logAppNameCapture = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor appNameCapture = ArgumentCaptor.forClass(String.class); + + PowerMockito.whenNew(LogPrefixAppender.class).withArguments(userInfoCapture.capture(), clientInfoCapture.capture(), serverInfoCapture.capture(), logAppNameCapture.capture(), appNameCapture.capture()).thenReturn(stubAppender); + + LogAppender appender = Slf4JLogFactory.createLogAppender(true, true, false, true, testName.getMethodName()); Assert.assertEquals(stubAppender, appender); + Assert.assertTrue(userInfoCapture.getValue()); Assert.assertTrue(clientInfoCapture.getValue()); Assert.assertFalse(serverInfoCapture.getValue()); Assert.assertTrue(logAppNameCapture.getValue()); From 1d9a24ad24ae99def5e81755846d77ea43198dd0 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 4 Jan 2020 10:25:35 -0600 Subject: [PATCH 087/544] Variable/Property Rename Updating 'appInfo' references to 'clientInfo' to aid in associating the property to the ClientInfoSupplier. --- configuration/esapi/ESAPI.properties | 2 +- src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java | 2 +- .../java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java | 2 +- .../java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java | 2 +- .../java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java | 2 +- .../org/owasp/esapi/reference/DefaultSecurityConfiguration.java | 2 +- src/test/resources/esapi/ESAPI.properties | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 922dcfa0e..20421a0bd 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -394,7 +394,7 @@ Logger.MaxLogFileSize=10000000 # Determines whether ESAPI should log the user info. Logger.UserInfo=true # Determines whether ESAPI should log the app info. -Logger.AppInfo=true +Logger.ClientInfo=true #=========================================================================== # ESAPI Intrusion Detection diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java index 2c0f2176c..93230c8e5 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java @@ -55,7 +55,7 @@ public class JavaLogFactory implements LogFactory { boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); - boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_CLIENT_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java index cdcdbc3b8..0f318600a 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLogFactory.java @@ -53,7 +53,7 @@ public class Log4JLogFactory implements LogFactory { boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); - boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_CLIENT_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); diff --git a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java index 3e032baef..bfa0914a3 100644 --- a/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java +++ b/src/main/java/org/owasp/esapi/logging/log4j/Log4JLoggerFactory.java @@ -41,7 +41,7 @@ public class Log4JLoggerFactory implements LoggerFactory { LOG4J_LOG_SCRUBBER = Log4JLogFactory.createLogScrubber(encodeLog); boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); - boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_CLIENT_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); diff --git a/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java b/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java index 3b670745e..fab645062 100644 --- a/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/slf4j/Slf4JLogFactory.java @@ -59,7 +59,7 @@ public class Slf4JLogFactory implements LogFactory { boolean logUserInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_USER_INFO); - boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APP_INFO); + boolean logClientInfo = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_CLIENT_INFO); boolean logApplicationName = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_APPLICATION_NAME); String appName = ESAPI.securityConfiguration().getStringProp(DefaultSecurityConfiguration.APPLICATION_NAME); boolean logServerIp = ESAPI.securityConfiguration().getBooleanProp(DefaultSecurityConfiguration.LOG_SERVER_IP); diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index c15ad45ec..a91e0e9b7 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -153,7 +153,7 @@ public static SecurityConfiguration getInstance() { public static final String LOG_APPLICATION_NAME = "Logger.LogApplicationName"; public static final String LOG_SERVER_IP = "Logger.LogServerIP"; public static final String LOG_USER_INFO = "Logger.UserInfo"; - public static final String LOG_APP_INFO = "Logger.AppInfo"; + public static final String LOG_CLIENT_INFO = "Logger.ClientInfo"; public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile"; public static final String VALIDATION_PROPERTIES_MULTIVALUED = "Validator.ConfigurationFile.MultiValued"; public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates"; diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index e11f96c99..2537757c1 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -427,7 +427,7 @@ Logger.MaxLogFileSize=10000000 # Determines whether ESAPI should log the user info. Logger.UserInfo=true # Determines whether ESAPI should log the app info. -Logger.AppInfo=true +Logger.ClientInfo=true #=========================================================================== # ESAPI Intrusion Detection From fc4fd1e9a02fa0d1216a45d65a38f6914e0fe2b6 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 4 Jan 2020 11:30:08 -0600 Subject: [PATCH 088/544] LogPrefixAppender output enhancements Making the implementation smarter in order to limit the prefix content to only pertient content based on whether the delegate suppliers are returning valid loggable information. --- .../logging/appender/LogPrefixAppender.java | 33 ++- .../appender/LogPrefixAppenderTest.java | 231 +++++++++++------- 2 files changed, 166 insertions(+), 98 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java b/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java index 0dc4750a7..9beb1a97e 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java +++ b/src/main/java/org/owasp/esapi/logging/appender/LogPrefixAppender.java @@ -23,7 +23,7 @@ */ public class LogPrefixAppender implements LogAppender { /** Output format used to assemble return values. */ - private static final String RESULT_FORMAT = "[%s %s:%s -> %s] %s";// EVENT_TYPE, CLIENT_INFO, SERVER_INFO, messageBody + private static final String RESULT_FORMAT = "[%s] %s"; //Assembled Prefix, MSG /** Whether or not to record user information. */ private final boolean logUserInfo; @@ -63,15 +63,34 @@ public String appendTo(String logName, EventType eventType, String message) { ClientInfoSupplier clientInfoSupplier = new ClientInfoSupplier(); clientInfoSupplier.setLogClientInfo(logClientInfo); - ServerInfoSupplier serverInfoSupplier = new ServerInfoSupplier(logName); + ServerInfoSupplier serverInfoSupplier = new ServerInfoSupplier(logName); serverInfoSupplier.setLogServerIp(logServerIp); serverInfoSupplier.setLogApplicationName(logApplicationName, appName); - String eventTypeMsg = eventTypeSupplier.get(); - String userInfoMsg = userInfoSupplier.get(); - String clientInfoMsg = clientInfoSupplier.get(); - String serverInfoMsg = serverInfoSupplier.get(); + String eventTypeMsg = eventTypeSupplier.get().trim(); + String userInfoMsg = userInfoSupplier.get().trim(); + String clientInfoMsg = clientInfoSupplier.get().trim(); + String serverInfoMsg = serverInfoSupplier.get().trim(); + + //If both user and client have content, then postfix the semicolon to the userInfoMsg at this point to simplify the StringBuilder operations later. + userInfoMsg = (!userInfoMsg.isEmpty() && !clientInfoMsg.isEmpty()) ? userInfoMsg + ":" : userInfoMsg; + + //If both server has content, then prefix the arrow to the serverInfoMsg at this point to simplify the StringBuilder operations later. + serverInfoMsg = (!serverInfoMsg.isEmpty()) ? "-> " + serverInfoMsg: serverInfoMsg; + + String[] optionalPrefixContent = new String[] {userInfoMsg + clientInfoMsg, serverInfoMsg}; - return String.format(RESULT_FORMAT, eventTypeMsg, userInfoMsg, clientInfoMsg, serverInfoMsg, message); + StringBuilder logPrefix = new StringBuilder(); + //EventType is always appended + logPrefix.append(eventTypeMsg); + + for (String element : optionalPrefixContent) { + if (!element.isEmpty()) { + logPrefix.append(" "); + logPrefix.append(element); + } + } + + return String.format(RESULT_FORMAT, logPrefix.toString(), message); } } diff --git a/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java b/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java index 08bd34519..186d69648 100644 --- a/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java +++ b/src/test/java/org/owasp/esapi/logging/appender/LogPrefixAppenderTest.java @@ -4,7 +4,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; @@ -22,96 +21,146 @@ @RunWith(PowerMockRunner.class) @PrepareForTest(LogPrefixAppender.class) public class LogPrefixAppenderTest { - @Rule - public TestName testName = new TestName(); - - private EventTypeLogSupplier etlsSpy; - private String etlsSpyGet = "EVENT_TYPE"; - - private ClientInfoSupplier cisSpy; - private String cisSpyGet = "CLIENT_INFO"; + private static final String EMPTY_RESULT = " "; + private static final String ETL_RESULT = "EVENT_TYPE"; + private static final String CIS_RESULT = "CLIENT_INFO"; + private static final String UIS_RESULT = "USER_INFO"; + private static final String SIS_RESULT = "SERVER_INFO"; + + @Rule + public TestName testName = new TestName(); - private UserInfoSupplier uisSpy; - private String uisSpyGet = "USER_INFO"; - - private ServerInfoSupplier sisSpy; - private String sisSpyGet = "SERVER_INFO"; - - @Before - public void buildSupplierSpies() { - etlsSpy = spy(new EventTypeLogSupplier(Logger.EVENT_UNSPECIFIED)); - uisSpy = spy(new UserInfoSupplier()); - cisSpy = spy(new ClientInfoSupplier()); - sisSpy = spy(new ServerInfoSupplier(testName.getMethodName())); - - when(etlsSpy.get()).thenReturn(etlsSpyGet); - when(uisSpy.get()).thenReturn(uisSpyGet); - when(cisSpy.get()).thenReturn(cisSpyGet); - when(sisSpy.get()).thenReturn(sisSpyGet); - } - - @Test - public void verifyDelegatePassthroughCreation() throws Exception { - ArgumentCaptor eventTypeCapture = ArgumentCaptor.forClass(EventType.class); - ArgumentCaptor logNameCapture = ArgumentCaptor.forClass(String.class); - whenNew(EventTypeLogSupplier.class).withArguments(eventTypeCapture.capture()).thenReturn(etlsSpy); - whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); - whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); - whenNew(ServerInfoSupplier.class).withArguments(logNameCapture.capture()).thenReturn(sisSpy); - - LogPrefixAppender lpa = new LogPrefixAppender(true, true, true, true, testName.getMethodName() + "-APPLICATION"); - String result = lpa.appendTo(testName.getMethodName() + "-LOGGER", Logger.EVENT_UNSPECIFIED, - testName.getMethodName() + "-MESSAGE"); - - // Based on the forced returns in the before block - assertEquals("[EVENT_TYPE USER_INFO:CLIENT_INFO -> SERVER_INFO] " + testName.getMethodName() + "-MESSAGE", result); - - assertEquals(Logger.EVENT_UNSPECIFIED, eventTypeCapture.getValue()); - assertEquals(testName.getMethodName() + "-LOGGER", logNameCapture.getValue()); - - verify(etlsSpy, times(1)).get(); - verify(uisSpy, times(1)).get(); - verify(cisSpy, times(1)).get(); - verify(sisSpy, times(1)).get(); - - verify(uisSpy, times(1)).setLogUserInfo(true); - verify(cisSpy, times(1)).setLogClientInfo(true); - verify(sisSpy, times(1)).setLogServerIp(true); - verify(sisSpy, times(1)).setLogApplicationName(true, testName.getMethodName() + "-APPLICATION"); - - verifyNoMoreInteractions(etlsSpy, uisSpy, cisSpy, sisSpy); - } - - @Test - public void verifyDelegatePassthroughCreation2() throws Exception { - ArgumentCaptor eventTypeCapture = ArgumentCaptor.forClass(EventType.class); - ArgumentCaptor logNameCapture = ArgumentCaptor.forClass(String.class); - whenNew(EventTypeLogSupplier.class).withArguments(eventTypeCapture.capture()).thenReturn(etlsSpy); - whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); - whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); - whenNew(ServerInfoSupplier.class).withArguments(logNameCapture.capture()).thenReturn(sisSpy); - - LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, false, null); - String result = lpa.appendTo(testName.getMethodName() + "-LOGGER", Logger.EVENT_UNSPECIFIED, - testName.getMethodName() + "-MESSAGE"); - - // Based on the forced returns in the before block - assertEquals("[EVENT_TYPE USER_INFO:CLIENT_INFO -> SERVER_INFO] " + testName.getMethodName() + "-MESSAGE", result); - - assertEquals(Logger.EVENT_UNSPECIFIED, eventTypeCapture.getValue()); - assertEquals(testName.getMethodName() + "-LOGGER", logNameCapture.getValue()); - - verify(etlsSpy, times(1)).get(); - verify(uisSpy, times(1)).get(); - verify(cisSpy, times(1)).get(); - verify(sisSpy, times(1)).get(); - - verify(uisSpy, times(1)).setLogUserInfo(false); - verify(cisSpy, times(1)).setLogClientInfo(false); - verify(sisSpy, times(1)).setLogServerIp(false); - verify(sisSpy, times(1)).setLogApplicationName(false, null); - - verifyNoMoreInteractions(etlsSpy, uisSpy, cisSpy, sisSpy); - } + private String testLoggerName = testName.getMethodName() + "-LOGGER"; + private String testLogMessage = testName.getMethodName() + "-MESSAGE"; + private String testApplicationName = testName.getMethodName() + "-APPLICATION_NAME"; + private EventType testEventType = Logger.EVENT_UNSPECIFIED; + private EventTypeLogSupplier etlsSpy; + private ClientInfoSupplier cisSpy; + private UserInfoSupplier uisSpy; + private ServerInfoSupplier sisSpy; + + @Before + public void buildSupplierSpies() { + etlsSpy = spy(new EventTypeLogSupplier(Logger.EVENT_UNSPECIFIED)); + uisSpy = spy(new UserInfoSupplier()); + cisSpy = spy(new ClientInfoSupplier()); + sisSpy = spy(new ServerInfoSupplier(testName.getMethodName())); + + testLoggerName = testName.getMethodName() + "-LOGGER"; + testLogMessage = testName.getMethodName() + "-MESSAGE"; + testApplicationName = testName.getMethodName() + "-APPLICATION_NAME"; + } + @Test + public void testCtrArgTruePassthroughToDelegates() throws Exception { + when(etlsSpy.get()).thenReturn(ETL_RESULT); + when(uisSpy.get()).thenReturn(UIS_RESULT); + when(cisSpy.get()).thenReturn(CIS_RESULT); + when(sisSpy.get()).thenReturn(SIS_RESULT); + + whenNew(EventTypeLogSupplier.class).withArguments(testEventType).thenReturn(etlsSpy); + whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); + whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); + whenNew(ServerInfoSupplier.class).withArguments(testLoggerName).thenReturn(sisSpy); + + LogPrefixAppender lpa = new LogPrefixAppender(true, true,true,true, testApplicationName); + lpa.appendTo(testLoggerName, testEventType, testLogMessage); + + verify(uisSpy, times(1)).setLogUserInfo(true); + verify(cisSpy, times(1)).setLogClientInfo(true); + verify(sisSpy, times(1)).setLogServerIp(true); + verify(sisSpy, times(1)).setLogApplicationName(true, testApplicationName); + } + + @Test + public void testCtrArgFalsePassthroughToDelegates() throws Exception { + when(etlsSpy.get()).thenReturn(ETL_RESULT); + when(uisSpy.get()).thenReturn(UIS_RESULT); + when(cisSpy.get()).thenReturn(CIS_RESULT); + when(sisSpy.get()).thenReturn(SIS_RESULT); + + whenNew(EventTypeLogSupplier.class).withArguments(testEventType).thenReturn(etlsSpy); + whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); + whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); + whenNew(ServerInfoSupplier.class).withArguments(testLoggerName).thenReturn(sisSpy); + + LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, false, null); + lpa.appendTo(testLoggerName, testEventType, testLogMessage); + + verify(uisSpy, times(1)).setLogUserInfo(false); + verify(cisSpy, times(1)).setLogClientInfo(false); + verify(sisSpy, times(1)).setLogServerIp(false); + verify(sisSpy, times(1)).setLogApplicationName(false, null); + } + + @Test + public void testDelegateCtrArgs() throws Exception { + ArgumentCaptor eventTypeCapture = ArgumentCaptor.forClass(EventType.class); + ArgumentCaptor logNameCapture = ArgumentCaptor.forClass(String.class); + whenNew(EventTypeLogSupplier.class).withArguments(eventTypeCapture.capture()).thenReturn(etlsSpy); + whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); + whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); + whenNew(ServerInfoSupplier.class).withArguments(logNameCapture.capture()).thenReturn(sisSpy); + + LogPrefixAppender lpa = new LogPrefixAppender(true, true,true,true, testApplicationName); + lpa.appendTo(testLoggerName, testEventType, testLogMessage); + + assertEquals(testEventType, eventTypeCapture.getValue()); + assertEquals(testLoggerName, logNameCapture.getValue()); + } + + @Test + public void testLogContentWhenClientInfoEmpty() throws Exception { + runTest(ETL_RESULT, UIS_RESULT, EMPTY_RESULT,SIS_RESULT, "[EVENT_TYPE USER_INFO -> SERVER_INFO]"); + } + + + @Test + public void testLogContentWhenUserInfoEmpty() throws Exception { + runTest(ETL_RESULT, EMPTY_RESULT, CIS_RESULT,SIS_RESULT, "[EVENT_TYPE CLIENT_INFO -> SERVER_INFO]"); + } + + @Test + public void testLogContentWhenClientInfoEmptyAndServerInfoEmpty() throws Exception { + runTest(ETL_RESULT, UIS_RESULT, EMPTY_RESULT,EMPTY_RESULT, "[EVENT_TYPE USER_INFO]"); + } + + @Test + public void testLogContentWhenUserInfoEmptyAndServerInfoEmpty() throws Exception { + runTest(ETL_RESULT, EMPTY_RESULT, CIS_RESULT,EMPTY_RESULT, "[EVENT_TYPE CLIENT_INFO]"); + } + + @Test + public void testLogContentWhenUserInfoAndClientInfoEmpty() throws Exception { + runTest(ETL_RESULT, EMPTY_RESULT, EMPTY_RESULT, SIS_RESULT, "[EVENT_TYPE -> SERVER_INFO]"); + } + + @Test + public void testLogContentWhenServerInfoEmpty() throws Exception { + runTest(ETL_RESULT, UIS_RESULT, CIS_RESULT, EMPTY_RESULT, "[EVENT_TYPE USER_INFO:CLIENT_INFO]"); + } + + @Test + public void testLogContentWhenUserInfoEmptyAndClientInfoEmptyAndServerInfoEmpty() throws Exception { + runTest(ETL_RESULT, EMPTY_RESULT, EMPTY_RESULT, EMPTY_RESULT, "[EVENT_TYPE]"); + } + + + private void runTest(String typeResult, String userResult, String clientResult, String serverResult, String exResult) throws Exception{ + when(etlsSpy.get()).thenReturn(typeResult); + when(uisSpy.get()).thenReturn(userResult); + when(cisSpy.get()).thenReturn(clientResult); + when(sisSpy.get()).thenReturn(serverResult); + + whenNew(EventTypeLogSupplier.class).withArguments(testEventType).thenReturn(etlsSpy); + whenNew(UserInfoSupplier.class).withNoArguments().thenReturn(uisSpy); + whenNew(ClientInfoSupplier.class).withNoArguments().thenReturn(cisSpy); + whenNew(ServerInfoSupplier.class).withArguments(testLoggerName).thenReturn(sisSpy); + + //Since everything is mocked these booleans don't much matter aside from the later verifies + LogPrefixAppender lpa = new LogPrefixAppender(false, false, false, false, null); + String result = lpa.appendTo(testLoggerName, testEventType, testLogMessage); + + assertEquals(exResult + " " + testName.getMethodName() + "-MESSAGE", result); + } } From 724ffdd0b0ebe5a958be952a7578fb5f97525020 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 4 Jan 2020 11:37:31 -0600 Subject: [PATCH 089/544] EventTypeSupplier Improvement Improving null handling by defaulting a null constructor parameter to Logger.EVENT_UNSPECIFIED. This then prevents the get() call from ever returning an empty String. Test case updated to be parameterized and validates each of the define EventType cases, along with the null EventType case. --- .../appender/EventTypeLogSupplier.java | 5 ++- .../appender/EventTypeLogSupplierTest.java | 40 ++++++++++++++----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java index 447c7a6e5..2f857f128 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java @@ -17,6 +17,7 @@ import java.util.function.Supplier; +import org.owasp.esapi.Logger; import org.owasp.esapi.Logger.EventType; /** @@ -34,11 +35,11 @@ public class EventTypeLogSupplier implements Supplier { * @param evtyp EventType reference to supply log representation for */ public EventTypeLogSupplier(EventType evtyp) { - this.eventType = evtyp; + this.eventType = evtyp == null ? Logger.EVENT_UNSPECIFIED : evtyp; } @Override public String get() { - return eventType == null ? "" : eventType.toString(); + return eventType.toString(); } } diff --git a/src/test/java/org/owasp/esapi/logging/appender/EventTypeLogSupplierTest.java b/src/test/java/org/owasp/esapi/logging/appender/EventTypeLogSupplierTest.java index c81204d1e..f86fbbca4 100644 --- a/src/test/java/org/owasp/esapi/logging/appender/EventTypeLogSupplierTest.java +++ b/src/test/java/org/owasp/esapi/logging/appender/EventTypeLogSupplierTest.java @@ -2,25 +2,45 @@ import static org.junit.Assert.assertEquals; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import org.owasp.esapi.Logger; import org.owasp.esapi.Logger.EventType; +@RunWith(Parameterized.class) public class EventTypeLogSupplierTest { + @Parameters (name="{0} -> {1}") + public static Collection assembleTests() { + List paramSets = new ArrayList<>(); + paramSets.add(new Object[] {Logger.EVENT_FAILURE,Logger.EVENT_FAILURE.toString()}); + paramSets.add(new Object[] {Logger.EVENT_SUCCESS,Logger.EVENT_SUCCESS.toString()}); + paramSets.add(new Object[] {Logger.EVENT_UNSPECIFIED,Logger.EVENT_UNSPECIFIED.toString()}); + paramSets.add(new Object[] {Logger.SECURITY_AUDIT,Logger.SECURITY_AUDIT.toString()}); + paramSets.add(new Object[] {Logger.SECURITY_FAILURE,Logger.SECURITY_FAILURE.toString()}); + paramSets.add(new Object[] {Logger.SECURITY_SUCCESS,Logger.SECURITY_SUCCESS.toString()}); + paramSets.add(new Object[] {null, Logger.EVENT_UNSPECIFIED.toString()}); + + return paramSets; + } + + private final EventType eventType; + private final String expectedResult; + + public EventTypeLogSupplierTest(EventType eventType, String result) { + this.eventType = eventType; + this.expectedResult = result; + } @Test public void testEventTypeLog() { - EventType eventType = Logger.EVENT_UNSPECIFIED; EventTypeLogSupplier supplier = new EventTypeLogSupplier(eventType); - - assertEquals(eventType.toString(), supplier.get()); + assertEquals(expectedResult, supplier.get()); } - @Test - public void testNullEventTypeLog() { - - EventTypeLogSupplier supplier = new EventTypeLogSupplier(null); - - assertEquals("", supplier.get()); - } } From 872f8db9f728b0aaab9ac5b270658ad036997ef0 Mon Sep 17 00:00:00 2001 From: jeremiahjstacey Date: Sat, 1 Feb 2020 11:25:12 -0600 Subject: [PATCH 090/544] Issue 536 (#537) * Variable Scope Increase Reducing duplication by updating common strings to package-protected. * SecurityWrapperResponse setHeader updates Updating implementation to independently test the header and the value method params. Additional Logging has been added to identify each unique case of ValidationException offense. Tests have been updated and extended to verify the setHeader method. * SecurityWrapperResponse addHeader updates Updating addHeader behavior and testing to be inline with setHeader updates requested in issue. Tests have been updated and extended to verify the addHeader method. * SecurityWrapperRepsonse addHeader log update Copy/Pasta "set" context -- now corrected to "add" * SecurityWrapperResponse Header Log Improvements Appending the input header name to log messages when ValidationExceptions occur. --- .../filters/SecurityWrapperResponse.java | 55 +- .../filters/SecurityWrapperRequestTest.java | 17 +- .../filters/SecurityWrapperResponseTest.java | 649 ++++++++++++++++-- 3 files changed, 658 insertions(+), 63 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index 91852b1ab..3cb152477 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -173,16 +173,28 @@ public void addDateHeader(String name, long date) { * @param value */ public void addHeader(String name, String value) { + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String strippedName = StringUtilities.stripControls(name); + String strippedValue = StringUtilities.stripControls(value); + String safeName = null; + String safeValue = null; try { - // TODO: make stripping a global config - SecurityConfiguration sc = ESAPI.securityConfiguration(); - String strippedName = StringUtilities.stripControls(name); - String strippedValue = StringUtilities.stripControls(value); - String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); - String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); - getHttpServletResponse().addHeader(safeName, safeValue); + safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); } catch (ValidationException e) { - logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e); + logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header NAME denied: HTTPHeaderName:"+ name, e); + } + + try { + safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); + } catch (ValidationException e) { + logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header VALUE denied: HTTPHeaderName:"+ name, e); + } + + boolean validName = StringUtilities.notNullOrEmpty(safeName, true); + boolean validValue = StringUtilities.notNullOrEmpty(safeValue, true); + + if (validName && validValue) { + getHttpServletResponse().addHeader(safeName, safeValue); } } @@ -483,15 +495,28 @@ public void setDateHeader(String name, long date) { * @param value */ public void setHeader(String name, String value) { + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String strippedName = StringUtilities.stripControls(name); + String strippedValue = StringUtilities.stripControls(value); + String safeName = null; + String safeValue = null; try { - String strippedName = StringUtilities.stripControls(name); - String strippedValue = StringUtilities.stripControls(value); - SecurityConfiguration sc = ESAPI.securityConfiguration(); - String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); - String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); - getHttpServletResponse().setHeader(safeName, safeValue); + safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); } catch (ValidationException e) { - logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e); + logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header NAME denied: HTTPHeaderName:"+ name, e); + } + + try { + safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); + } catch (ValidationException e) { + logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header VALUE denied: HTTPHeaderName:"+ name, e); + } + + boolean validName = StringUtilities.notNullOrEmpty(safeName, true); + boolean validValue = StringUtilities.notNullOrEmpty(safeValue, true); + + if (validName && validValue) { + getHttpServletResponse().setHeader(safeName, safeValue); } } diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java index dddad6d3b..03b776df8 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java @@ -18,8 +18,8 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; // A hack for now; eventually, I plan to move this into a new org.owasp.esapi.PropNames class. -kww @@ -41,7 +41,6 @@ import org.mockito.stubbing.Answer; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; -import org.owasp.esapi.Logger.EventType; import org.owasp.esapi.SecurityConfiguration; import org.owasp.esapi.Validator; import org.owasp.esapi.errors.ValidationException; @@ -61,15 +60,15 @@ @RunWith(PowerMockRunner.class) @PrepareForTest(ESAPI.class) public class SecurityWrapperRequestTest { - private static final String ESAPI_VALIDATOR_GETTER_METHOD_NAME = "validator"; - private static final String ESAPI_GET_LOGGER_METHOD_NAME = "getLogger"; - private static final String ESAPY_SECURITY_CONFIGURATION_GETTER_METHOD_NAME = "securityConfiguration"; - private static final String SECURITY_CONFIGURATION_QUERY_STRING_LENGTH_KEY_NAME = "HttpUtilities.URILENGTH"; - private static final String SECURITY_CONFIGURATION_PARAMETER_STRING_LENGTH_KEY_NAME = "HttpUtilities.httpQueryParamValueLength"; + protected static final String ESAPI_VALIDATOR_GETTER_METHOD_NAME = "validator"; + protected static final String ESAPI_GET_LOGGER_METHOD_NAME = "getLogger"; + protected static final String ESAPY_SECURITY_CONFIGURATION_GETTER_METHOD_NAME = "securityConfiguration"; + protected static final String SECURITY_CONFIGURATION_QUERY_STRING_LENGTH_KEY_NAME = "HttpUtilities.URILENGTH"; + protected static final String SECURITY_CONFIGURATION_PARAMETER_STRING_LENGTH_KEY_NAME = "HttpUtilities.httpQueryParamValueLength"; private static final String SECURITY_CONFIGURATION_HEADER_NAME_LENGTH_KEY_NAME = "HttpUtilities.MaxHeaderNameSize"; private static final String SECURITY_CONFIGURATION_HEADER_VALUE_LENGTH_KEY_NAME = "HttpUtilities.MaxHeaderValueSize"; - private static final int SECURITY_CONFIGURATION_TEST_LENGTH = 255; + protected static final int SECURITY_CONFIGURATION_TEST_LENGTH = 255; private static final String QUERY_STRING_CANONCALIZE_TYPE_KEY = "HTTPQueryString"; private static final String PARAMETER_STRING_CANONCALIZE_TYPE_KEY = "HTTPParameterValue"; diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java index 362fa5be1..499da8799 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java @@ -1,37 +1,616 @@ package org.owasp.esapi.filters; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; - -import java.util.Collection; +import static org.owasp.esapi.reference.DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; import org.mockito.Mockito; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Logger; +import org.owasp.esapi.SecurityConfiguration; +import org.owasp.esapi.Validator; +import org.owasp.esapi.errors.ValidationException; import org.owasp.esapi.http.MockHttpServletResponse; import org.owasp.esapi.util.TestUtils; +import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; //@PrepareForTest({SecurityWrapperResponse.class}) @RunWith(PowerMockRunner.class) +@PrepareForTest(ESAPI.class) @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*"}) public class SecurityWrapperResponseTest { - + private static final String HEADER_NAME_CONTEXT = "HTTPHeaderName"; + private static final String HEADER_VALUE_CONTEXT = "HTTPHeaderValue"; + private static final String SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR = "HttpUtilities.MaxHeaderNameSize"; + private static final String SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR = "HttpUtilities.MaxHeaderValueSize"; + + + @Rule + public TestName testName = new TestName(); + + @Mock + private HttpServletResponse mockResponse; + @Mock + private Validator mockValidator; + @Mock + private SecurityConfiguration mockSecConfig; + @Mock + private Logger mockLogger; + + private String goodHeaderName; + private String goodHeaderValue; + + @Before + public void setup() throws Exception { + //preconfig will impact other tests unless isolated. Still don't want to duplicate it though. + if (testName.getMethodName().startsWith("testSetHeader") || + testName.getMethodName().startsWith("testAddHeader")) { + PowerMockito.mockStatic(ESAPI.class); + PowerMockito.when(ESAPI.class, SecurityWrapperRequestTest.ESAPI_VALIDATOR_GETTER_METHOD_NAME).thenReturn(mockValidator); + PowerMockito.when(ESAPI.class, SecurityWrapperRequestTest.ESAPI_GET_LOGGER_METHOD_NAME, "SecurityWrapperResponse").thenReturn(mockLogger); + PowerMockito.when(ESAPI.class, SecurityWrapperRequestTest.ESAPY_SECURITY_CONFIGURATION_GETTER_METHOD_NAME).thenReturn(mockSecConfig); + //Is intrusion detection disabled? A: Yes, it is off. + //This logic is confusing: True, the value is False... + Mockito.when( mockSecConfig.getBooleanProp( DISABLE_INTRUSION_DETECTION ) ).thenReturn(true); + + goodHeaderName = testName.getMethodName() + "_goodHeaderName"; + goodHeaderValue = testName.getMethodName() + "_goodHeaderValue"; + } + } + @Test - public void testAddHeader(){ - HttpServletResponse servResp = mock(HttpServletResponse.class); - SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); - resp.addHeader("Foo", "bar"); - verify(servResp, times(1)).addHeader("Foo", "bar"); + public void testSetHeaderHappyPath() throws Exception { + String validateNameResponse = goodHeaderName; + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), ArgumentMatchers.eq(HEADER_NAME_CONTEXT), ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(1)).setHeader(validateNameResponse, validateValueResponse); + verify(mockLogger,times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), anyString(), ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testSetHeaderNameNull() throws Exception { + String validateNameResponse = null; + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).setHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); } - + + @Test + public void testSetHeaderNameEmpty() throws Exception { + String validateNameResponse = " "; + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).setHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testSetHeaderNameThrowsValidationException() throws Exception { + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenThrow(ValidationException.class); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).setHeader(anyString(), anyString()); + + verify(mockLogger, times(1)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), + ArgumentMatchers.contains("Attempt to set invalid header NAME denied: HTTPHeaderName:"+ goodHeaderName), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testSetHeaderValueNull() throws Exception { + String validateNameResponse = goodHeaderName; + String validateValueResponse = null; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).setHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testSetHeaderValueEmpty() throws Exception { + String validateNameResponse = goodHeaderName; + String validateValueResponse = " "; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).setHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testSetHeaderValueThrowsValidationException() throws Exception { + String validateNameResponse = goodHeaderName; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenThrow(ValidationException.class); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.setHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).setHeader(anyString(), anyString()); + + verify(mockLogger, times(1)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), + ArgumentMatchers.contains("Attempt to set invalid header VALUE denied: HTTPHeaderName:"+ goodHeaderName), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testAddHeaderHappyPath() throws Exception { + String validateNameResponse = goodHeaderName; + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.addHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), ArgumentMatchers.eq(HEADER_NAME_CONTEXT), ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(1)).addHeader(validateNameResponse, validateValueResponse); + verify(mockLogger,times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), anyString(), ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testAddHeaderNameNull() throws Exception { + String validateNameResponse = null; + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.addHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).addHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testAddHeaderNameEmpty() throws Exception { + String validateNameResponse = " "; + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.addHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).addHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testAddHeaderNameThrowsValidationException() throws Exception { + String validateValueResponse = goodHeaderValue; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenThrow(ValidationException.class); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.addHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).addHeader(anyString(), anyString()); + + verify(mockLogger, times(1)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), + ArgumentMatchers.contains("Attempt to add invalid header NAME denied: HTTPHeaderName:"+ goodHeaderName ), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testAddHeaderValueNull() throws Exception { + String validateNameResponse = goodHeaderName; + String validateValueResponse = null; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.addHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).addHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testAddHeaderValueEmpty() throws Exception { + String validateNameResponse = goodHeaderName; + String validateValueResponse = " "; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateValueResponse); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.addHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).addHeader(anyString(), anyString()); + verify(mockLogger, times(0)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class),anyString(), + ArgumentMatchers.any(Exception.class)); + } + + @Test + public void testAddHeaderValueThrowsValidationException() throws Exception { + String validateNameResponse = goodHeaderName; + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenReturn(validateNameResponse); + + PowerMockito.when(mockValidator.getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false))).thenThrow(ValidationException.class); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + PowerMockito.when(mockSecConfig.getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR)).thenReturn( + SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH); + + SecurityWrapperResponse response = new SecurityWrapperResponse(mockResponse); + + response.addHeader(goodHeaderName, goodHeaderValue); + + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderName), + ArgumentMatchers.eq(HEADER_NAME_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockValidator, times(1)).getValidInput(anyString(), ArgumentMatchers.eq(goodHeaderValue), + ArgumentMatchers.eq(HEADER_VALUE_CONTEXT), + ArgumentMatchers.eq(SecurityWrapperRequestTest.SECURITY_CONFIGURATION_TEST_LENGTH), + ArgumentMatchers.eq(false)); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_NAME_SIZE_ATTR); + verify(mockSecConfig, times(1)).getIntProp(SEC_CTX_MAX_HEADER_VALUE_SIZE_ATTR); + verify(mockResponse, times(0)).addHeader(anyString(), anyString()); + + verify(mockLogger, times(1)).warning(ArgumentMatchers.any(org.owasp.esapi.Logger.EventType.class), + ArgumentMatchers.contains("Attempt to add invalid header VALUE denied: HTTPHeaderName:"+ goodHeaderName), + ArgumentMatchers.any(Exception.class)); + } + @Test public void testAddRefererHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -39,7 +618,7 @@ public void testAddRefererHeader(){ resp.addReferer("http://127.0.0.1:3000/campaigns?goal=all§ion=active&sort-by=-id&status=Draft%2CLaunched"); verify(servResp, times(1)).addHeader("referer", ""); } - + @Test public void testAddDateHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -48,7 +627,7 @@ public void testAddDateHeader(){ resp.addDateHeader("Foo", currentTime); verify(servResp, times(1)).addDateHeader("Foo", currentTime); } - + @Test public void testSetDateHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -57,7 +636,7 @@ public void testSetDateHeader(){ resp.setDateHeader("Foo", currentTime); verify(servResp, times(1)).setDateHeader("Foo", currentTime); } - + @Test public void testSetInvalidDateHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -66,15 +645,7 @@ public void testSetInvalidDateHeader(){ resp.setDateHeader("alert"); verify(servResp, times(0)).setHeader("foo", ""); } - + @Test public void testInvalidDateHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -91,7 +662,7 @@ public void testInvalidDateHeader(){ resp.addDateHeader("Foo\\r\\n", currentTime); verify(servResp, times(0)).addDateHeader("Foo", currentTime); } - + @Test public void testAddHeaderInvalidValueLength(){ //refactor this to use a spy. @@ -102,7 +673,7 @@ public void testAddHeaderInvalidValueLength(){ resp.addHeader("Foo", TestUtils.generateStringOfLength(4097)); verify(servResp, times(0)).addHeader("Foo", "bar"); } - + @Test public void testAddHeaderInvalidKeyLength(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -110,7 +681,7 @@ public void testAddHeaderInvalidKeyLength(){ resp.addHeader(TestUtils.generateStringOfLength(257), "bar"); verify(servResp, times(0)).addHeader("Foo", "bar"); } - + @Test public void testAddIntHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -118,7 +689,7 @@ public void testAddIntHeader(){ resp.addIntHeader("aaaa", 4); verify(servResp, times(1)).addIntHeader("aaaa", 4); } - + @Test public void testAddInvalidIntHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -126,7 +697,7 @@ public void testAddInvalidIntHeader(){ resp.addIntHeader(TestUtils.generateStringOfLength(257), Integer.MIN_VALUE); verify(servResp, times(0)).addIntHeader(TestUtils.generateStringOfLength(257), Integer.MIN_VALUE); } - + @Test public void testContainsHeader(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -137,7 +708,7 @@ public void testContainsHeader(){ verify(servResp, times(1)).addIntHeader("aaaa", Integer.MIN_VALUE); assertEquals(true, servResp.containsHeader("aaaa")); } - + @Test public void testAddValidCookie(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -148,7 +719,7 @@ public void testAddValidCookie(){ cookie.setMaxAge(5000); Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); spyResp.addCookie(cookie); - + /* * We're indirectly testing our class. Since it ultimately * delegates to HttpServletResponse.addHeader, we're actually @@ -158,7 +729,7 @@ public void testAddValidCookie(){ */ verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Max-Age=5000; Secure; HttpOnly"); } - + @Test public void testAddValidCookieWithDomain(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -172,7 +743,7 @@ public void testAddValidCookieWithDomain(){ spyResp.addCookie(cookie); verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Domain=evil.com; Secure; HttpOnly"); } - + @Test public void testAddValidCookieWithPath(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -186,7 +757,7 @@ public void testAddValidCookieWithPath(){ spyResp.addCookie(cookie); verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Domain=evil.com; Path=/foo/bar; Secure; HttpOnly"); } - + @Test public void testAddInValidCookie(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -195,11 +766,11 @@ public void testAddInValidCookie(){ SecurityWrapperResponse spyResp = spy(resp); Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(5000)); Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); - + spyResp.addCookie(cookie); verify(servResp, times(0)).addHeader("Set-Cookie", "Foo=" + TestUtils.generateStringOfLength(5000) + "; Secure; HttpOnly"); } - + @Test public void testSendError() throws Exception{ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -208,10 +779,10 @@ public void testSendError() throws Exception{ SecurityWrapperResponse spyResp = spy(resp); Mockito.doCallRealMethod().when(spyResp).sendError(200); spyResp.sendError(200); - + verify(servResp, times(1)).sendError(200, "HTTP error code: 200");; } - + @Test public void testSendStatus() throws Exception{ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -220,10 +791,10 @@ public void testSendStatus() throws Exception{ SecurityWrapperResponse spyResp = spy(resp); Mockito.doCallRealMethod().when(spyResp).setStatus(200);; spyResp.setStatus(200); - + verify(servResp, times(1)).setStatus(200);; } - + @Test public void testSendStatusWithString() throws Exception{ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -232,7 +803,7 @@ public void testSendStatusWithString() throws Exception{ SecurityWrapperResponse spyResp = spy(resp); Mockito.doCallRealMethod().when(spyResp).setStatus(200, "foo");; spyResp.setStatus(200, "foo"); - + verify(servResp, times(1)).sendError(200, "foo");; } } From 422ba9f27ad1956682a7b0903c9f8119e6cd598d Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Sat, 8 Feb 2020 22:34:19 -0500 Subject: [PATCH 091/544] Issue 521 (#535) * Add formal deprecation policy. * Add property Validator.ValidationRule.getValid.ignore509Fix. Truly a kludge if there every was one. * Add static field VALIDATOR_IGNORE509 for kludge. * Address issue #521 by splitting out failing JUnit test cases, testGetValidSafeHTML() and testIsValidSafeHTML() into separate test files. New files will be src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleLogsTest.java and src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java * Address issue #521 by kludge to add backward-compatibility flag to restore the old behavior accidentally broken by the changes to address issue #509. * Javadoc clarifications to address issue #521 for behavior broken by issue #509 commits. * New test files for GitHub issue #521; initial commit. * Add additional sentence about ESAPI deprecation policy. * Since we've deprecated Log4J 1 logger, let's go all in and remove it from the default ESAPI.Logger in ESAPI.properties as well. * Changed new property name from the horribly named Validator.ValidationRule.getValid.ignore509Fix to the more appropriately named Validator.HtmlValidationAction whose possible values are "clean" (for legacy behavior) and "throw" for the new behavior as fixed by GitHub issue #509. If the property is not encountered, it is treated as if "clean" had been specified, i.e., the legacy behavior. * Added string constant for new property, Validator.HtmlValidationAction. * Changes in keeping with new prop name, Validator.HtmlValidationAction * Rename JUnit test file. * Rename JUnit test class to sync w/ new file name. * Convert from JUnit 3 to JUnit 4. --- README.md | 4 + configuration/esapi/ESAPI.properties | 40 ++++- .../java/org/owasp/esapi/ValidationRule.java | 20 ++- .../DefaultSecurityConfiguration.java | 3 +- .../validation/HTMLValidationRule.java | 60 ++++++- .../owasp/esapi/reference/ValidatorTest.java | 63 +------ .../HTMLValidationRuleCleanTest.java | 158 +++++++++++++++++ .../HTMLValidationRuleThrowsTest.java | 167 ++++++++++++++++++ src/test/resources/esapi/ESAPI.properties | 41 ++++- 9 files changed, 484 insertions(+), 72 deletions(-) create mode 100644 src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java create mode 100644 src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java diff --git a/README.md b/README.md index e6701c280..f7c38d99d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ The default branch for ESAPI legacy is now the 'develop' branch (rather than the # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java +# ESAPI Deprecation Policy +Unless we unintentionally screw-up, our intent is to keep classes, methods, and/or fields whihc have been annotated as "@deprecated" for a minimum of two (2) years or until the next major release number (e.g., 3.x as of now), which ever comes first, before we remove them. +Note that this policy does not apply to classes under the **org.owasp.esapi.reference** package. You are not expected to be using such classes directly in your code. + # Contributing to ESAPI legacy ## How can I contribute or help with fix bugs? Fork and submit a pull request! Simple as pi! We generally only accept bug fixes, not new features because as a legacy project, we don't intend on adding new features, although we may make exceptions. If you wish to propose a new feature, the best place to discuss it is via the ESAPI-DEV mailing list mentioned below. Note that we vet all pull requests, including coding style of any contributions; use the same coding style found in the files you are already editing. diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 20421a0bd..7ac0fb9d3 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -67,8 +67,9 @@ ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector # Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html -ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory +# Note that this is now considered deprecated! +#ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory +ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory # To use the new SLF4J logger in ESAPI (see GitHub issue #129), set # ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory # and do whatever other normal SLF4J configuration that you normally would do for your application. @@ -499,3 +500,38 @@ Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ # Validation of dates. Controls whether or not 'lenient' dates are accepted. # See DataFormat.setLenient(boolean flag) for further details. Validator.AcceptLenientDates=false + +# ~~~~~ Important Note ~~~~~ +# This is a workaround to make sure that a commit to address GitHub issue #509 +# doesn't accidentally break someone's production code. So essentially what we +# are doing is to reverting back to the previous possibly buggy (by +# documentation intent at least), but, by now, expected legacy behavior. +# Prior to the code changes for issue #509, if invalid / malicious HTML input was +# observed, AntiSamy would simply attempt to sanitize (cleanse) it and it would +# only be logged. However, the code change made ESAPI comply with its +# documentation, which stated that a ValidationException should be thrown in +# such cases. Unfortunately, changing this behavior--especially when no one is +# 100% certain that the documentation was correct--could break existing code +# using ESAPI so after a lot of debate, issue #521 was created to restore the +# previous behavior, but still allow the documented behavior. (We did this +# because it wasn't really causing an security issues since AntiSamy would clean +# it up anyway and we value backward compatibility as long as it doesn't clearly +# present security vulnerabilities.) +# More defaults about this are written up under GitHub issue #521 and +# the pull request it references. Future major releases of ESAPI (e.g., ESAPI 3.x) +# will not support this previous behavior, but it will remain for ESAPI 2.x. +# Set this to 'throw' if you want the originally intended behavior of throwing +# that was fixed via issue #509. Set to 'clean' if you want want the HTML input +# sanitized instead. +# +# Possible values: +# clean -- Use the legacy behavior where unsafe HTML input is logged and the +# sanitized (i.e., clean) input as determined by AntiSamy and your +# AntiSamy rules is returned. This is the default behavior if this +# new property is not found. +# throw -- The new, presumably correct and originally intended behavior where +# a ValidationException is thrown when unsafe HTML input is +# encountered. +# +#Validator.HtmlValidationAction=clean +Validator.HtmlValidationAction=throw diff --git a/src/main/java/org/owasp/esapi/ValidationRule.java b/src/main/java/org/owasp/esapi/ValidationRule.java index 25cc95ac1..8f186a54b 100644 --- a/src/main/java/org/owasp/esapi/ValidationRule.java +++ b/src/main/java/org/owasp/esapi/ValidationRule.java @@ -15,14 +15,23 @@ public interface ValidationRule { * the value to be parsed * @return a validated value * @throws ValidationException - * if any validation rules fail + * if any validation rules fail, except if the + * {@code ESAPI.properties}> property + * "Validator.ValidationRule.getValid.ignore509Fix" is set to + * {@code true}, which is the default behavior for ESAPI 2.x + * releases. See + * {@link https://github.com/ESAPI/esapi-java-legacy/issues/509} + * and {@link https://github.com/ESAPI/esapi-java-legacy/issues/521} + * for futher details. + * + * @see #getValid(String context, String input, ValidationErrorList errorList) */ Object getValid(String context, String input) throws ValidationException; /** - * Whether or not a valid valid can be null. getValid will throw an - * Exception and getSafe will return the default value if flag is set to + * Whether or not a valid valid can be null. {@code getValid} will throw an + * Exception and {#code getSafe} will return the default value if flag is set to * true * * @param flag @@ -59,7 +68,8 @@ Object getValid(String context, String input, ValidationErrorList errorList) throws ValidationException; /** - * Try to call get valid, then call sanitize, finally return a default value + * Try to call {@code getvalid}, then call a 'sanitize' method for sanitization (if one exists), + * finally return a default value. */ Object getSafe(String context, String input); @@ -78,4 +88,4 @@ Object getValid(String context, String input, */ String whitelist(String input, Set list); -} \ No newline at end of file +} diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index a91e0e9b7..a9fd89cc9 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -114,7 +114,7 @@ public static SecurityConfiguration getInstance() { public static final String DIGITAL_SIGNATURE_ALGORITHM = "Encryptor.DigitalSignatureAlgorithm"; public static final String DIGITAL_SIGNATURE_KEY_LENGTH = "Encryptor.DigitalSignatureKeyLength"; // ==================================// - // New in ESAPI Java 2.0 // + // New in ESAPI Java 2.x // // ================================= // public static final String PREFERRED_JCE_PROVIDER = "Encryptor.PreferredJCEProvider"; public static final String CIPHER_TRANSFORMATION_IMPLEMENTATION = "Encryptor.CipherTransformation"; @@ -157,6 +157,7 @@ public static SecurityConfiguration getInstance() { public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile"; public static final String VALIDATION_PROPERTIES_MULTIVALUED = "Validator.ConfigurationFile.MultiValued"; public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates"; + public static final String VALIDATOR_HTML_VALIDATION_ACTION = "Validator.HtmlValidationAction"; /** * Special {@code System} property that, if set to {@code true}, will diff --git a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java index f0196fc04..0670860d9 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java @@ -97,8 +97,60 @@ public String sanitize( String context, String input ) { return safe; } + /** + * Check whether we want the legacy behavior ("clean") or the presumably intended + * behavior of "throw" for how to treat unsafe HTML input when AntiSamy is invoked. + * This admittedly is an UGLY hack to ensure that issue 509 and its corresponding + * fix in PR #510 does not break existing developer's existing code. Full + * details are described in GitHub issue 521. + * + * Checks new ESAPI property "Validator.HtmlValidationAction". A value of "clean" + * means to revert to legacy behavior. A value of "throw" means to use the new + * behavior as implemented in GitHub issue 509. + * + * @return false - If "Validator.HtmlValidationAction" is set to "throw". Otherwise {@code true}. + * @since 2.2.1.0 + */ + private boolean legacyHtmlValidation() { + boolean legacy = true; // Make legacy support the default behavior for backward compatibility. + String propValue = "clean"; // For legacy support. + try { + // DISCUSS: + // Hindsight: maybe we should have getBooleanProp(), getStringProp(), + // getIntProp() methods that take a default arg as well? + // At least for ESAPI 3.x. + propValue = ESAPI.securityConfiguration().getStringProp( + // Future: This will be moved to a new PropNames class + org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION ); + switch ( propValue.toLowerCase() ) { + case "throw": + legacy = false; // New, presumably correct behavior, as addressed by GitHub issue 509 + break; + case "clean": + legacy = true; // Give the caller that legacy behavior of sanitizing. + break; + default: + LOGGER.warning(Logger.EVENT_FAILURE, "ESAPI property " + + org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION + + " was set to \"" + propValue + "\". Must be set to either \"clean\"" + + " (the default for legacy support) or \"throw\"; assuming \"clean\" for legacy behavior."); + legacy = true; + break; + } + } catch( ConfigurationException cex ) { + // OPEN ISSUE: Should we log this? I think so. Convince me otherwise. But maybe + // we should only log it once or every Nth time?? + LOGGER.warning(Logger.EVENT_FAILURE, "ESAPI property " + + org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION + + " must be set to either \"clean\" (the default for legacy support) or \"throw\"; assuming \"clean\"", + cex); + } + + return legacy; + } + private String invokeAntiSamy( String context, String input ) throws ValidationException { - // CHECKME should this allow empty Strings? " " us IsBlank instead? + // CHECKME should this allow empty Strings? " " use IsBlank instead? if ( StringUtilities.isEmpty(input) ) { if (allowNull) { return null; @@ -114,7 +166,11 @@ private String invokeAntiSamy( String context, String input ) throws ValidationE List errors = test.getErrorMessages(); if ( !errors.isEmpty() ) { - throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + errors.toString()); + if ( legacyHtmlValidation() ) { // See GitHub issues 509 and 521 + LOGGER.info(Logger.SECURITY_FAILURE, "Cleaned up invalid HTML input: " + errors ); + } else { + throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + errors.toString()); + } } return test.getCleanHTML().trim(); diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index ad48ef53b..b0fd24789 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -238,39 +238,8 @@ public void testGetValidRedirectLocation() { // instance.getValidRedirectLocation(String, String, boolean, ValidationErrorList) } - public void testGetValidSafeHTML() throws Exception { - System.out.println("getValidSafeHTML"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - - // new school test case setup - HTMLValidationRule rule = new HTMLValidationRule("test"); - ESAPI.validator().addRule(rule); - - assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. ")); - - String test1 = "Jeff"; - String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors); - assertEquals(test1, result1); - - String test2 = "Aspect Security"; - String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors); - assertEquals(test2, result2); - - String test3 = "Test. "; - assertEquals("Test.", rule.getSafe("test", test3)); - - assertEquals("Test. <
load=alert()
", rule.getSafe("test", "Test. <
load=alert()")); - assertEquals("Test.
b
", rule.getSafe("test", "Test.
b
")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts - // This would be nice to catch, but just looks like text to AntiSamy - // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); - // String result4 = instance.getValidSafeHTML("test", test4); - // assertEquals("", result4); - } + // Test split out and moved to HTMLValidationRuleLogsTest.java & HTMLValidationRuleThrowsTest.java + // public void testGetValidSafeHTML() throws Exception { public void testIsInvalidFilename() { System.out.println("testIsInvalidFilename"); @@ -881,32 +850,8 @@ public void testIsValidRedirectLocation() { // isValidRedirectLocation(String, String, boolean) } - public void testIsValidSafeHTML() { - System.out.println("isValidSafeHTML"); - Validator instance = ESAPI.validator(); - - assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. ", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test.
", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - - // TODO: waiting for a way to validate text headed for an attribute for scripts - // This would be nice to catch, but just looks like text to AntiSamy - // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.isValidSafeHTML("test1", "Jeff", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test2", "Aspect Security", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test3", "Test. ", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test4", "Test.
", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test6", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test7", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(errors.size() == 0); - - } + // Test split out and moved to HTMLValidationRuleLogsTest.java & HTMLValidationRuleThrowsTest.java + // public void testIsValidSafeHTML() { public void testSafeReadLine() { System.out.println("safeReadLine"); diff --git a/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java new file mode 100644 index 000000000..dfed45607 --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java @@ -0,0 +1,158 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2019 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author kevin.w.wall@gmail.com + * @since 2019 + */ +package org.owasp.esapi.reference; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.EncoderConstants; +import org.owasp.esapi.SecurityConfiguration; +import org.owasp.esapi.SecurityConfigurationWrapper; +import org.owasp.esapi.ValidationErrorList; +import org.owasp.esapi.ValidationRule; +import org.owasp.esapi.Validator; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.filters.SecurityWrapperRequest; +import org.owasp.esapi.reference.validation.HTMLValidationRule; + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import static org.junit.Assert.*; + +/** + * The Class HTMLValidationRuleCleanTest. + * + * Based on original test cases, testGetValidSafeHTML() and + * testIsValidSafeHTML() from ValidatorTest by + * Mike Fauzy (mike.fauzy@aspectsecurity.com) and + * Jeff Williams (jeff.williams@aspectsecurity.com) + * that were originally part of src/test/java/org/owasp/esapi/reference/ValidatorTest.java. + * + * This class tests the cases where the new ESAPI.property + * Validator.HtmlValidationAction + * is set to "clean", which causes certain calls to + * ESAPI.validator().getValidSafeHTML() or ESAPI.validator().isValidSafeHTML() + * to simply log a warning and return the cleansed (sanitizied) output rather + * than throwing a ValidationException when certain unsafe input is + * encountered. + */ +public class HTMLValidationRuleCleanTest { + + private static class ConfOverride extends SecurityConfigurationWrapper { + private String desiredReturn = "clean"; + + ConfOverride(SecurityConfiguration orig, String desiredReturn) { + super(orig); + this.desiredReturn = desiredReturn; + } + + @Override + public String getStringProp(String propName) { + // Would it be better making this file a static import? + if ( propName.equals( org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION ) ) { + return desiredReturn; + } else { + return super.getStringProp( propName ); + } + } + } + + + /** + * Instantiates a new HTTP utilities test. + * + * @param testName the test name + */ + public HTMLValidationRuleCleanTest() { + } + + @After + public void tearDown() throws Exception { + ESAPI.override(null); + } + + @Before + public void setUp() throws Exception { + ESAPI.override( + new ConfOverride( ESAPI.securityConfiguration(), "clean" ) + ); + + } + + @Test + public void testGetValidSafeHTML() throws Exception { + System.out.println("getValidSafeHTML"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + + HTMLValidationRule rule = new HTMLValidationRule("test"); + ESAPI.validator().addRule(rule); + + assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. ")); + + String test1 = "Jeff"; + String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors); + assertEquals(test1, result1); + + String test2 = "Aspect Security"; + String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors); + assertEquals(test2, result2); + + String test3 = "Test. Cookie :-)"; + assertEquals("Test. Cookie :-)", rule.getSafe("test", test3)); + + assertEquals("Test. <
load=alert()
", rule.getSafe("test", "Test. <
load=alert()")); + assertEquals("Test.
b
", rule.getSafe("test", "Test.
b
")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts + // This would be nice to catch, but just looks like text to AntiSamy + // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); + // String result4 = instance.getValidSafeHTML("test", test4); + // assertEquals("", result4); + } + + + @Test + public void testIsValidSafeHTML() { + System.out.println("isValidSafeHTML"); + Validator instance = ESAPI.validator(); + + assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. ", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test.
", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + + // TODO: waiting for a way to validate text headed for an attribute for scripts + // This would be nice to catch, but just looks like text to AntiSamy + // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.isValidSafeHTML("test1", "Jeff", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test2", "Aspect Security", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test3", "Test. ", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test4", "Test.
", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test6", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test7", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(errors.size() == 0); + + } +} diff --git a/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java new file mode 100644 index 000000000..6726ef56f --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java @@ -0,0 +1,167 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2019 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author kevin.w.wall@gmail.com + * @since 2019 + */ +package org.owasp.esapi.reference; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.SecurityConfiguration; +import org.owasp.esapi.SecurityConfigurationWrapper; +import org.owasp.esapi.ValidationErrorList; +import org.owasp.esapi.ValidationRule; +import org.owasp.esapi.Validator; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.reference.validation.HTMLValidationRule; + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import static org.junit.Assert.*; + +/** + * The Class HTMLValidationRuleThrowsTest. + * + * Based on original test cases, testGetValidSafeHTML() and + * testIsValidSafeHTML() from ValidatorTest by + * Mike Fauzy (mike.fauzy@aspectsecurity.com) and + * Jeff Williams (jeff.williams@aspectsecurity.com) + * that were originally part of src/test/java/org/owasp/esapi/reference/ValidatorTest.java. + * + * This class tests the cases where the new ESAPI.property + * Validator.HtmlValidationAction + * is set to "throw", which causes certain calls to + * ESAPI.validator().getValidSafeHTML() or ESAPI.validator().isValidSafeHTML() + * to throw a ValidationException rather than simply logging a warning and returning + * the cleansed (sanitizied) output when certain unsafe input is encountered. + */ +public class HTMLValidationRuleThrowsTest { + private static class ConfOverride extends SecurityConfigurationWrapper { + private String desiredReturn = "clean"; + + ConfOverride(SecurityConfiguration orig, String desiredReturn) { + super(orig); + this.desiredReturn = desiredReturn; + } + + @Override + public String getStringProp(String propName) { + // Would it be better making this file a static import? + if ( propName.equals( org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION ) ) { + return desiredReturn; + } else { + return super.getStringProp( propName ); + } + } + } + + // Must be public! + @Rule + public ExpectedException thrownEx = ExpectedException.none(); + + @After + public void tearDown() throws Exception { + ESAPI.override(null); + thrownEx = ExpectedException.none(); + } + + @Before + public void setUp() throws Exception { + ESAPI.override( + new ConfOverride( ESAPI.securityConfiguration(), "throw" ) + ); + + } + + @Test + public void testGetValid() throws Exception { + System.out.println("getValid"); + Validator instance = ESAPI.validator(); + HTMLValidationRule rule = new HTMLValidationRule("test"); + ESAPI.validator().addRule(rule); + + thrownEx.expect(ValidationException.class); + thrownEx.expectMessage("test: Invalid HTML input"); + + instance.getRule("test").getValid("test", "Test. "); + } + + @Test + public void testGetValidSafeHTML() throws Exception { + System.out.println("getValidSafeHTML"); + Validator instance = ESAPI.validator(); + + HTMLValidationRule rule = new HTMLValidationRule("test"); + ESAPI.validator().addRule(rule); + + String[] testInput = { + // These first two don't cause AntiSamy to throw. + // "Test. Aspect Security", + // "Test. <
load=alert()", + "Test. ", + "Test. ", + "Test.
b
", + "Test. alert(document.cookie)", + "Test. alert(document.cookie)", + "Test. alert(document.cookie)" + }; + + int errors = 0; + for( int i = 0; i < testInput.length; i++ ) { + try { + String result = instance.getValidSafeHTML("test", testInput[i], 100, false); + errors++; + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + "' failed to throw."); + } + catch( ValidationException vex ) { + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + "' returned:"); + System.out.println("\t" + i + ": logMsg =" + vex.getLogMessage()); + assertEquals( vex.getUserMessage(), "test: Invalid HTML input"); + } + catch( Exception ex ) { + errors++; + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + + "' threw wrong exception type: " + ex.getClass().getName() ); + } + } + + if ( errors > 0 ) { + fail("testGetValidSafeHTML() encountered " + errors + " failures."); + } + } + + @Test + public void testIsValidSafeHTML() { + System.out.println("isValidSafeHTML"); + Validator instance = ESAPI.validator(); + thrownEx = ExpectedException.none(); // Not expecting any exceptions here. + + assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. ", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test.
", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + + ValidationErrorList errors = new ValidationErrorList(); + assertFalse(instance.isValidSafeHTML("test1", "Test. ", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test2", "Test.
", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test3", "Test. alert(document.cookie)", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test4", "Test. alert(document.cookie)", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue( errors.size() == 5 ); + } +} diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 2537757c1..14b47d32f 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -97,9 +97,9 @@ ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector # Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html -ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory -#ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory -#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory +# Note that this is now considered deprecated! +#ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory +ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory # To use the new SLF4J logger in ESAPI (see GitHub issue #129), set # ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory # and do whatever other normal SLF4J configuration that you normally would do for your application. @@ -529,3 +529,38 @@ Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ # Validation of dates. Controls whether or not 'lenient' dates are accepted. # See DataFormat.setLenient(boolean flag) for further details. Validator.AcceptLenientDates=false + +# ~~~~~ Important Note ~~~~~ +# This is a workaround to make sure that a commit to address GitHub issue #509 +# doesn't accidentally break someone's production code. So essentially what we +# are doing is to reverting back to the previous possibly buggy (by +# documentation intent at least), but, by now, expected legacy behavior. +# Prior to the code changes for issue #509, if invalid / malicious HTML input was +# observed, AntiSamy would simply attempt to sanitize (cleanse) it and it would +# only be logged. However, the code change made ESAPI comply with its +# documentation, which stated that a ValidationException should be thrown in +# such cases. Unfortunately, changing this behavior--especially when no one is +# 100% certain that the documentation was correct--could break existing code +# using ESAPI so after a lot of debate, issue #521 was created to restore the +# previous behavior, but still allow the documented behavior. (We did this +# because it wasn't really causing an security issues since AntiSamy would clean +# it up anyway and we value backward compatibility as long as it doesn't clearly +# present security vulnerabilities.) +# More defaults about this are written up under GitHub issue #521 and +# the pull request it references. Future major releases of ESAPI (e.g., ESAPI 3.x) +# will not support this previous behavior, but it will remain for ESAPI 2.x. +# Set this to 'throw' if you want the originally intended behavior of throwing +# that was fixed via issue #509. Set to 'clean' if you want want the HTML input +# sanitized instead. +# +# Possible values: +# clean -- Use the legacy behavior where unsafe HTML input is logged and the +# sanitized (i.e., clean) input as determined by AntiSamy and your +# AntiSamy rules is returned. This is the default behavior if this +# new property is not found. +# throw -- The new, presumably correct and originally intended behavior where +# a ValidationException is thrown when unsafe HTML input is +# encountered. +# +#Validator.HtmlValidationAction=clean +Validator.HtmlValidationAction=throw From 74492fee25b9640c985da28a91db478f21e6de92 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 9 Feb 2020 00:37:18 -0500 Subject: [PATCH 092/544] Close issue #538 addressing CVE-2019-17571 with security bulletin. --- SECURITY.md | 8 ++++++++ documentation/ESAPI-security-bulletin2.odt | Bin 0 -> 31990 bytes documentation/ESAPI-security-bulletin2.pdf | Bin 0 -> 103931 bytes 3 files changed, 8 insertions(+) create mode 100644 documentation/ESAPI-security-bulletin2.odt create mode 100644 documentation/ESAPI-security-bulletin2.pdf diff --git a/SECURITY.md b/SECURITY.md index f4d5ecb55..bee2b246a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -40,3 +40,11 @@ can understand what needs to be done to fix it. Unfortunately at this time, we are not in a position to pay out bug bounties for vulnerabilities. Eventually, we would like to have BugCrowd handle this, but that's still a ways off. + +## Security Bulletins + +There are some ESAPI security bulletins published in the "documentation" directory on GitHub. +For details see: + +* (Security Bulletin #1 - MAC Bypass in ESAPI Symmetric Encryption)[documentation/ESAPI-security-bulletin1.pdf], which covers CVE-2013-5679 and CVE-2013-5960 +* (Security Bulletin #2 - How Does CVE-2019-17571 Impact ESAPI?)[documentation/ESAPI-security-bulletin2.pdf], which covers the Log4J 1 deserialization CVE. diff --git a/documentation/ESAPI-security-bulletin2.odt b/documentation/ESAPI-security-bulletin2.odt new file mode 100644 index 0000000000000000000000000000000000000000..c65623126fc7cab343fded85c86ac6b1b636ca78 GIT binary patch literal 31990 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 hu8LAtC6J)dh{>{Mu-IV&pn?RhbK0;$T~CAi{{R Date: Sun, 9 Feb 2020 00:53:42 -0500 Subject: [PATCH 093/544] Update SECURITY.md to fix security bulletin links. It must be past my bedtime. Sigh. ;-) --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index bee2b246a..18d510ad9 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -46,5 +46,5 @@ Eventually, we would like to have BugCrowd handle this, but that's still a ways There are some ESAPI security bulletins published in the "documentation" directory on GitHub. For details see: -* (Security Bulletin #1 - MAC Bypass in ESAPI Symmetric Encryption)[documentation/ESAPI-security-bulletin1.pdf], which covers CVE-2013-5679 and CVE-2013-5960 -* (Security Bulletin #2 - How Does CVE-2019-17571 Impact ESAPI?)[documentation/ESAPI-security-bulletin2.pdf], which covers the Log4J 1 deserialization CVE. +* [Security Bulletin #1 - MAC Bypass in ESAPI Symmetric Encryption](documentation/ESAPI-security-bulletin1.pdf), which covers CVE-2013-5679 and CVE-2013-5960 +* [Security Bulletin #2 - How Does CVE-2019-17571 Impact ESAPI?](documentation/ESAPI-security-bulletin2.pdf), which covers the Log4J 1 deserialization CVE. From 5ed5f253e3e9a5135a627942e17f9c5791b09f07 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 9 Feb 2020 21:53:35 +0100 Subject: [PATCH 094/544] upgrade for convergence --- pom.xml | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index 6a34d2cc0..1997e5a00 100644 --- a/pom.xml +++ b/pom.xml @@ -364,7 +364,7 @@ org.powermock powermock-reflect - 2.0.0 + 2.0.2 test @@ -526,36 +526,25 @@ - enforce-bytecode-version enforce + + + 1.7 + + ESAPI 2.x now uses the JDK1.7 for it's baseline. Please make sure that your + JAVA_HOME environment variable is pointed to a JDK1.7 distribution. + + 1.7 - true - - enforce-jdk-version - - enforce - - - - - 1.7 - - ESAPI 2.x now uses the JDK1.7 for it's baseline. Please make sure that your - JAVA_HOME environment variable is pointed to a JDK1.7 distribution. - - - - - From e179580e133c03044fd6c0adfc93d335afb98549 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 9 Feb 2020 22:25:30 +0100 Subject: [PATCH 095/544] new way for getting file content from filesystem --- .../reference/ExtensiveEncoderURITest.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java index 37e9ea213..36bd7c116 100644 --- a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java +++ b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java @@ -2,10 +2,12 @@ import static org.junit.Assert.assertEquals; -import java.io.File; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -34,14 +36,24 @@ public ExtensiveEncoderURITest(String uri){ @Parameters public static Collection getMyUris() throws Exception{ URL url = ExtensiveEncoderURITest.class.getResource("/urisForTest.txt"); - String fileName = url.getFile(); - File urisForText = new File(fileName); - - inputs = Files.readAllLines(urisForText.toPath(), StandardCharsets.UTF_8); + try( InputStream is = url.openStream() ) { + InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); + BufferedReader br = new BufferedReader(isr); + inputs = readAllLines(br); + } return inputs; } - + + private static List readAllLines(BufferedReader br) throws IOException { + List lines = new ArrayList<>(); + String line; + while ((line = br.readLine()) != null) { + lines.add(line); + } + return lines; + } + @Test public void testUrlsFromFile() throws Exception{ assertEquals(this.expected, v.isValidURI("URL", uri, false)); From c951b1cd05112bf9760d3cd3d08c842c418c443a Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 9 Feb 2020 23:32:58 +0100 Subject: [PATCH 096/544] update for reading configuration from file path with spaces --- .../DefaultSecurityConfiguration.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index a9fd89cc9..d578850ba 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -20,6 +20,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -581,13 +582,17 @@ public File getResourceFile(String filename) { } if (fileUrl != null) { - String fileLocation = fileUrl.getFile(); - f = new File(fileLocation); - if (f.exists()) { - logSpecial("Found in SystemResource Directory/resourceDirectory: " + f.getAbsolutePath()); - return f; - } else { - logSpecial("Not found in SystemResource Directory/resourceDirectory (this should never happen): " + f.getAbsolutePath()); + try { + String fileLocation = fileUrl.toURI().getPath(); + f = new File(fileLocation); + if (f.exists()) { + logSpecial("Found in SystemResource Directory/resourceDirectory: " + f.getAbsolutePath()); + return f; + } else { + logSpecial("Not found in SystemResource Directory/resourceDirectory (this should never happen): " + f.getAbsolutePath()); + } + } catch (URISyntaxException e) { + logSpecial("Error while converting URL " + fileUrl + " to file path: " + e.getMessage()); } } else { logSpecial("Not found in SystemResource Directory/resourceDirectory: " + resourceDirectory + File.separator + filename); From fd1650d4fd8da129843a1346811f26dc43466de7 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 17 Feb 2020 14:14:54 -0500 Subject: [PATCH 097/544] Update pom.xml SNAPSHOT version Changed from 2.3.0.0-SNAPSHOT to 2.2.1.0-SNAPSHOT. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1997e5a00..881b59c5e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.3.0.0-SNAPSHOT + 2.2.1.0-SNAPSHOT jar From f8180f59c636e90a9677deb9c29afae2f1f7c665 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 17 Feb 2020 20:42:13 -0500 Subject: [PATCH 098/544] Drop 'failBuildOnCVSS' from 5.9 to 5.0. Corrected spelling of 'suppressionFiles'. (Old value still worked.) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 881b59c5e..132075ffd 100644 --- a/pom.xml +++ b/pom.xml @@ -689,8 +689,8 @@ dependency-check-maven 5.0.0 - 5.9 - ./suppressions.xml + 5.0 + ./suppressions.xml From 448c8f3a1feb517ba07880e709ea4ed37ab447ee Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 17 Feb 2020 20:45:04 -0500 Subject: [PATCH 099/544] Added suppression of CVE-2019-17571; deleted suppression of CVE-2016-1000031. --- suppressions.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/suppressions.xml b/suppressions.xml index 181b75909..ebedb9648 100644 --- a/suppressions.xml +++ b/suppressions.xml @@ -1,10 +1,22 @@ - + + - .*\bcommons-fileupload-1.3.2.jar - CVE-2016-1000031 + ^log4j:log4j:1\.2\.17$ + cpe:/a:apache:log4j + CVE-2019-17571 - \ No newline at end of file + From 7aafaeeb3476c86886756699501f09963b8657f8 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 23 Feb 2020 18:12:58 +0100 Subject: [PATCH 100/544] additional time for windows to always sleep more than given seconds --- src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java index 8f43d3b1b..0d2b8a519 100644 --- a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java @@ -391,7 +391,9 @@ public final void testAddandGetAttributes() { private static void nap(int n) { try { System.out.println("Sleeping " + n + " seconds..."); - Thread.sleep( n * 1000 ); + // adds additional time to make sure we sleep more than n seconds + int additionalTimeToSleep = 100; + Thread.sleep( n * 1000 + additionalTimeToSleep ); } catch (InterruptedException e) { ; // Ignore } From b479d5908d10100a32355c9b9cde05964d4075f0 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 23 Feb 2020 19:16:36 +0100 Subject: [PATCH 101/544] inline reader for try-with-resource --- .../org/owasp/esapi/reference/ExtensiveEncoderURITest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java index 36bd7c116..0283492c7 100644 --- a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java +++ b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java @@ -36,10 +36,8 @@ public ExtensiveEncoderURITest(String uri){ @Parameters public static Collection getMyUris() throws Exception{ URL url = ExtensiveEncoderURITest.class.getResource("/urisForTest.txt"); - - try( InputStream is = url.openStream() ) { - InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); - BufferedReader br = new BufferedReader(isr); + + try( BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) ) { inputs = readAllLines(br); } return inputs; From 048fc0c40549cd86b28d0ac5ef89cbfdc4cbb289 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 23 Feb 2020 19:17:42 +0100 Subject: [PATCH 102/544] removing not needed code --- .../org/owasp/esapi/reference/ExtensiveEncoderURITest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java index 0283492c7..18d88c79e 100644 --- a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java +++ b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java @@ -4,7 +4,6 @@ import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -22,7 +21,7 @@ @RunWith(Parameterized.class) public class ExtensiveEncoderURITest { - static List inputs = new ArrayList(); + static List inputs = new ArrayList<>(); Validator v = ESAPI.validator(); String uri; boolean expected; @@ -53,7 +52,7 @@ private static List readAllLines(BufferedReader br) throws IOException { } @Test - public void testUrlsFromFile() throws Exception{ + public void testUrlsFromFile() { assertEquals(this.expected, v.isValidURI("URL", uri, false)); } From 7003acc1da17eb68bc92c97a2b7d47a093021385 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Sun, 23 Feb 2020 17:17:02 -0500 Subject: [PATCH 103/544] Updated README.md to refer to config jar Updated 2.2.0.0 release on GitHub and added jar and signature for ESAPI default config files. This README.md update just references that. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f7c38d99d..fd3e2d5bf 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ The default branch for ESAPI legacy is now the 'develop' branch (rather than the # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java +# Locating ESAPI Jar files +The latest ESAPI 2.2.0.0 default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. + +The latest regular ESAPI jars can are available from Maven Central. + + # ESAPI Deprecation Policy Unless we unintentionally screw-up, our intent is to keep classes, methods, and/or fields whihc have been annotated as "@deprecated" for a minimum of two (2) years or until the next major release number (e.g., 3.x as of now), which ever comes first, before we remove them. Note that this policy does not apply to classes under the **org.owasp.esapi.reference** package. You are not expected to be using such classes directly in your code. From e007ec83f5169a7f75ba63354a63c697af685fe2 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 25 Feb 2020 22:22:57 -0500 Subject: [PATCH 104/544] Update to include link to the latest release. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fd3e2d5bf..68037a5d2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ OWASP ESAPI (The OWASP Enterprise Security API) is a free, open source, web appl # What does Legacy mean? -

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. +

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. IMPORTANT NOTE: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. @@ -24,7 +24,7 @@ The default branch for ESAPI legacy is now the 'develop' branch (rather than the https://github.com/ESAPI/esapi-java # Locating ESAPI Jar files -The latest ESAPI 2.2.0.0 default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. +The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.0.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. The latest regular ESAPI jars can are available from Maven Central. From d26b98f11d0d1bacc66df36d7c659c822bb3fba7 Mon Sep 17 00:00:00 2001 From: Bill Sempf Date: Tue, 19 May 2020 00:01:59 -0400 Subject: [PATCH 105/544] Release notes for 2.2.1.0 (#543) I have no idea how to associate this with Issue 542 but if I figure it out I will do it. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 documentation/esapi4java-core-2.2.1.0-release-notes.txt diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt new file mode 100644 index 000000000..7f91e07a5 --- /dev/null +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -0,0 +1,115 @@ +Release notes for ESAPI 2.2.1.0 + Release date: 2020-May-12 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.0.0, 2019-June-24 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + + TBD + +================================================================================================================= + +Basic ESAPI facts + +ESAPI 2.2.0.0 release: + 194 Java source files + 4150 JUnit tests in 118 Java source files + +ESAPI 2.2.1.0 release: + TBD + +GitHub Issues fixed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +143 Enchance encodeForOS to auto-detect the underling OS +226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() +245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch +256 White space clean up +382 Build Fails on path with space +494 Encoder's encodeForCSS doesn't handle RGB Triplets +503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` +509 HTMLValidationRule.getValid(String,String) does not follow documented specifications +511 Add missing documentation to Validator.addRule() and Validator.getRule() +512 Update Apache Commons Bean Utils to 1.9.4 +515 Adding tests for getCookies (also 516) +519 Issue 494 CSSCodec RGB Triplets +530 Log Bridge Tests +536 Various fixes +538 Addressing log4j 1.x CVE-2019-17571 + +----------------------------------------------------------------------------- + + Changes requiring special attention + +----------------------------------------------------------------------------- + +TBD + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +Documentation updates for locating Jar files +Unneeded code removed from ExtensiveEncoder +Inline reader added to ExtensiveEncoder +Additional time for windows to always sleep more than given seconds in CryptoTokenTest +Change required by tweak to CipherText.toString() method +Removed call to deprecated CryptoHelper.computeDerivedKey() method +New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class +Use existing toString method rather than a StringBuilder +Documentation and tests +JavaLogger move +Splitting user infor from Client Supplier + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) +Generated manually (this time) + +Developer Total commits Total Number + of Files Changed +===================================================== +jeremiahjstacey 11 68 +kwwall 15 26 +wiitek 3 6 +xeno6696 8 9 +Michael-Ziluck 2 3 +===================================================== + +----------------------------------------------------------------------------- + +53 Closed PRs since 2.2.0.0 release +=================================== +504 New scripts to suppress noise for 'mvn test' +510 Resolve #509 - Properly throw exception when HTML fails +513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ +519 Issue 494 CSSCodec RGB Triplets +520 OS Name DefaultExecutorTests #143 +540 Issue 382: Build Fails on path with space +596 Closes Issue 245 + +----------------------------------------------------------------------------- + +Notice: + + Release notes written by Bill Sempf (bill.sempf@owasp.org) please direct any communication to me. + +Project co-leaders + Kevin W. Wall (kwwall) + Matt Seil (xeno6696) + +Special shout-outs to: + Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire + Dave Wichers (davewichers) - for Maven Central / Sonatype help + +Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. + From 482194884f5db00b8d22bfde6e5a8823374dd128 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 May 2020 00:03:59 -0400 Subject: [PATCH 106/544] Update release date to 2020-TBD Need to wait until other PR is merged. --- documentation/esapi4java-core-2.2.1.0-release-notes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index 7f91e07a5..bb46556b8 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.2.1.0 - Release date: 2020-May-12 + Release date: 2020-TBD Project leaders: -Kevin W. Wall -Matt Seil From 90d3d0d9ba48c6b372980cdded869faf31fd59e7 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 17:25:09 -0400 Subject: [PATCH 107/544] Fix Javadoc --- src/main/java/org/owasp/esapi/codecs/HashTrie.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/HashTrie.java b/src/main/java/org/owasp/esapi/codecs/HashTrie.java index 4c8d41910..fef14aee7 100644 --- a/src/main/java/org/owasp/esapi/codecs/HashTrie.java +++ b/src/main/java/org/owasp/esapi/codecs/HashTrie.java @@ -252,8 +252,7 @@ Entry getLongestMatch(CharSequence key, int pos) /** * Recursively lookup the longest key match. * @param keyIn Where to read the key from - * @param pos The position in the key that is being - * looked up at this level. + * @param key The key that is being looked up at this level. * @return The Entry associated with the longest key * match or null if none exists. */ From 9be40d4055c0bc186845e8679c72cf4b476ad710 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 17:27:51 -0400 Subject: [PATCH 108/544] Fix Javadoc --- src/main/java/org/owasp/esapi/crypto/CipherText.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java index 8e57f03e7..1047116fa 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -804,7 +804,8 @@ protected boolean canEqual(Object other) { * proved in 1996 [see http://pssic.free.fr/Extra%20Reading/SEC+/SEC+/hmac-cb.pdf] that * HMAC security doesn’t require that the underlying hash function be collision resistant, * but only that it acts as a pseudo-random function, which SHA1 satisfies. - * @param ciphertext The ciphertext value for which the MAC is computed. + * @param authKey The {@Code SecretKey} used with the computed HMAC-SHA1 + * to ensure authenticity. * @return The value for the MAC. */ private byte[] computeMAC(SecretKey authKey) { From 23608fc130683b1ec1b3efea9331be92a815272b Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 17:59:19 -0400 Subject: [PATCH 109/544] Converted String array explicity to string, which is probably what was intended in exception messages. Otherwise end up with something that looks like '[Ljava.lang.String;@7825ed4c' which is not very helpful. --- .../esapi/reference/accesscontrol/DelegatingACR.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java index 07af135d7..65cb0ebb9 100644 --- a/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java +++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java @@ -4,6 +4,7 @@ import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.Vector; +import java.util.Arrays; import org.apache.commons.collections4.iterators.ArrayListIterator; @@ -25,12 +26,12 @@ public void setPolicyParameters(DynaBeanACRParameter policyParameter) { } catch (SecurityException e) { throw new IllegalArgumentException(e.getMessage() + " delegateClass.delegateMethod(parameterClasses): \"" + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ")\" must be public.", e); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e.getMessage() + " delegateClass.delegateMethod(parameterClasses): \"" + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ")\" does not exist.", e); } @@ -42,14 +43,14 @@ public void setPolicyParameters(DynaBeanACRParameter policyParameter) { throw new IllegalArgumentException( " Delegate class \"" + delegateClassName + "\" must be concrete, because method " + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ") is not static.", ex); } catch (IllegalAccessException ex) { new IllegalArgumentException( " Delegate class \"" + delegateClassName + "\" must must have a zero-argument constructor, because " + "method delegateClass.delegateMethod(parameterClasses): \"" + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ")\" is not static.", ex); } } else { From 1531303c2d5f0904740124031043f552d77c351e Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 18:09:03 -0400 Subject: [PATCH 110/544] Add missing space in exception message. --- src/main/java/org/owasp/esapi/util/ObjFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/util/ObjFactory.java b/src/main/java/org/owasp/esapi/util/ObjFactory.java index 2c23e5f00..7853ef593 100644 --- a/src/main/java/org/owasp/esapi/util/ObjFactory.java +++ b/src/main/java/org/owasp/esapi/util/ObjFactory.java @@ -92,7 +92,7 @@ public static T make(String className, String typeName) throws Configuration // The class is meant to be singleton, however, the SecurityManager restricts us from calling the // getInstance method on the class, thus this is a configuration issue and a ConfigurationException // is thrown - throw new ConfigurationException( "The SecurityManager has restricted the object factory from getting a reference to the singleton implementation" + + throw new ConfigurationException( "The SecurityManager has restricted the object factory from getting a reference to the singleton implementation " + "of the class [" + className + "]", e ); } From b8c48a9c22317eff3ac52003de9b6eaecce0f7de Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 18:10:40 -0400 Subject: [PATCH 111/544] Make conversion from String array to String explicit for exception message. --- src/main/java/org/owasp/esapi/reference/DefaultValidator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index abe6c93a5..530e2efa8 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -23,6 +23,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.text.DateFormat; +import java.util.Arrays; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -764,7 +765,7 @@ public boolean isValidFileContent(String context, byte[] input, int maxBytes, bo public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException { if (isEmpty(input)) { if (allowNull) return null; - throw new ValidationException( context + ": Input required", "Input required: context=" + context + ", input=" + input, context ); + throw new ValidationException( context + ": Input required", "Input required: context=" + context + ", input=" + Arrays.toString(input), context ); } long esapiMaxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize(); From 120ff02fe530ac2933d36253316cf1e208d13023 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 7 Jun 2020 13:27:52 -0400 Subject: [PATCH 112/544] Remove references to WAF aliases. Feature not fullyed developed. Issue identifed by LGTM. Checked with Arshan and he confirmed it should be removed. --- configuration/esapi/waf-policy.xsd | Bin 20582 -> 19482 bytes .../AppGuardianConfiguration.java | 10 -------- .../configuration/ConfigurationParser.java | 22 +----------------- 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/configuration/esapi/waf-policy.xsd b/configuration/esapi/waf-policy.xsd index 4287e792b508ce0b8010b83473965465fca10b2d..1ddc99039e59d5d626245497c0e53a33e74f38f1 100644 GIT binary patch delta 28 kcmaF1fN|Cg#toHBn-!U*94FVyv2A|oUdzG4z{|h|0GodZ(EtDd delta 128 zcmbO=gYnq{#toHBoQVuM44Djx48@Z_u#57-c?_vQkpzd}&HhX-j!26B$<1bmDw_O( yMU)+;Sx?M)vOaGTOm33A_~iA@Rg+mw%_iR#u>jH)lPk^kP5v*=v3a(0H3tBCASYA+ diff --git a/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java b/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java index 36f5239fe..53e6c3404 100644 --- a/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java +++ b/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java @@ -77,11 +77,6 @@ public class AppGuardianConfiguration { public static final String JAVASCRIPT_TARGET_TOKEN = "##1##"; public static final String JAVASCRIPT_REDIRECT = ""; - /* - * The aliases declared in the beginning of the config file. - */ - private HashMap aliases; - /* * Fail response settings. */ @@ -114,8 +109,6 @@ public AppGuardianConfiguration() { afterBodyRules = new ArrayList(); beforeResponseRules = new ArrayList(); cookieRules = new ArrayList(); - - aliases = new HashMap(); } /* @@ -160,9 +153,6 @@ public void setDefaultResponseCode(int defaultResponseCode) { this.defaultResponseCode = defaultResponseCode; } - public void addAlias(String key, Object obj) { - aliases.put(key, obj); - } public List getBeforeBodyRules() { return beforeBodyRules; diff --git a/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java b/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java index 4531ed058..cc429d182 100644 --- a/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java +++ b/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java @@ -94,7 +94,6 @@ public static AppGuardianConfiguration readConfigurationFile(InputStream stream, doc = parser.build(stream); root = doc.getRootElement(); - Element aliasesRoot = root.getFirstChildElement("aliases"); Element settingsRoot = root.getFirstChildElement("settings"); Element authNRoot = root.getFirstChildElement("authentication-rules"); Element authZRoot = root.getFirstChildElement("authorization-rules"); @@ -106,30 +105,11 @@ public static AppGuardianConfiguration readConfigurationFile(InputStream stream, Element beanShellRoot = root.getFirstChildElement("bean-shell-rules"); - /** - * Parse the 'aliases' section. - */ - if ( aliasesRoot != null ) { - Elements aliases = aliasesRoot.getChildElements("alias"); - - for(int i=0;i section is required"); + throw new ConfigurationException("", "The section is required"); } else if ( settingsRoot != null ) { From 9fb1ff81b9ced9108e64ec79661d958d28dd293a Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 7 Jun 2020 16:53:10 -0400 Subject: [PATCH 113/544] Address LGTM alerts about failure to use 'secure' cookies. --- .../esapi/filters/SecurityWrapperRequest.java | 92 +++++++++---------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java index 910733d99..939f572b6 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java @@ -64,11 +64,11 @@ public class SecurityWrapperRequest extends HttpServletRequestWrapper implements * @param request The {@code HttpServletRequest} we are wrapping. */ public SecurityWrapperRequest(HttpServletRequest request) { - super( request ); + super( request ); } private HttpServletRequest getHttpServletRequest() { - return (HttpServletRequest)super.getRequest(); + return (HttpServletRequest)super.getRequest(); } /** @@ -128,8 +128,8 @@ public String getContentType() { public String getContextPath() { String path = getHttpServletRequest().getContextPath(); SecurityConfiguration sc = ESAPI.securityConfiguration(); - //Return empty String for the ROOT context - if (path == null || "".equals(path.trim())) return ""; + //Return empty String for the ROOT context + if (path == null || "".equals(path.trim())) return ""; String clean = ""; try { @@ -159,7 +159,7 @@ public Cookie[] getCookies() { int maxAge = c.getMaxAge(); String domain = c.getDomain(); String path = c.getPath(); - + Cookie n = new Cookie(name, value); n.setMaxAge(maxAge); @@ -347,7 +347,7 @@ public String getParameter(String name) { * @return The "scrubbed" parameter value. */ public String getParameter(String name, boolean allowNull) { - SecurityConfiguration sc = ESAPI.securityConfiguration(); + SecurityConfiguration sc = ESAPI.securityConfiguration(); return getParameter(name, allowNull, sc.getIntProp("HttpUtilities.httpQueryParamValueLength"), "HTTPParameterValue"); } @@ -423,7 +423,7 @@ public Enumeration getParameterNames() { Enumeration en = getHttpServletRequest().getParameterNames(); while (en.hasMoreElements()) { try { - SecurityConfiguration sc = ESAPI.securityConfiguration(); + SecurityConfiguration sc = ESAPI.securityConfiguration(); String name = (String) en.nextElement(); String clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", sc.getIntProp("HttpUtilities.httpQueryParamNameLength"), true); v.add(clean); @@ -446,8 +446,8 @@ public String[] getParameterValues(String name) { String[] values = getHttpServletRequest().getParameterValues(name); List newValues; - if(values == null) - return null; + if(values == null) + return null; newValues = new ArrayList(); SecurityConfiguration sc = ESAPI.securityConfiguration(); for (String value : values) { @@ -469,7 +469,7 @@ public String[] getParameterValues(String name) { */ public String getPathInfo() { String path = getHttpServletRequest().getPathInfo(); - if (path == null) return null; + if (path == null) return null; String clean = ""; SecurityConfiguration sc = ESAPI.securityConfiguration(); try { @@ -681,15 +681,15 @@ public String getServerName() { * HttpServletRequest after parsing and checking the range 0-65536. * @return The local server port */ - public int getServerPort() { - int port = getHttpServletRequest().getServerPort(); - if ( port < 0 || port > 0xFFFF ) { - logger.warning( Logger.SECURITY_FAILURE, "HTTP server port out of range: " + port ); - port = 0; - } - return port; - } - + public int getServerPort() { + int port = getHttpServletRequest().getServerPort(); + if ( port < 0 || port > 0xFFFF ) { + logger.warning( Logger.SECURITY_FAILURE, "HTTP server port out of range: " + port ); + port = 0; + } + return port; + } + /** * Returns the server path from the HttpServletRequest after canonicalizing @@ -710,52 +710,44 @@ public String getServletPath() { /** * Returns a session, creating it if necessary, and sets the HttpOnly flag - * on the Session ID cookie. + * on the Session ID cookie. The 'secure' flag is also set if the property + * {@Code HttpUtilities.ForceSecureCookies} is set to {@Code true} in the ESAPI.properties file. * @return The current session */ public HttpSession getSession() { - HttpSession session = getHttpServletRequest().getSession(); - - // send a new cookie header with HttpOnly on first and second responses - if (ESAPI.securityConfiguration().getBooleanProp("HttpUtilities.ForceHttpOnlySession")) { - if (session.getAttribute("HTTP_ONLY") == null) { - session.setAttribute("HTTP_ONLY", "set"); - Cookie cookie = new Cookie(ESAPI.securityConfiguration().getStringProp("HttpUtilities.HttpSessionIdName"), session.getId()); - cookie.setPath( getHttpServletRequest().getContextPath() ); - cookie.setMaxAge(-1); // session cookie - HttpServletResponse response = ESAPI.currentResponse(); - if (response != null) { - ESAPI.currentResponse().addCookie(cookie); - } - } - } - return session; + return getSession(true); } /** - * Returns a session, creating it if necessary, and sets the HttpOnly flag - * on the Session ID cookie. - * @param create Create a new session if one doesn't exist + * Returns the current session associated with this request or, if there is no current session and + * {@Code create} is {@Code true}, returns a new session and sets the HttpOnly flag on the session ID cookie. + * The 'secure' flag is also set if the property {@Code HttpUtilities.ForceSecureCookies} is set to + * {@Code true} in the ESAPI.properties file. + * @param create If set to {@Code true}, create a new session if one doesn't exist, otherwise return {@Code null} * @return The current session */ public HttpSession getSession(boolean create) { HttpSession session = getHttpServletRequest().getSession(create); + if (session == null) { return null; } + SecurityConfiguration sc = ESAPI.securityConfiguration(); + // send a new cookie header with HttpOnly on first and second responses - if (ESAPI.securityConfiguration().getBooleanProp("HttpUtilities.ForceHttpOnlySession")) { - if (session.getAttribute("HTTP_ONLY") == null) { - session.setAttribute("HTTP_ONLY", "set"); - Cookie cookie = new Cookie(ESAPI.securityConfiguration().getStringProp("HttpUtilities.HttpSessionIdName"), session.getId()); - cookie.setMaxAge(-1); // session cookie - cookie.setPath( getHttpServletRequest().getContextPath() ); - HttpServletResponse response = ESAPI.currentResponse(); - if (response != null) { - ESAPI.currentResponse().addCookie(cookie); - } - } + if ( sc.getBooleanProp("HttpUtilities.ForceHttpOnlySession") ) { + if (session.getAttribute("HTTP_ONLY") == null) { + session.setAttribute("HTTP_ONLY", "set"); + Cookie cookie = new Cookie( sc.getStringProp("HttpUtilities.HttpSessionIdName"), session.getId() ); + cookie.setMaxAge(-1); // session cookie + cookie.setPath( getHttpServletRequest().getContextPath() ); + cookie.setSecure( sc.getBooleanProp("HttpUtilities.ForceSecureCookies") ); + HttpServletResponse response = ESAPI.currentResponse(); + if (response != null) { + ESAPI.currentResponse().addCookie(cookie); + } + } } return session; } From e700d20f5d3b3bdb7f4323b3174ca64468385c23 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 7 Jun 2020 20:31:38 -0400 Subject: [PATCH 114/544] Reorganized one of the tests and added some new test cases. --- .../owasp/esapi/crypto/CryptoTokenTest.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java index 0d2b8a519..52c60c6ee 100644 --- a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java @@ -288,34 +288,42 @@ public final void testSetAndGetAttribute() { // where attribute values contains each of the values that will // be quoted, namely: '\', '=', and ';' try { + String[] attrValues = { "\\", ";", "=", "foo=", "bar\\=", "foobar=\"" }; String complexValue = "kwwall;1291183520293;abc=x=yx;xyz=;efg=a;a;;bbb=quotes\\tuff"; - - ctok.setAttribute("..--__", ""); // Ugly, but legal attr name; empty is legal value. - ctok.setAttribute("attr1", "\\"); - ctok.setAttribute("attr2", ";"); - ctok.setAttribute("attr3", "="); + ctok.setAttribute("complexAttr", complexValue); + ctok.setAttribute("..--__", ""); // Ugly weird but legal attr name; empty is legal value. + + for ( int i = 0; i < attrValues.length; i++ ) { + String attrName = "attr" + i; + String attrVal = attrValues[i]; + + ctok.setAttribute( attrName, attrVal ); + } + String tokenVal = ctok.getToken(); assertNotNull("tokenVal should not be null", tokenVal); CryptoToken ctok2 = new CryptoToken(tokenVal); + String weirdAttr = ctok2.getAttribute("..--__"); assertTrue("Expecting empty string for value of weird attr, but got: " + weirdAttr, weirdAttr.equals("")); - String attr1 = ctok2.getAttribute("attr1"); - assertTrue("attr1 has unexpected value of " + attr1, attr1.equals("\\") ); - - String attr2 = ctok2.getAttribute("attr2"); - assertTrue("attr2 has unexpected value of " + attr2, attr2.equals(";") ); - - String attr3 = ctok2.getAttribute("attr3"); - assertTrue("attr3 has unexpected value of " + attr3, attr3.equals("=") ); - String complexAttr = ctok2.getAttribute("complexAttr"); assertNotNull(complexAttr); assertTrue("complexAttr has unexpected value of " + complexAttr, complexAttr.equals(complexValue) ); + for ( int i = 0; i < attrValues.length; i++ ) { + String attrName = "attr" + i; + String attrVal = attrValues[i]; + String retrieved = ctok2.getAttribute( attrName ); + String assertMsg = "Exptected attribute '" + attrName + "' to have value of '" + attrVal + "', but had value of '" + retrieved; + + assertTrue( assertMsg, attrVal.equals( attrVal ) ); + ctok.setAttribute( attrName, attrVal ); + } + } catch (ValidationException e) { fail("Caught unexpected ValidationException: " + e); } catch (Exception e) { From e1b634e81606b1a2c7cd60b1251af13ee1831511 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:08:52 -0400 Subject: [PATCH 115/544] Add registered trademark to first 'OWASP' referene. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68037a5d2..155838fae 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Enterprise Security API for Java (Legacy)
-OWASP ESAPI (The OWASP Enterprise Security API) is a free, open source, web application security control library that makes it easier for programmers to write lower-risk applications. The ESAPI for Java library is designed to make it easier for programmers to retrofit security into existing applications. ESAPI for Java also serves as a solid foundation for new development. +OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web application security control library that makes it easier for programmers to write lower-risk applications. The ESAPI for Java library is designed to make it easier for programmers to retrofit security into existing applications. ESAPI for Java also serves as a solid foundation for new development.
@@ -85,3 +85,6 @@ Old archives for the old Mailman mailing lists for ESAPI-Users and ESAPI-Dev are For a general overview of Google Groups and its web interface, see https://groups.google.com/forum/#!overview For assistance subscribing and unsubscribing to Google Groups, see https://webapps.stackexchange.com/questions/13508/how-can-i-subscribe-to-a-google-mailing-list-with-a-non-google-e-mail-address/15593#15593 + +---------- +OWASP is a registered trademark of The OWASP Foundation. From 8eb9d0fa5528aadbdf474908f18cb4b27ee70df4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:10:55 -0400 Subject: [PATCH 116/544] Update Jaavdoc. --- src/main/java/org/owasp/esapi/ESAPI.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/owasp/esapi/ESAPI.java b/src/main/java/org/owasp/esapi/ESAPI.java index 33eca3fcc..e11239c75 100644 --- a/src/main/java/org/owasp/esapi/ESAPI.java +++ b/src/main/java/org/owasp/esapi/ESAPI.java @@ -93,6 +93,8 @@ public static Authenticator authenticator() { } /** + * The ESAPI Encoder is primarilly used to provide output encoding to + * prevent Cross-Site Scripting (XSS). * @return the current ESAPI Encoder object being used to encode and decode data for this application. */ public static Encoder encoder() { From 56b4aa68c6dafa6b85f6d187756bad1f4da75074 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:11:18 -0400 Subject: [PATCH 117/544] Update Javadoc. --- src/main/java/org/owasp/esapi/Encoder.java | 106 +++++++++++++++++---- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index ca82238be..b89c85e82 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -23,28 +23,102 @@ /** - * The Encoder interface contains a number of methods for decoding input and encoding output - * so that it will be safe for a variety of interpreters. To prevent - * double-encoding, callers should make sure input does not already contain encoded characters - * by calling canonicalize. Validator implementations should call canonicalize on user input - * before validating to prevent encoded attacks. + * The {@code Encoder} interface contains a number of methods for decoding input and encoding output + * so that it will be safe for a variety of interpreters. Its primary use is to + * provide output encoding to prevent XSS. *

- * All of the methods must use a "whitelist" or "positive" security model. - * For the encoding methods, this means that all characters should be encoded, except for a specific list of - * "immune" characters that are known to be safe. - *

- * The Encoder performs two key functions, encoding and decoding. These functions rely + * To prevent double-encoding, callers should make sure input does not already contain encoded characters + * by calling one of the {@code canonicalize()} methods. Validator implementations should call + * {@code canonicalize()} on user input before validating to prevent encoded attacks. + *

+ * All of the methods must use an "allow list" or "positive" security model rather + * than a "deny list" or "negative" security model. For the encoding methods, this means that + * all characters should be encoded, except for a specific list of "immune" characters that are + * known to be safe. + *

+ * The {@code Encoder} performs two key functions, encoding and decoding. These functions rely * on a set of codecs that can be found in the org.owasp.esapi.codecs package. These include: - *

  • CSS Escaping
  • + *
      + *
    • CSS Escaping
    • *
    • HTMLEntity Encoding
    • *
    • JavaScript Escaping
    • - *
    • MySQL Escaping
    • - *
    • Oracle Escaping
    • + *
    • MySQL Database Escaping
    • + *
    • Oracle Database Escaping
    • *
    • Percent Encoding (aka URL Encoding)
    • - *
    • Unix Escaping
    • + *
    • Unix Shell Escaping
    • *
    • VBScript Escaping
    • - *
    • Windows Encoding
    - *

    + *

  • Windows Cmd Escaping
  • + *
  • LDAP Escaping
  • + *
  • XML and XML Attribute Encoding
  • + *
  • XPath Escaping
  • + *
  • Base64 Encoding
  • + *
+ *

+ * Note that in addition to these encoder methods, ESAPI also provides a JSP Tag + * Library ({@code META-INF/esapi.tld}) in the ESAPI jar. This allows one to use + * the more convenient JSP tags in JSPs. These * tags are simply wrappers for the + * various "encodeForXXYZ()" methods. + *

+ * Some important final words: + *

    + *
  • Where to output encode: + * Knowing where to place the output encoding in your code + * is just as important as knowing which context (HTML, HTML attribute, CSS, + * JavaScript, or URL) to use for the output encoding and surprisingly the two + * are often related. In general, output encoding should be done just prior to the + * output being rendered because that is what determines what the appropriate + * context is for the output encoding. In fact, doing output encoding on + * untrusted data that is stored and to be used later--whether stored in an HTTP + * session or in a database--is almost always considered an anti-pattern. An + * example of this is one gathers and stores some untrusted data item such as an + * email address from a user. A developer thinks "let's output encode this and + * store the encoded data in the database, thus making the untrusted data safe + * to use, thus saving us all the encoding troubles later on". On the surface, + * that sounds like a reasonable approach. The problem is how to know what + * output encoding to use, not only for now, but for all possible future + * uses? It might be that the current application code base is only using it in + * an HTML contexxt that is displayed in an HTML report or shown in an HTML + * context in the user's profile. But what it it is later used in a mailto: URL? + * Then instead of HTML encoding, it would need to have URL encoding. Similarly, + * what if there is a later switch made to use AJAX and the untrusted email + * address gets used in a JavaScript context? The complication is that even if + * you know with certainty today all the ways that an untrusted data item is + * used in your application, it is genrally impossible to predict all the + * contexts that it may be used in the future, not only in your application, but + * in other applications that could access that data in the database. + *
  • + *
  • Avoiding multiple nested contexts: + * A really tricky situation to get correct is hen there are multiple nested + * encoding contexts. But far, the most common place this seems to come up is + * untrusted URLs used in JavaScript. How should you handle that? Well, to be + * honest, the best way is to rewrite your code to avoid it. An example of + * this that is well worth reading may be found at + * ESAPI-DEV mailing list archives: + * URL encoding within JavaScript. Be sure to read the entire thread. + * The question itself is too nuanced to be answered in Javadoc, but now, + * hopefully you are at least aware of the potential pitfalls. + *
  • + *
* * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security From eae125386565b5b89ed4c11565eac041a4b50f57 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:12:08 -0400 Subject: [PATCH 118/544] Change cookie path from "//" to "/". --- .../java/org/owasp/esapi/reference/DefaultHTTPUtilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index 149937579..f690e26bd 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -715,7 +715,7 @@ public void killAllCookies(HttpServletRequest request, HttpServletResponse respo * @param name */ public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) { - String path = "//"; + String path = "/"; String domain=""; Cookie cookie = getFirstCookie(request, name); if ( cookie != null ) { From 195f07c6553e9c62ea520468c3c34faf2a4aaccd Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 26 Jun 2020 20:40:09 -0400 Subject: [PATCH 119/544] Minor typo fix; add Inc. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 155838fae..01095ed7d 100644 --- a/README.md +++ b/README.md @@ -87,4 +87,4 @@ For a general overview of Google Groups and its web interface, see https://group For assistance subscribing and unsubscribing to Google Groups, see https://webapps.stackexchange.com/questions/13508/how-can-i-subscribe-to-a-google-mailing-list-with-a-non-google-e-mail-address/15593#15593 ---------- -OWASP is a registered trademark of The OWASP Foundation. +OWASP is a registered trademark of the OWASP Foundation, Inc. From 980cce5b2e20770088c7a68641a95706864e32d5 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 26 Jun 2020 20:47:26 -0400 Subject: [PATCH 120/544] Update pom.xml to use latest versions of AntiSamy, slf4j, & batik-css. Antisamy: 1.5.8 -> 1.5.10 slf4j: 1.7.26 -> 1.7.30 batik: 1.11 -> 1.13 (addresses CVE-2019-17566) --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 132075ffd..a57922307 100644 --- a/pom.xml +++ b/pom.xml @@ -228,7 +228,7 @@ org.owasp.antisamy antisamy - 1.5.8 + 1.5.10 xml-apis @@ -243,7 +243,7 @@ org.slf4j slf4j-api - 1.7.26 + 1.7.30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ecf4a202c46c910b3b98559fd635b44b683dfa7a Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 2 Jul 2020 16:30:30 -0400 Subject: [PATCH 129/544] Update wiki link to new OWASP URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01095ed7d..baddc52bb 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ More detail is available in the file '[SECURITY.md](https://raw.githubuserconten ## Where to Find More Information on ESAPI -*Wiki:* https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API +*Wiki:* https://owasp.org/www-project-enterprise-security-api/ *Nightly Build:* Travis CI - https://travis-ci.org/bkimminich/esapi-java-legacy From 7abf4a5cfe296a04003ca5222cfb5a53f3bca935 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 3 Jul 2020 00:20:56 -0400 Subject: [PATCH 130/544] Fix typo. s/parse/parser/ --- src/main/java/org/owasp/esapi/Encoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index 48b1e9cf4..b9d42dea5 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -468,7 +468,7 @@ public interface Encoder { *

* The use of a real XML parser is strongly encouraged. However, in the * hopefully rare case that you need to make sure that data is safe for - * inclusion in an XML document and cannot use a parse, this method provides + * inclusion in an XML document and cannot use a parser, this method provides * a safe mechanism to do so. * * @see Character Encoding in Entities From 807adf71fa3c1efd7a3b09a7a190b87c4c18f228 Mon Sep 17 00:00:00 2001 From: davewichers Date: Fri, 3 Jul 2020 16:35:08 -0400 Subject: [PATCH 131/544] Upgrade some dependencies and most plugins. Add FindSecBugs plugin to SpotBugs. Add animal sniffer plugin to try to detect use of Java APIs not in Java 7. Plugin runs but doesn't detect use of APIs currently causing compilation errors with Java 7. --- pom.xml | 204 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 118 insertions(+), 86 deletions(-) diff --git a/pom.xml b/pom.xml index a9dcd8a5e..3235520cd 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ The Open Web Application Security Project (OWASP) - http://www.owasp.org/index.php + https://owasp.org @@ -90,7 +90,7 @@ Jeff Williams - Aspect Security + Contrast Security Project Inventor @@ -121,7 +121,6 @@ Dave Wichers - Aspect Security Jim Manico @@ -133,12 +132,17 @@ UTF-8 + 1.23 + 2.0.7 + 4.0.4 + 3.0.0-M5 javax.servlet javax.servlet-api + 3.0.1 provided @@ -253,6 +257,7 @@ commons-io commons-io + 2.6 @@ -292,24 +297,38 @@ 1.4.01 + + + com.github.spotbugs + spotbugs-annotations + ${version.spotbugs} + true + + + net.jcip + jcip-annotations + 1.0 + true + + junit junit - 4.12 + 4.13 test org.bouncycastle bcprov-jdk15on - 1.61 + 1.65.01 test org.powermock powermock-api-mockito2 - 2.0.2 + ${version.powermock} test @@ -319,31 +338,13 @@ org.javassist javassist - - - org.mockito - mockito-core - - - org.powermock - powermock-reflect - - - net.bytebuddy - byte-buddy - - - net.bytebuddy - byte-buddy-agent - - + org.javassist javassist 3.25.0-GA @@ -352,19 +353,26 @@ org.mockito mockito-core - 2.27.0 + + 2.28.2 test org.powermock powermock-module-junit4 - 2.0.2 + ${version.powermock} test + + + junit + junit + + org.powermock powermock-reflect - 2.0.2 + ${version.powermock} test @@ -384,13 +392,13 @@ org.openjdk.jmh jmh-core - 1.21 + ${version.jmh} test org.openjdk.jmh jmh-generator-annprocess - 1.21 + ${version.jmh} test @@ -401,51 +409,22 @@ org.apache.maven.plugins maven-assembly-plugin - 3.1.1 + 3.3.0 org.apache.maven.plugins maven-dependency-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.0.0-M1 - - - com.github.spotbugs - spotbugs-maven-plugin - 3.1.11 - - - - com.github.spotbugs - spotbugs - 3.1.12 - - - - - - ${PhaseIfJava8plus} - - - - true - true - true - Low - Max - false - - - + net.sourceforge.maven-taglib maven-taglib-plugin @@ -467,7 +446,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.8.1 1.7 1.7 @@ -501,7 +480,7 @@ org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.0.0-M1 @@ -516,33 +495,60 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M2 + 3.0.0-M3 org.codehaus.mojo extra-enforcer-rules 1.2 + + org.codehaus.mojo + animal-sniffer-enforcer-rule + + 1.17 + - - enforce - + check-java-versions + compile + enforce 1.7 - + ESAPI 2.x now uses the JDK1.7 for its baseline. Please make sure that your JAVA_HOME environment variable is pointed to a JDK1.7 or later distribution. - + 1.7 + true + + Dependencies shouldn't require Java 8+ + true + + + + check-java7API-signatures + compile + enforce + + + + + org.codehaus.mojo.signature + + java17 + 1.0 + + + @@ -564,13 +570,13 @@ org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.0.0-M1 org.apache.maven.plugins maven-jar-plugin - 3.1.0 + 3.2.0 @@ -584,7 +590,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.1.0 + 3.2.0 7 none @@ -608,13 +614,13 @@ org.apache.maven.plugins maven-pmd-plugin - 3.11.0 + 3.13.0 org.apache.maven.plugins maven-project-info-reports-plugin - 3.0.0 + 3.1.0 @@ -626,13 +632,13 @@ org.apache.maven.plugins maven-site-plugin - 3.7.1 + 3.9.1 org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.2.1 attach-sources @@ -644,14 +650,14 @@ org.apache.maven.plugins - maven-surefire-report-plugin - 2.22.1 + maven-surefire-plugin + ${version.surefire} org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 + maven-surefire-report-plugin + ${version.surefire} @@ -687,7 +693,7 @@ org.owasp dependency-check-maven - 5.0.0 + 5.3.2 5.0 ./suppressions.xml @@ -767,11 +773,11 @@ org.apache.maven.plugins - maven-surefire-report-plugin + maven-surefire-plugin org.apache.maven.plugins - maven-surefire-plugin + maven-surefire-report-plugin + org.codehaus.mojo versions-maven-plugin @@ -799,12 +805,38 @@ + Java8plus [1.8,) site + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + ${version.spotbugs} + + + + com.h3xstream.findsecbugs + findsecbugs-plugin + 1.10.1 + + + Max + false + + + + + From cceffa6aab0887e379be1c7117d2e620b36cfdee Mon Sep 17 00:00:00 2001 From: davewichers Date: Fri, 3 Jul 2020 17:56:33 -0400 Subject: [PATCH 132/544] Fix 1 link to the OWASP web site in pom. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3235520cd..8623ac2e6 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ ESAPI - https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API + https://owasp.org/www-project-enterprise-security-api/ The Enterprise Security API (ESAPI) project is an OWASP project to create simple strong security controls for every web platform. Security controls are not simple to build. You can read about the From f94ca94e15237c0f0887913d5d33c697fb97594a Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Fri, 3 Jul 2020 18:48:59 -0400 Subject: [PATCH 133/544] Fix for GitHub Issue 552 (#553) * Fix #552 * Undesired commit, but 'git checkout -- documentation/esapi4java-core-2.2.1.0-release-notes.txt' has no effect. Sigh. * Updated from 'misc-cleanup' branch. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 248 ++++++++++-------- .../logging/appender/ClientInfoSupplier.java | 8 +- .../appender/EventTypeLogSupplier.java | 8 +- .../logging/appender/ServerInfoSupplier.java | 8 +- .../logging/appender/UserInfoSupplier.java | 8 +- 5 files changed, 153 insertions(+), 127 deletions(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index bb46556b8..46f8bab9d 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -1,115 +1,133 @@ -Release notes for ESAPI 2.2.1.0 - Release date: 2020-TBD - Project leaders: - -Kevin W. Wall - -Matt Seil - -Previous release: ESAPI 2.2.0.0, 2019-June-24 - - -Executive Summary: Important Things to Note for this Release ------------------------------------------------------------- - - TBD - -================================================================================================================= - -Basic ESAPI facts - -ESAPI 2.2.0.0 release: - 194 Java source files - 4150 JUnit tests in 118 Java source files - -ESAPI 2.2.1.0 release: - TBD - -GitHub Issues fixed in this release - -Issue # GitHub Issue Title ----------------------------------------------------------------------------------------------- - -143 Enchance encodeForOS to auto-detect the underling OS -226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() -245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch -256 White space clean up -382 Build Fails on path with space -494 Encoder's encodeForCSS doesn't handle RGB Triplets -503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` -509 HTMLValidationRule.getValid(String,String) does not follow documented specifications -511 Add missing documentation to Validator.addRule() and Validator.getRule() -512 Update Apache Commons Bean Utils to 1.9.4 -515 Adding tests for getCookies (also 516) -519 Issue 494 CSSCodec RGB Triplets -530 Log Bridge Tests -536 Various fixes -538 Addressing log4j 1.x CVE-2019-17571 - ------------------------------------------------------------------------------ - - Changes requiring special attention - ------------------------------------------------------------------------------ - -TBD - ------------------------------------------------------------------------------ - - Other changes in this release, some of which not tracked via GitHub issues - ------------------------------------------------------------------------------ - -Documentation updates for locating Jar files -Unneeded code removed from ExtensiveEncoder -Inline reader added to ExtensiveEncoder -Additional time for windows to always sleep more than given seconds in CryptoTokenTest -Change required by tweak to CipherText.toString() method -Removed call to deprecated CryptoHelper.computeDerivedKey() method -New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class -Use existing toString method rather than a StringBuilder -Documentation and tests -JavaLogger move -Splitting user infor from Client Supplier - ------------------------------------------------------------------------------ - -Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) -Generated manually (this time) - -Developer Total commits Total Number - of Files Changed -===================================================== -jeremiahjstacey 11 68 -kwwall 15 26 -wiitek 3 6 -xeno6696 8 9 -Michael-Ziluck 2 3 -===================================================== - ------------------------------------------------------------------------------ - -53 Closed PRs since 2.2.0.0 release -=================================== -504 New scripts to suppress noise for 'mvn test' -510 Resolve #509 - Properly throw exception when HTML fails -513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ -519 Issue 494 CSSCodec RGB Triplets -520 OS Name DefaultExecutorTests #143 -540 Issue 382: Build Fails on path with space -596 Closes Issue 245 - ------------------------------------------------------------------------------ - -Notice: - - Release notes written by Bill Sempf (bill.sempf@owasp.org) please direct any communication to me. - -Project co-leaders - Kevin W. Wall (kwwall) - Matt Seil (xeno6696) - -Special shout-outs to: - Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire - Dave Wichers (davewichers) - for Maven Central / Sonatype help - -Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. - +Release notes for ESAPI 2.2.1.0 + Release date: 2020-July-?? + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.0.0, 2019-June-24 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +This is a minor release. It's main purpose was to update dependencies to eliminate potential vulnerabilities arising from dependencies with known CVEs. See the section "Changes requiring special attention" below for additional details. + +Also special props to Bill Sempf for stepping up and volunteering to prepare the initial cut of these release notes. Had he not done so, this release either would not have release notes or it would have been delayed another 6 months while I procrastinated further with various distractions. (Squirrel!) + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.0.0 release: + 194 Java source files + 4150 JUnit tests in 118 Java source files + +ESAPI 2.2.1.0 release: + 211 Java source files + 4309 JUnit tests in 134 Java source files + +GitHub Issues fixed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +143 Enchance encodeForOS to auto-detect the underling OS +226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() +245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch +256 White space clean up +382 Build Fails on path with space +494 Encoder's encodeForCSS doesn't handle RGB Triplets +503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` +509 HTMLValidationRule.getValid(String,String) does not follow documented specifications +511 Add missing documentation to Validator.addRule() and Validator.getRule() +512 Update Apache Commons Bean Utils to 1.9.4 +515 Adding tests for getCookies (also 516) +519 Issue 494 CSSCodec RGB Triplets +522 javadoc corrections for Encoder.canonicalize() +530 Log Bridge Tests +536 Various fixes +538 Addressing log4j 1.x CVE-2019-17571 +552 Rewrite implementation of some ESAPI classes to remove Java 8 dependencies + +----------------------------------------------------------------------------- + + Changes requiring special attention + +----------------------------------------------------------------------------- +The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4j 1.x as it is way past the end-of-life and we now support SLF4J. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +Related to that CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4j 1.2.17. ESAPI is not affected by this (even if you chose to use Log4j 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + +Notable dependency updates (excludes those only used with JUnit tests): + antiSamy 1.5.8 -> 1.5.10 + batik-css 1.11 -> 1.13 + commons-beansutil 1.9.3 -> 1.9.4 + slf4j-api 1.7.26 -> 1.7.30 + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatiblity.) + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +Documentation updates for locating Jar files +Unneeded code removed from ExtensiveEncoder +Inline reader added to ExtensiveEncoder +Additional time for windows to always sleep more than given seconds in CryptoTokenTest +Change required by tweak to CipherText.toString() method +Removed call to deprecated CryptoHelper.computeDerivedKey() method +New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class +Use existing toString method rather than a StringBuilder +Documentation and tests +JavaLogger moved +Splitting user info from Client Supplier + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) +Generated manually (this time) + +Developer Total Total Number +(GitHub ID) commits of Files Changed +===================================================== +jeremiahjstacey 11 68 +kwwall 16 26 +wiitek 3 6 +xeno6696 8 9 +Michael-Ziluck 2 3 +sempf 1 1 +===================================================== + +----------------------------------------------------------------------------- + +53 Closed PRs since 2.2.0.0 release (those rejected not listed) +=============================================================== +504 New scripts to suppress noise for 'mvn test' +510 Resolve #509 - Properly throw exception when HTML fails +513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ +519 Issue 494 CSSCodec RGB Triplets +520 OS Name DefaultExecutorTests #143 +540 Issue 382: Build Fails on path with space +596 Closes Issue 245 + +----------------------------------------------------------------------------- + +Notice: + + Release notes written by Bill Sempf (bill.sempf@owasp.org), but please direct any communication to the project leaders. + +Project co-leaders + Kevin W. Wall (kwwall) + Matt Seil (xeno6696) + +Special shout-outs to: + Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire + Dave Wichers (davewichers) - for pom.xml improvements + Bill Sempf -- for these release notes. Awesome job, Bill. I owe you a brew. + +Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. diff --git a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java index 2ff07a19a..5a8524340 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -27,7 +28,8 @@ * Supplier which can provide a String representing the client-side connection * information. */ -public class ClientInfoSupplier implements Supplier { +public class ClientInfoSupplier // implements Supplier +{ /** Default Last Host string if the Authenticated user is null.*/ private static final String DEFAULT_LAST_HOST = "#UNKNOWN_HOST#"; /** Session Attribute containing the ESAPI Session id. */ @@ -47,7 +49,7 @@ public class ClientInfoSupplier implements Supplier { /** Whether to log the user info from this instance. */ private boolean logClientInfo = true; - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { String clientInfo = ""; diff --git a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java index 2f857f128..4af1bd50b 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import org.owasp.esapi.Logger; import org.owasp.esapi.Logger.EventType; @@ -25,7 +26,8 @@ * an EventType for logging * */ -public class EventTypeLogSupplier implements Supplier { +public class EventTypeLogSupplier // implements Supplier +{ /** EventType reference to supply log representation of. */ private final EventType eventType; @@ -38,7 +40,7 @@ public EventTypeLogSupplier(EventType evtyp) { this.eventType = evtyp == null ? Logger.EVENT_UNSPECIFIED : evtyp; } - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { return eventType.toString(); } diff --git a/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java index ca9b4bbd8..c9e0d8e02 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import javax.servlet.http.HttpServletRequest; @@ -25,7 +26,8 @@ * Supplier which can provide a String representing the server-side connection * information. */ -public class ServerInfoSupplier implements Supplier { +public class ServerInfoSupplier // implements Supplier +{ /** Whether to log the server connection info. */ private boolean logServerIP = true; /** Whether to log the application name. */ @@ -45,7 +47,7 @@ public ServerInfoSupplier(String logName) { this.logName = logName; } - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { // log server, port, app name, module name -- server:80/app/module StringBuilder appInfo = new StringBuilder(); diff --git a/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java index e9c34e31a..6065ed366 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import org.owasp.esapi.ESAPI; import org.owasp.esapi.User; @@ -24,14 +25,15 @@ * Supplier which can provide a String representing the client-side connection * information. */ -public class UserInfoSupplier implements Supplier { +public class UserInfoSupplier // implements Supplier +{ /** Default UserName string if the Authenticated user is null.*/ private static final String DEFAULT_USERNAME = "#ANONYMOUS#"; /** Whether to log the user info from this instance. */ private boolean logUserInfo = true; - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { // log user information - username:session@ipaddr User user = ESAPI.authenticator().getCurrentUser(); From e8e613fefb24e263a1676c042261e388f16c4569 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Sun, 12 Jul 2020 21:59:43 -0400 Subject: [PATCH 134/544] Prep 2.2.1.0 (#557) * Delete the release notes that prevents me from doing a 'git pull origin' from my fork. * Add note referring to minimal Java 7 baseline. * Close #542 - final release notes for 2.2.1.0. * Close #556 - Add some final additional 'see also' refs. * Close #554 * Close #555 * Added issue 521 which should have been closed per PR 535. Issue now closed. * Close #558 * Update to reflect fix to issue #558 * Find/fix dependency causing java.lang.OutOfMemoryError: PermGen space that only occurs on Mac with Java 7. Apparently it was the surefire plugin. Co-authored-by: davewichers --- README.md | 4 +- .../esapi4java-core-2.2.1.0-release-notes.txt | 253 ++++++++++++++---- pom.xml | 5 +- src/main/java/org/owasp/esapi/Encoder.java | 3 + .../org/owasp/esapi/crypto/CryptoHelper.java | 10 +- .../owasp/esapi/crypto/CryptoHelperTest.java | 10 +- .../esapi/reference/AccessControllerTest.java | 2 +- .../owasp/esapi/reference/ValidatorTest.java | 8 +- .../esapi/fbac-policies/DataAccessRules.txt | 4 +- 9 files changed, 234 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index baddc52bb..dc59e7a2a 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,11 @@ OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web ap # What does Legacy mean?

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. -IMPORTANT NOTE: +IMPORTANT NOTES: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. +Also, the minimal baseline Java version to use ESAPI is Java 7. (This was changed from Java 6 during the 2.2.0.0 release.) + # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index 94da6b95d..ee3caf5ea 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -27,40 +27,61 @@ ESAPI 2.2.1.0 release: 211 Java source files 4309 JUnit tests in 134 Java source files -GitHub Issues fixed in this release +39 GitHub Issues closed in this release Issue # GitHub Issue Title ---------------------------------------------------------------------------------------------- -143 Enchance encodeForOS to auto-detect the underling OS -226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() -245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch -256 White space clean up -382 Build Fails on path with space -494 Encoder's encodeForCSS doesn't handle RGB Triplets -503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` -509 HTMLValidationRule.getValid(String,String) does not follow documented specifications -511 Add missing documentation to Validator.addRule() and Validator.getRule() -512 Update Apache Commons Bean Utils to 1.9.4 -515 Adding tests for getCookies (also 516) -519 Issue 494 CSSCodec RGB Triplets -522 javadoc corrections for Encoder.canonicalize() -530 Log Bridge Tests -536 Various fixes -538 Addressing log4j 1.x CVE-2019-17571 -552 Rewrite implementation of some ESAPI classes to remove Java 8 dependencies - +143 - Enchance encodeForOS to auto-detect the underling OS +173 - DOMConfigurator is being used inappropriately in the ESAPIWebApplicationFirewallFilter +226 - Javadoc Inaccuracy in getRandomInteger() and getRandomReal() +232 - SecurityWrapperResponse.createCookieHeader modification request (closed; marked 'wontfix') +235 - exception is java.lang.NoClassDefFoundError: org.owasp.esapi.codecs.Codec +245 - KeyDerivationFunction::computeDerivedKey - possible security level mismatch +256 - Whitespace in JavaEncryptor +263 - I am getting validation exception while validating a paramter coming from http request +268 - SecurityWrapperResponse setStatus should not always set SC_OK +269 - org.owasp.esapi.reference.DefaultValidator reports ValidationException with IE 9 +271 - Add Constructor to DefaultSecurityConfiguration to accept a properties file (1.4) +276 - Patch for /branches/2.1/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java +310 - Make HTMLValidationRule to look for antisamy-esapi.xml in classpaths enhancement +382 - Build Fails on path with space +465 - Update both ESAPI.properties files to show comment for ESAPI logger support for SLF4J +488 - Missed a legal input case in DefaultSecurityConfiguration.java +494 - Encoder's encodeForCSS doesn't handle RGB Triplets +495 - Maven Install Requires GPG Key +499 - ValidatorTest.isValidDirectoryPath() has tests that fail under Windows if ESAPI tests run from different drive where Windows installed +500 - Suppress noise from ESAPI searching for properties and stop ignoring important IOExceptions +503 - Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` +509 - HTMLValidationRule.getValid(String,String) does not follow documented specifications +511 - Add missing documentation to Validator.addRule() and Validator.getRule() +512 - Update Apache Commons Bean Utils to 1.9.4 +515 - NullPointerException can result from line 163 of SecurityWrapperRequest.java: +521 - JUnit test ValidatorTest.testIsValidSafeHTML() now failing +522 - javadoc corrections for Encoder.canonicalize() +523 - Links in README to users list and dev list are reversed +527 - Configuration flag for disabling Logger User and App Information +530 - Apply default logging content to SLF4J messages +532 - Update JUL and Log4J code structure and workflow to match SLF4J +536 - SecurityWrapperResponse setHeader error message is unclear +538 - Addressing log4j 1.x CVE-2019-17571 +542 - Write up ESAPI release notes for planned 2.2.1.0 release +552 - Rewrite implementation of some ESAPI classes to remove Java 8 dependencies +554 - CryptoHelper.arrayCompare() fails with NPE under Java 7 when one of the arguments is null +555 - JUnit test org.owasp.esapi.reference.AccessControllerTest.testIsAuthorizedForData fails when run against Java 7 on Linux +556 - Major overhaul to ESAPI Encoder Javadoc based on ESAPI Encoder Usability Study +558 - ValidatorTest.testIsValidDirectoryPath() JUnit test fails under MacOS ----------------------------------------------------------------------------- - Changes requiring special attention + Changes Requiring Special Attention ----------------------------------------------------------------------------- -The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4j 1.x as it is way past the end-of-life and we now support SLF4J. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. +The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. Related to that CVE and how it affects ESAPI, be sure to read https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf -which describes CVE-2019-17571, a deserialization vulnerability in Log4j 1.2.17. ESAPI is not affected by this (even if you chose to use Log4j 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is not affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. Notable dependency updates (excludes those only used with JUnit tests): antiSamy 1.5.8 -> 1.5.10 @@ -70,6 +91,32 @@ Notable dependency updates (excludes those only used with JUnit tests): Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatiblity.) +----------------------------------------------------------------------------- + + Known Issues / Problems + +----------------------------------------------------------------------------- +If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + +I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html +and + https://github.com/bcgit/bc-java/issues/477 +I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + ----------------------------------------------------------------------------- Other changes in this release, some of which not tracked via GitHub issues @@ -77,60 +124,154 @@ Finally, while ESAPI still supports JDK 7 (even though that too is way past end- ----------------------------------------------------------------------------- Documentation updates for locating Jar files -Unneeded code removed from ExtensiveEncoder +Unneeded code removed from ExtensiveEncoderURI test Inline reader added to ExtensiveEncoder Additional time for windows to always sleep more than given seconds in CryptoTokenTest Change required by tweak to CipherText.toString() method Removed call to deprecated CryptoHelper.computeDerivedKey() method New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class -Use existing toString method rather than a StringBuilder -Documentation and tests -JavaLogger moved -Splitting user info from Client Supplier +Miscellaneous documentation and tests +JavaLogger moved to new package +Log4J 1.x no longer ESAPI's default logger ----------------------------------------------------------------------------- -Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) -Generated manually (this time) +Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-07-11) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +HJW8472 11 8 1 +jeremiahjstacey 78 70 6 +kwwall 65 64 8 +Michael-Ziluck 3 2 2 +sempf 1 1 1 +wiitek 6 4 2 +xeno6696 3 5 1 +======================================================== + Total: 21 -Developer Total Total Number -(GitHub ID) commits of Files Changed -===================================================== -jeremiahjstacey 11 68 -kwwall 16 26 -wiitek 3 6 -xeno6696 8 9 -Michael-Ziluck 2 3 -sempf 1 1 -===================================================== ----------------------------------------------------------------------------- -53 Closed PRs since 2.2.0.0 release (those rejected not listed) -=============================================================== +21 Closed PRs merged since 2.2.0.0 release (those rejected not listed) +====================================================================== +PR# GitHub ID Description +---------------------------------------------------------------------- +504 -- kwwall -- New scripts to suppress noise for 'mvn test' +505 -- kwwall -- Close issue #256. White-space clean up. +506 -- kwwall -- Closes Issue 245 +508 -- Michael-Ziluck -- Resolves #226 - Corrected docs for the bounded, numeric, random methods +510 -- Michael-Ziluck -- Resolve #509 - Properly throw exception when HTML fails +513 -- kwwall -- Close issue #512 by updating to 1.9.4 of Commons Beans Util. +514 -- xeno6696 -- Fixed issues #503 by writing a new addReferer method, also temporaril… +516 -- jeremiahjstacey -- Issue 515 +518 -- jeremiahjstacey -- Issue #511 Copying Docs from DefaultValidator +519 -- jeremiahjstacey -- Issue 494 CSSCodec RGB Triplets +520 -- jeremiahjstacey -- OS Name DefaultExecutorTests #143 +533 -- jeremiahjstacey -- #532 JUL and Log4J match SLF4J class structure and Workflow +535 -- kwwall -- Issue 521 +537 -- jeremiahjstacey -- Issue 536 +539 -- wiiitek -- upgrade for convergence +540 -- wiiitek -- Issue 382: Build Fails on path with space +541 -- HJW8472 -- Fixed issue #310 +543 -- sempf -- Release notes for 2.2.1.0 +551 -- kwwall -- Misc cleanup +553 -- kwwall -- Fix for GitHub Issue 552 +557 -- kwwall -- Final prep for 2.2.1.0 release + + +CHANGELOG: Create your own. May I suggest: + + git log --since=2019-06-25 --reverse --pretty=medium + + which will show all the commits since just after the last (2.2.0.0) release. + +----------------------------------------------------------------------------- -504 New scripts to suppress noise for 'mvn test' -510 Resolve #509 - Properly throw exception when HTML fails -513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ -519 Issue 494 CSSCodec RGB Triplets -520 OS Name DefaultExecutorTests #143 -540 Issue 382: Build Fails on path with space -596 Closes Issue 245 +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.1.0 + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.1.0-RC1 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.5.10:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.12:compile + [INFO] | | \- org.apache.httpcomponents:httpcore:jar:4.4.13:compile + [INFO] | \- commons-codec:commons-codec:jar:1.14:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.13:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xerces:xercesImpl:jar:2.12.0:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.0.4:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.65.01:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ ----------------------------------------------------------------------------- -Notice: +Ackknowledgements: Release notes written by Bill Sempf (bill.sempf@owasp.org), but please direct any communication to the project leaders. -Project co-leaders - Kevin W. Wall (kwwall) - Matt Seil (xeno6696) - Special shout-outs to: - Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire - Dave Wichers (davewichers) - for pom.xml improvements - Bill Sempf -- for these release notes. Awesome job, Bill. I owe you a brew. + Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire and for refactoring ESAPI loggers. + Dave Wichers (davewichers) - for several extremely useful pom.xml improvements. + Bill Sempf (sempf) -- for these release notes. Awesome job, Bill. I owe you a brew. + Chamila Wijayarathna and Nalin A. G. Arachchilage for their authorship and subsequent extensive discussion of their paper "Fighting Against XSS Attacks: A Usability Evaluation of OWASP ESAPI Output Encoding" (https://scholarspace.manoa.hawaii.edu/bitstream/10125/60167/0727.pdf). Their paper and their willingness to engage with me to discuss it was what led to the (hopefully) improved Javadoc for the ESAPI Encoder interface. + And lastly a special thanks to first-time contributors Michael-Ziluck, wiiitek, and HJW8472. Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/pom.xml b/pom.xml index 8623ac2e6..8a383d6f2 100644 --- a/pom.xml +++ b/pom.xml @@ -135,7 +135,10 @@ 1.23 2.0.7 4.0.4 - 3.0.0-M5 + + 3.0.0-M2 diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index b9d42dea5..a8b949d71 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -149,6 +149,9 @@ * * * + * @see OWASP Cross-Site Scripting Prevention Cheat Sheet. + * @see OWASP Proactive Controls: C4: Encode and Escape Data + * @see Properly encoding and escaping for the web. * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 diff --git a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java index 11a6bcb9f..31f276ac4 100644 --- a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java +++ b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java @@ -352,7 +352,15 @@ public static void copyByteArray(final byte[] src, byte[] dest) */ @Deprecated public static boolean arrayCompare(byte[] b1, byte[] b2) { - // Note: See GitHub issue #246 + // Note: See GitHub issue #246 and #554. + // If we make Java 8 the minimal ESAPI baseline before we remove this + // method, we can at least remove these next 6 lines. (Issue 554.) + if ( b1 == null && b2 == null ) { // Must test this first! + return true; // Prevent NPE; compatibility with Java 8 and later. + } + if ( b1 == null || b2 == null ) { + return false; // Prevent NPE; compatibility with Java 8 and later. + } return java.security.MessageDigest.isEqual(b1, b2); } diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java index 678b0acf8..aa7ae0cf3 100644 --- a/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java @@ -131,7 +131,13 @@ public final void testArrayCompare() { // stop = System.nanoTime(); // diff = stop - start; // System.out.println("diff: " + diff + " nanosec"); - + +// start = System.nanoTime(); + assertFalse(CryptoHelper.arrayCompare(null, ba1)); +// stop = System.nanoTime(); +// diff = stop - start; +// System.out.println("diff: " + diff + " nanosec"); + ba2 = ba1; // start = System.nanoTime(); assertTrue(CryptoHelper.arrayCompare(ba1, ba2)); @@ -186,4 +192,4 @@ private boolean checkByteArray(byte[] ba, byte b) { public static junit.framework.Test suite() { return new JUnit4TestAdapter(CryptoHelperTest.class); } -} \ No newline at end of file +} diff --git a/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java b/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java index ad53d9ea3..107c1da71 100644 --- a/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java +++ b/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java @@ -226,7 +226,7 @@ public void testIsAuthorizedForData() { userRW = Class.forName("java.lang.String"); anyR = Class.forName("java.io.BufferedReader"); userAdminR = Class.forName("java.util.Random"); - userAdminRW = Class.forName("java.awt.event.MouseWheelEvent"); + userAdminRW = Class.forName("javax.crypto.Cipher"); undefined = Class.forName("java.io.FileWriter"); }catch(ClassNotFoundException cnf){ diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index b0fd24789..fc0dc7e06 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -350,7 +350,13 @@ public void testIsValidDirectoryPath() throws IOException { // Unix specific paths should pass assertTrue(instance.isValidDirectoryPath("test", "/", parent, false)); // Root directory - assertTrue(instance.isValidDirectoryPath("test", "/etc", parent, false)); // Always exist directory + // Unfortunately, on MacOS both "/etc" and "/var" are symlinks + // to "/private/etc" and "/private/var" respectively, and "/sbin" + // and "/bin" sometimes are symlinks on certain *nix OSs, so we need + // to special case MacOS here. + boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac"); + String testDirNotSymLink = isMac ? "/private" : "/etc"; + assertTrue(instance.isValidDirectoryPath("test", testDirNotSymLink, parent, false)); // Always exist directory // Unix specific paths that should not exist or work assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false)); // Standard shell, not dir diff --git a/src/test/resources/esapi/fbac-policies/DataAccessRules.txt b/src/test/resources/esapi/fbac-policies/DataAccessRules.txt index f21e8c869..0341aa56f 100644 --- a/src/test/resources/esapi/fbac-policies/DataAccessRules.txt +++ b/src/test/resources/esapi/fbac-policies/DataAccessRules.txt @@ -4,6 +4,6 @@ java.io.BufferedReader | any | read | default deny java.lang.String | User | read, write | java.lang.Math | Admin | read, write | java.util.ArrayList | Admin | read | -java.awt.event.MouseWheelEvent | Admin, User | write, read | +javax.crypto.Cipher | Admin, User | write, read | java.util.Date | User | write | -java.util.Random | User, Admin | read | \ No newline at end of file +java.util.Random | User, Admin | read | From 6bc2889e370acfdee6d8ffd711fc9a354506893d Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 12 Jul 2020 22:40:52 -0400 Subject: [PATCH 135/544] Change release from 2.2.1.0-RC1 to 2.2.1.0. Also add additional comment about surefire plug-in. --- pom.xml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 8a383d6f2..33af4721a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.2.1.0-RC1 + 2.2.1.0 jar @@ -136,8 +136,11 @@ 2.0.7 4.0.4 + org.owasp.esapi.reference.DefaultValidatorInputStringAPITest.getValidInputNullAllowedPassthrough Time elapsed: 2.057 s <<< ERROR! + java.lang.OutOfMemoryError: PermGen space + + when running tests with Java 7 on Mac OS X. No problems observed on Linux. + --> 3.0.0-M2 From fefe6033d8e21963ab5f84b1649b8708366cd524 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 12 Jul 2020 22:42:38 -0400 Subject: [PATCH 136/544] Correct # of commits. --- documentation/esapi4java-core-2.2.1.0-release-notes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index ee3caf5ea..f91f82332 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -142,9 +142,10 @@ Generated manually (this time) -- all errors are the fault of kwwall and his ina Developer Total Total Number # Merged (GitHub ID) commits of Files Changed PRs ======================================================== +davewichers 2 1 0 HJW8472 11 8 1 jeremiahjstacey 78 70 6 -kwwall 65 64 8 +kwwall 67 64 8 Michael-Ziluck 3 2 2 sempf 1 1 1 wiitek 6 4 2 From fd009ec4cb166f8ecd72e4cb0fa303109558372e Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 12 Jul 2020 22:57:35 -0400 Subject: [PATCH 137/544] Prep for next development release and change version from 2.2.1.0 to 2.3.0.0-SNAPSHOT. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33af4721a..6356f1973 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.2.1.0 + 2.3.0.0-SNAPSHOT jar From 5ddb8399e60d947dd454007976910e3f51c37f87 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 13 Jul 2020 23:23:44 -0400 Subject: [PATCH 138/544] Miscellaneous minor clean-up of release notes. 1) Add release date. 2) Fix multiple spelling errors 3) Document new 'Known Issue' about running 'mvn test' from Windows 10 'cmd' prompt. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index f91f82332..7d6c54ef7 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.2.1.0 - Release date: 2020-July-?? + Release date: 2020-July-12 Project leaders: -Kevin W. Wall -Matt Seil @@ -32,14 +32,14 @@ ESAPI 2.2.1.0 release: Issue # GitHub Issue Title ---------------------------------------------------------------------------------------------- -143 - Enchance encodeForOS to auto-detect the underling OS +143 - Enhance encodeForOS to auto-detect the underling OS 173 - DOMConfigurator is being used inappropriately in the ESAPIWebApplicationFirewallFilter 226 - Javadoc Inaccuracy in getRandomInteger() and getRandomReal() 232 - SecurityWrapperResponse.createCookieHeader modification request (closed; marked 'wontfix') 235 - exception is java.lang.NoClassDefFoundError: org.owasp.esapi.codecs.Codec 245 - KeyDerivationFunction::computeDerivedKey - possible security level mismatch 256 - Whitespace in JavaEncryptor -263 - I am getting validation exception while validating a paramter coming from http request +263 - I am getting validation exception while validating a parameter coming from http request 268 - SecurityWrapperResponse setStatus should not always set SC_OK 269 - org.owasp.esapi.reference.DefaultValidator reports ValidationException with IE 9 271 - Add Constructor to DefaultSecurityConfiguration to accept a properties file (1.4) @@ -77,7 +77,7 @@ Issue # GitHub Issue Title Changes Requiring Special Attention ----------------------------------------------------------------------------- -The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. +The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. Related to that CVE and how it affects ESAPI, be sure to read https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf @@ -89,7 +89,7 @@ Notable dependency updates (excludes those only used with JUnit tests): commons-beansutil 1.9.3 -> 1.9.4 slf4j-api 1.7.26 -> 1.7.30 -Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatiblity.) +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) ----------------------------------------------------------------------------- @@ -117,6 +117,13 @@ and I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + +Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + +We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + ----------------------------------------------------------------------------- Other changes in this release, some of which not tracked via GitHub issues @@ -167,7 +174,7 @@ PR# GitHub ID Description 508 -- Michael-Ziluck -- Resolves #226 - Corrected docs for the bounded, numeric, random methods 510 -- Michael-Ziluck -- Resolve #509 - Properly throw exception when HTML fails 513 -- kwwall -- Close issue #512 by updating to 1.9.4 of Commons Beans Util. -514 -- xeno6696 -- Fixed issues #503 by writing a new addReferer method, also temporaril… +514 -- xeno6696 -- Fixed issues #503 by writing a new addReferer method, also temporarily… 516 -- jeremiahjstacey -- Issue 515 518 -- jeremiahjstacey -- Issue #511 Copying Docs from DefaultValidator 519 -- jeremiahjstacey -- Issue 494 CSSCodec RGB Triplets @@ -260,7 +267,7 @@ Direct and Transitive Runtime and Test Dependencies: ----------------------------------------------------------------------------- -Ackknowledgements: +Acknowledgments: Release notes written by Bill Sempf (bill.sempf@owasp.org), but please direct any communication to the project leaders. From 31f0f988d45de4ed08e515dc3a82842fad4f5bd4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 17 Jul 2020 21:28:21 -0400 Subject: [PATCH 139/544] Rephrase potentially confusing or misleading statements about ESAPI 3. Thanks to Timo Pagel for bringing this to our awareness. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc59e7a2a..85874bed9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web ap # What does Legacy mean? -

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. +

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however significan _new_ feature development for this branch will _not_ be done. Features that have already been scheduled for the 2.x branch will move forward. IMPORTANT NOTES: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. @@ -25,6 +25,8 @@ Also, the minimal baseline Java version to use ESAPI is Java 7. (This was # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java +Note however that work on ESAPI 3 has not yet become in earnest and is only in its earliest planning stages. Even the code that is presently there will likely change. + # Locating ESAPI Jar files The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.0.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. From 92ae8e11b2d16e47e85c61db0e47f503da6162d4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 19 Jul 2020 22:56:06 -0400 Subject: [PATCH 140/544] Fix latest release #s. Fix some MarkDown. Add link to 'Should I use ESAPI?'. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 85874bed9..3f7970602 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web ap # What does Legacy mean? -

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however significan _new_ feature development for this branch will _not_ be done. Features that have already been scheduled for the 2.x branch will move forward. +

This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however significan *new* feature development for this branch will *not* be done. Features that have already been scheduled for the 2.x branch will move forward. IMPORTANT NOTES: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. @@ -28,10 +28,11 @@ https://github.com/ESAPI/esapi-java Note however that work on ESAPI 3 has not yet become in earnest and is only in its earliest planning stages. Even the code that is presently there will likely change. # Locating ESAPI Jar files -The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.0.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. +The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.1.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.1.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.1.0/esapi-2.2.1.0-configuration.jar) and [esapi-2.2.1.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.1.0/esapi-2.2.1.0-configuration.jar.asc) respectively. The latest regular ESAPI jars can are available from Maven Central. +However, before you start a *new* project using ESAPI, but sure to read "[Should I use ESAPI?](https://owasp.org/www-project-enterprise-security-api/#div-shouldiuseesapi)". # ESAPI Deprecation Policy Unless we unintentionally screw-up, our intent is to keep classes, methods, and/or fields whihc have been annotated as "@deprecated" for a minimum of two (2) years or until the next major release number (e.g., 3.x as of now), which ever comes first, before we remove them. @@ -82,7 +83,7 @@ Webchat http://webchat.freenode.net/ *Mailing lists:* As of 2019-03-25, ESAPI's 2 mailing lists were officially moved OFF of their Mailman mailing lists to a new home on Google Groups. -The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to _after_ you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-users/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-dev/join)" respectively. +The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to *after* you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-users/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-dev/join)" respectively. Old archives for the old Mailman mailing lists for ESAPI-Users and ESAPI-Dev are still available at https://lists.owasp.org/pipermail/esapi-users/ and https://lists.owasp.org/pipermail/esapi-dev/ respectively. From 073193e4498c83a325c1caec6bd42f3912438ee2 Mon Sep 17 00:00:00 2001 From: jeremiahjstacey Date: Fri, 24 Jul 2020 18:46:39 -0500 Subject: [PATCH 141/544] Issue #560 JUL fixes (#562) * JUL Property Resource Logic fix Adjusting the loading behavior of the esapi-java-logging.properties file to account for a possible null stream resolution. Updating the error handling from system error output to throwing ConfigurationExceptions. Tests updated accordingly * JUL Default Logging configuration Adding a default version of esapi-java-logging.properties to the configuration directory. * JUL Documentation updates Noting the property file requirement and resource location in the class java doc. * Property Comment Updates Supplying a more accurate description of the effect of Logger.ClientInfo * Property Comment Updates Supplying a more accurate description of the effect of Logger.ClientInfo --- configuration/esapi/ESAPI.properties | 2 +- .../esapi/esapi-java-logging.properties | 6 +++ .../esapi/logging/java/JavaLogFactory.java | 10 ++++- .../logging/java/JavaLogFactoryTest.java | 44 +++++++------------ src/test/resources/esapi/ESAPI.properties | 2 +- 5 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 configuration/esapi/esapi-java-logging.properties diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index bafe1e38c..ccf146114 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -394,7 +394,7 @@ Logger.LogFileName=ESAPI_logging_file Logger.MaxLogFileSize=10000000 # Determines whether ESAPI should log the user info. Logger.UserInfo=true -# Determines whether ESAPI should log the app info. +# Determines whether ESAPI should log the session id and client IP. Logger.ClientInfo=true #=========================================================================== diff --git a/configuration/esapi/esapi-java-logging.properties b/configuration/esapi/esapi-java-logging.properties new file mode 100644 index 000000000..71011acc5 --- /dev/null +++ b/configuration/esapi/esapi-java-logging.properties @@ -0,0 +1,6 @@ +handlers= java.util.logging.ConsoleHandler +.level= INFO +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%3$-7s] %5$s %n +#https://www.logicbig.com/tutorials/core-java-tutorial/logging/customizing-default-format.html \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java index 93230c8e5..601d0da2a 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java @@ -26,6 +26,7 @@ import org.owasp.esapi.LogFactory; import org.owasp.esapi.Logger; import org.owasp.esapi.codecs.HTMLEntityCodec; +import org.owasp.esapi.errors.ConfigurationException; import org.owasp.esapi.logging.appender.LogAppender; import org.owasp.esapi.logging.appender.LogPrefixAppender; import org.owasp.esapi.logging.cleaning.CodecLogScrubber; @@ -35,6 +36,10 @@ import org.owasp.esapi.reference.DefaultSecurityConfiguration; /** * LogFactory implementation which creates JAVA supporting Loggers. + * + * This implementation requires that a file named 'esapi-java-logging.properties' exists on the classpath. + *
+ * A default file implementation is available in the configuration jar on GitHub under the 'Releases' * */ public class JavaLogFactory implements LogFactory { @@ -86,9 +91,12 @@ public class JavaLogFactory implements LogFactory { */ try (InputStream stream = JavaLogFactory.class.getClassLoader(). getResourceAsStream("esapi-java-logging.properties")) { + if (stream == null) { + throw new ConfigurationException("Unable to locate resource: esapi-java-logging.properties"); + } logManager.readConfiguration(stream); } catch (IOException ioe) { - System.err.print(new IOException("Failed to load esapi-java-logging.properties.", ioe)); + throw new ConfigurationException("Failed to load esapi-java-logging.properties.", ioe); } } diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java index c0713f239..465083da3 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java @@ -14,24 +14,21 @@ */ package org.owasp.esapi.logging.java; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; import java.util.List; import java.util.logging.LogManager; +import org.hamcrest.CustomMatcher; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import org.owasp.esapi.Logger; +import org.owasp.esapi.errors.ConfigurationException; import org.owasp.esapi.logging.appender.LogAppender; import org.owasp.esapi.logging.appender.LogPrefixAppender; import org.owasp.esapi.logging.cleaning.CodecLogScrubber; @@ -47,9 +44,12 @@ public class JavaLogFactoryTest { @Rule public TestName testName = new TestName(); + + @Rule + public ExpectedException exEx = ExpectedException.none(); @Test - public void testIOExceptionOnMissingConfiguration() throws Exception { + public void testConfigurationExceptionOnMissingConfiguration() throws Exception { final IOException originException = new IOException(testName.getMethodName()); LogManager testLogManager = new LogManager() { @@ -59,31 +59,17 @@ public void readConfiguration(InputStream ins) throws IOException, SecurityExcep } }; - OutputStream nullOutputStream = new OutputStream() { + exEx.expectMessage("Failed to load esapi-java-logging.properties"); + exEx.expect(ConfigurationException.class); + + exEx.expectCause(new CustomMatcher("Check for IOException") { @Override - public void write(int b) throws IOException { - //No Op + public boolean matches(Object item) { + return item instanceof IOException; } - }; - - ArgumentCaptor stdErrOut = ArgumentCaptor.forClass(Object.class); - PrintStream orig = System.err; - try (PrintStream errPrinter = new PrintStream(nullOutputStream)) { - PrintStream spyPrinter = PowerMockito.spy(errPrinter); - Mockito.doCallRealMethod().when(spyPrinter).print(stdErrOut.capture()); - System.setErr(spyPrinter); - - JavaLogFactory.readLoggerConfiguration(testLogManager); - - Object writeData = stdErrOut.getValue(); - assertTrue(writeData instanceof IOException); - IOException actual = (IOException) writeData; - assertEquals(originException, actual.getCause()); - assertEquals("Failed to load esapi-java-logging.properties.", actual.getMessage()); - } finally { - System.setErr(orig); - } + }); + JavaLogFactory.readLoggerConfiguration(testLogManager); } @Test diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 14b47d32f..6df0acc93 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -426,7 +426,7 @@ Logger.LogFileName=ESAPI_logging_file Logger.MaxLogFileSize=10000000 # Determines whether ESAPI should log the user info. Logger.UserInfo=true -# Determines whether ESAPI should log the app info. +# Determines whether ESAPI should log the session id and client IP. Logger.ClientInfo=true #=========================================================================== From 4b97073075a26facadf2931de792eddf0f8141b4 Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Mon, 27 Jul 2020 03:29:35 +0100 Subject: [PATCH 142/544] fix: upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5 (#559) Snyk has created this PR to upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5. See this package in NPM: https://www.npmjs.com/package/com.github.spotbugs:spotbugs-annotations See this project in Snyk: https://app.snyk.io/org/planetlevel/project/f53b118f-f068-49f2-98e2-0b5681787cd7?utm_source=github&utm_medium=upgrade-pr --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6356f1973..5c574aca3 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ UTF-8 1.23 2.0.7 - 4.0.4 + 4.0.5 + Dependencies shouldn't require Java 8+ From b7a7043963b09acf2c2401a4b565298b90c32308 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 22:59:44 -0400 Subject: [PATCH 143/544] Revert "fix: upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5 (#559)" This reverts commit 4b97073075a26facadf2931de792eddf0f8141b4. Synk-bot PR #559 results in errors when running 'mvn site' about the '4.0.5' version of com.github.spotbugs:spotbugs-annotations not being found (in Maven Central). I should have tested it first. :-( Lesson learned. --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5c574aca3..6356f1973 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ UTF-8 1.23 2.0.7 - 4.0.5 + 4.0.4 + Dependencies shouldn't require Java 8+ From ba79395ecbddcfb0896f565cf54ba26936268668 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:20:58 -0400 Subject: [PATCH 144/544] Update pom to reflect new 2.2.1.1 patch release. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6356f1973..4a60e515e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.3.0.0-SNAPSHOT + 2.2.1.1 jar From 3200f669ce3ae1fe7685a592d1e2f32eb987b892 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:28:22 -0400 Subject: [PATCH 145/544] Javadoc fix-ups. --- src/main/java/org/owasp/esapi/Encoder.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index a8b949d71..4c831d281 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -149,11 +149,10 @@ * * * - * @see OWASP Cross-Site Scripting Prevention Cheat Sheet. + * @see OWASP Cross-Site Scripting Prevention Cheat Sheet * @see OWASP Proactive Controls: C4: Encode and Escape Data - * @see Properly encoding and escaping for the web. - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @see Properly encoding and escaping for the web + * @author Jeff Williams (jeff.williams .at. owasp.org) * @since June 1, 2007 */ public interface Encoder { @@ -167,7 +166,7 @@ public interface Encoder { * Encoder.AllowMixedEncoding=false * * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize + * @see #canonicalize(String, boolean, boolean) * @see W3C specifications * * @param input the text to canonicalize @@ -178,7 +177,7 @@ public interface Encoder { /** * This method is the equivalent to calling {@code Encoder.canonicalize(input, strict, strict);}. * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize + * @see #canonicalize(String, boolean, boolean) * @see W3C specifications * * @param input From 5b95e700c8ef812b01a4d788daad9cf30cf35e29 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:36:37 -0400 Subject: [PATCH 146/544] Added 'IMPORTANT WORKAROUND for 2.2.1.0 ESAPI Logging' section. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index 7d6c54ef7..6119a5c4c 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -124,6 +124,37 @@ Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly Pow We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + + *** IMPORTANT WORKAROUND for 2.2.1.0 ESAPI Logging *** + +Lastly, if you try to use the new ESAPI 2.2.1.0 logging, you will notice that you need to change ESAPI.Logger and also possibly provide some other logging properties as well. This is because the logger packages were reorganized to improve maintainability, but we failed to mention it. To use ESAPI logging in ESAPI 2.2.1.0 (and later), you MUST set the ESAPI.Logger property to one of: + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *must* supply an "esapi-java-logging.properties" file in your classpath. Unfortunately, we failed to drop add that to the ESAPI configuration jar under the GitHub 'Releases', so this file has been added explicitly to the 2.2.1.0 release 'Assets' for this release (for details, see https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.1.0). Even worse, there was a logic error in the static initializer of JavaLogFactory (now fixed in the 2.2.1.1 patch release) that causes a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory or Slf4JLogFactory, you will also want to ensure that you have the following ESAPI logging properties set to get the logs to appear what you are used to with Log4J 1.x logging: + # Set the application name if these logs are combined with other applications + Logger.ApplicationName=ExampleApplication + # If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true + Logger.LogEncodingRequired=false + # Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments. + Logger.LogApplicationName=true + # Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments. + Logger.LogServerIP=true + # LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you + # want to place it in a specific directory. + Logger.LogFileName=ESAPI_logging_file + # MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) + Logger.MaxLogFileSize=10000000 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. ----------------------------------------------------------------------------- Other changes in this release, some of which not tracked via GitHub issues @@ -209,7 +240,7 @@ Direct and Transitive Runtime and Test Dependencies: [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- - [INFO] org.owasp.esapi:esapi:jar:2.2.1.0-RC1 + [INFO] org.owasp.esapi:esapi:jar:2.2.1.0 [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile From 156b98cd8aaa7a21d483bded315f3509663576b2 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:51:12 -0400 Subject: [PATCH 147/544] New release notes for 2.2.1.1 patch release. --- .../esapi4java-core-2.2.1.1-release-notes.txt | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 documentation/esapi4java-core-2.2.1.1-release-notes.txt diff --git a/documentation/esapi4java-core-2.2.1.1-release-notes.txt b/documentation/esapi4java-core-2.2.1.1-release-notes.txt new file mode 100644 index 000000000..7fa3ffc35 --- /dev/null +++ b/documentation/esapi4java-core-2.2.1.1-release-notes.txt @@ -0,0 +1,237 @@ +Release notes for ESAPI 2.2.1.1 + Release date: 2020-July-26 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.1.0, 2020-July-12 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +This is a patch release to address GitHub issue #560. See that GitHub issue and + +Also special props to Bill Sempf for stepping up and volunteering to prepare the initial cut of these release notes. Had he not done so, this release either would not have release notes or it would have been delayed another 6 months while I procrastinated further with various distractions. (Squirrel!) + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.1.0 release: + 211 Java source files + 4309 JUnit tests in 134 Java source files + +ESAPI 2.2.1.1 release: + 211 Java source files + 4312 JUnit tests in 134 Java source files + +39 GitHub Issues closed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +560 - Could not initialize class org.owasp.esapi.logging.java.JavaLogFactory (ESAPI 2.2.1.0) +561 - Update ESAPI-release-steps.odt to note how to do 'Release' on GitHub +564 - Create release notes for 2.2.1.1 patch release + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +As of ESAPI 2.2.1.0 (the previous release), the new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +However, if you try to juse the new ESAPI 2.2.1.0 logging you will notice that you need to change ESAPI.Logger and also possibly provide some other properties as well to get the logging behavior that you desire. + +To use ESAPI logging in ESAPI 2.2.1.0 (and later), you will need to set the ESAPI.Logger property to + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *MUST* supply an "esapi-java-logging.properties" file in your classpath. This file is included in the 'esapi-2.2.1.1-configuration.jar' file provided under the 'Assets' section of the GitHub Release at + https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.1.1 + +Unfortunately, there was a logic error in the static initializer of JavaLogFactory (now fixed in this release) that caused a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory, you will also want to ensure that you have the following ESAPI logging properties set: + # Set the application name if these logs are combined with other applications + Logger.ApplicationName=ExampleApplication + # If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true + Logger.LogEncodingRequired=false + # Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments. + Logger.LogApplicationName=true + # Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments. + Logger.LogServerIP=true + # LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you + # want to place it in a specific directory. + Logger.LogFileName=ESAPI_logging_file + # MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) + Logger.MaxLogFileSize=10000000 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. + + +Related to that aforemented Log4J 1.x CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is *NOT* affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) + +----------------------------------------------------------------------------- + + Known Issues / Problems + +----------------------------------------------------------------------------- +If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + +I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html +and + https://github.com/bcgit/bc-java/issues/477 +I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + + +Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + +We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Updates to README.md fileg +* Minor Javadoc fixes to org.owasp.esapi.Encoder +* Fixes / cleanup to 2.2.1.0 release notes (documentation/esapi4java-core-2.2.1.0-release-notes.txt) + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.1.0 and 2.2.1.1, i.e., between 2020-07-12 and 2020-07-26) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +jeremiahjstacey 5 5 1 +kwwall 67 64 8 +======================================================== + Total: 21 + + +----------------------------------------------------------------------------- + + +2 Closed PRs merged since 2.2.1.0 release (those rejected not listed) +====================================================================== +PR# GitHub ID Description +---------------------------------------------------------------------- +559 -- synk-bot -- Upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5 +562 -- jeremiahjstacey -- Issue #560 JUL fixes + +CHANGELOG: Create your own. May I suggest: + + git log --since=2020-07-13 --reverse --pretty=medium + + which will show all the commits since just after the last (2.2.1.0) release. + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.1.1 + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.1.1 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.5.10:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.12:compile + [INFO] | | \- org.apache.httpcomponents:httpcore:jar:4.4.13:compile + [INFO] | \- commons-codec:commons-codec:jar:1.14:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.13:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xerces:xercesImpl:jar:2.12.0:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.0.5:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.65.01:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + +elangoravi for bringing GitHub issue #560 to our attention. This is one where we thought the workaround instructions was harder than just trying to fix it and thus we were encouraged to release a patch. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) From 74fc4ba1fa9d356efa5e7052286482c689aafa09 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 27 Jul 2020 00:01:15 -0400 Subject: [PATCH 148/544] Close #561 plus other major changes and cleanup. --- documentation/ESAPI-release-steps.odt | Bin 273380 -> 165319 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/ESAPI-release-steps.odt b/documentation/ESAPI-release-steps.odt index bf188f05f965c9d5051d1f4fc60a4ff4df2e1b23..33e7a367d25f75d5535c1da7d9ec4f8ee7888f6c 100644 GIT binary patch delta 58060 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

+ * The primary use of ESAPI {@code Encoder} is to prevent XSS vulnerabilities by + * providing output encoding using the various "encodeForXYZ()" methods, + * where XYZ is one of CSS, HTML, HTMLAttribute, JavaScript, or URL. When + * using the ESAPI output encoders, it is important that you use the one for the + * appropriate context where the output will be rendered. For example, it + * the output appears in an JavaScript context, you should use {@code encodeForJavaScript} + * (note this includes all of the DOM JavaScript event handler attributes such as + * 'onfocus', 'onclick', 'onload', etc.). If the output would be rendered in an HTML + * attribute context (with the exception of the aforementioned 'onevent' type event + * handler attributes), you would use {@code encodeForHTMLAttribute}. If you are + * encoding anywhere a URL is expected (e.g., a 'href' attribute for for <a> or + * a 'src' attribute on a <img> tag, etc.), then you should use use {@code encodeForURL}. + * If encoding CSS, then use {@code encodeForCSS}. Etc. This is because there are + * different escaping requirements for these different contexts. Developers who are + * new to ESAPI or to defending against XSS vulnerabilities are highly encouraged to + * first read the + * + * OWASP Cross-Site Scripting Prevention Cheat Sheet. + *