From 9147a1b178458036d6ae8be370b950f03720eec8 Mon Sep 17 00:00:00 2001 From: Type-kun Date: Sun, 18 Sep 2016 14:13:11 +0500 Subject: [PATCH] Update APNGInspector class and add tests (fixes #2237) --- app/logical/apng_inspector.rb | 180 ++++++++++++++++---------- test/files/apng/actl_wronglen.png | Bin 0 -> 6675 bytes test/files/apng/actl_zero_frames.png | Bin 0 -> 6679 bytes test/files/apng/broken.png | Bin 0 -> 400 bytes test/files/apng/empty.png | 0 test/files/apng/iend_missing.png | Bin 0 -> 6667 bytes test/files/apng/jpg.png | Bin 0 -> 989 bytes test/files/apng/misaligned_chunks.png | Bin 0 -> 6679 bytes test/files/apng/normal_apng.png | Bin 0 -> 6679 bytes test/files/apng/not_apng.png | Bin 0 -> 400 bytes test/files/apng/single_frame.png | Bin 0 -> 11246 bytes test/unit/apng_inspector_test.rb | 64 +++++++++ 12 files changed, 175 insertions(+), 69 deletions(-) create mode 100644 test/files/apng/actl_wronglen.png create mode 100644 test/files/apng/actl_zero_frames.png create mode 100644 test/files/apng/broken.png create mode 100644 test/files/apng/empty.png create mode 100644 test/files/apng/iend_missing.png create mode 100644 test/files/apng/jpg.png create mode 100644 test/files/apng/misaligned_chunks.png create mode 100644 test/files/apng/normal_apng.png create mode 100644 test/files/apng/not_apng.png create mode 100644 test/files/apng/single_frame.png create mode 100644 test/unit/apng_inspector_test.rb diff --git a/app/logical/apng_inspector.rb b/app/logical/apng_inspector.rb index d8ade47db..30ce02676 100644 --- a/app/logical/apng_inspector.rb +++ b/app/logical/apng_inspector.rb @@ -1,70 +1,112 @@ -class APNGInspector - attr_reader :frames - - PNG_MAGIC_NUMBER = ["89504E470D0A1A0A"].pack('H*') - - def initialize(file_path) - @file_path = file_path - @corrupted = false - @animated = false - end - - #Calls associated block for each PNG chunk - #parameters passed are |chunk_name, chunk_length, file_descriptor| - #returns true if file is read succesfully from start to IEND, false otherwise. - def each_chunk - iend_reached = false - File.open(@file_path, 'rb') do |file| - return false if file.read(8) != PNG_MAGIC_NUMBER - - while chunkheader = file.read(8) - return false if chunkheader.to_s.length != 8 - - chunk_len, chunk_name = chunkheader.unpack("Na4") - current_pos = file.tell - yield chunk_name, chunk_len, file - if chunk_name == "IEND" - iend_reached = true - break - end - file.seek(current_pos+chunk_len+4, IO::SEEK_SET) - end - end - return iend_reached - end - - def inspect! - actl_corrupted = false - - read_success = each_chunk do |name, len, file| - if name == 'acTL' - if len != 8 then - actl_corrupted = true - else - framedata = file.read(4) - if framedata.to_s.length != 4 - actl_corrupted = true - else - framecount = framedata.unpack("N")[0] - if framecount < 1 - actl_corrupted = true - else - @animated = true - @frames = framecount - end - end - end - end - end - @corrupted = !read_success || actl_corrupted - return !@corrupted - end - - def corrupted? - @corrupted - end - - def animated? - !@corrupted && @animated && @frames > 1 - end +class APNGInspector + attr_reader :frames + + PNG_MAGIC_NUMBER = ["89504E470D0A1A0A"].pack('H*') + + def initialize(file_path) + @file_path = file_path + @corrupted = false + @animated = false + end + + #PNG file consists of 8-byte magic number, followed by arbitrary number of chunks + #Each chunk has the following structure: + #4-byte length (unsigned int, can be zero) + #4-byte name (ASCII string consisting of letters A-z) + #(length)-byte data + #4-byte CRC + # + #Any data after chunk named IEND is irrelevant + #APNG frame count is inside a chunk named acTL, in first 4 bytes of data. + + + #This function calls associated block for each PNG chunk + #parameters passed are |chunk_name, chunk_length, file_descriptor| + #returns true if file is read succesfully from start to IEND, + #or if 100 000 chunks are read; returns false otherwise. + def each_chunk + iend_reached = false + File.open(@file_path, 'rb') do |file| + #check if file is not PNG at all + return false if file.read(8) != PNG_MAGIC_NUMBER + + chunks = 0 + + #We could be dealing with large number of chunks, + #so the code should be optimized to create as few objects as possible. + #All literal strings are frozen and read() function uses string buffer. + chunkheader = '' + while file.read(8, chunkheader) + #ensure that first 8 bytes from chunk were read properly + if chunkheader == nil || chunkheader.length < 8 + return false + end + + current_pos = file.tell + + chunk_len, chunk_name = chunkheader.unpack("Na4".freeze) + return false if chunk_name =~ /[^A-Za-z]/ + yield chunk_name, chunk_len, file + + #no need to read further if IEND is reached + if chunk_name == "IEND".freeze + iend_reached = true + break + end + + #check if we processed too many chunks already + #if we did, file is probably maliciously formed + #fail gracefully without marking the file as corrupt + chunks += 1 + if chunks > 100000 + iend_reached = true + break + end + + #jump to the next chunk - go forward by chunk length + 4 bytes CRC + file.seek(current_pos+chunk_len+4, IO::SEEK_SET) + end + end + return iend_reached + end + + def inspect! + actl_corrupted = false + + read_success = each_chunk do |name, len, file| + if name == 'acTL'.freeze + framecount = parse_actl(len, file) + if framecount < 1 + actl_corrupted = true + else + @animated = true + @frames = framecount + end + end + end + + @corrupted = !read_success || actl_corrupted + return !@corrupted + end + + def corrupted? + @corrupted + end + + def animated? + !@corrupted && @animated + end + + private + + #return number of frames in acTL or -1 on failure + def parse_actl(len, file) + return -1 if len != 8 + framedata = file.read(4) + if framedata == nil || framedata.length != 4 + return -1 + end + return framedata.unpack("N".freeze)[0] + end + end \ No newline at end of file diff --git a/test/files/apng/actl_wronglen.png b/test/files/apng/actl_wronglen.png new file mode 100644 index 0000000000000000000000000000000000000000..b91ae886efae3468627296aab9d03f05a5859f03 GIT binary patch literal 6675 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 dO%=u>9r>xhPt_BTzc)I$ztaD0e0sxz7+z0>w literal 0 HcmV?d00001 diff --git a/test/files/apng/actl_zero_frames.png b/test/files/apng/actl_zero_frames.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba18e9e4b63841c4ecea2b1d4389d1c5994b2e2 GIT binary patch literal 6679 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 literal 0 HcmV?d00001 diff --git a/test/files/apng/broken.png b/test/files/apng/broken.png new file mode 100644 index 0000000000000000000000000000000000000000..d310545dfc0bc184ed5639bb3abc90fc287d6007 GIT binary patch literal 400 zcmeAS@N?(olHy`uVBq!ia0y~yV3-EN9Bd2>3^t5~j~EyjI1-aXd>9xQm>C!t7|y-j zb%23^K`IR*1`+~m05L$A`33U}2F8@4l9>z)95X##978JRyuIt~FLGPz*vJ377dY;6 zl=3`r(A(tZu?F=9FBywNox*N?UoEWWzwY^BQFi3W$+Ra2I+Q*NE$(!4b#5}5p7$m% z;x*4Uxw}!kCb8Q?zy7mxxbD2R{ne{gQCl4}mre=t(tOJ5X>}lRho@Kk>mG~K9b0)M zHLN8B+%G!K=eTRJi$S1lZu0R1+k~^2W{YVZ+o0AqReA546Tz!E|9j4S?7C4)m`8Ng zAH@PGi>C`@o;~LdY52}}&(wR5rv65;XV0GpW_5?kreC>Y`M%}zIYXJ}+gu|=j~CxQ z{rM8N-O-(I*LZ|(oVHwZ+Vz=51%jtNE>$uZtl_X($Fr&8kKooNkAEnM9=#=zykqr2 sQDvV literal 0 HcmV?d00001 diff --git a/test/files/apng/empty.png b/test/files/apng/empty.png new file mode 100644 index 000000000..e69de29bb diff --git a/test/files/apng/iend_missing.png b/test/files/apng/iend_missing.png new file mode 100644 index 0000000000000000000000000000000000000000..4c008b425f283a8f728d0483c6ebd01e81a7341e GIT binary patch literal 6667 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 XrYcBoH?*n3^N-!4viOws`qI?^<)GOb literal 0 HcmV?d00001 diff --git a/test/files/apng/jpg.png b/test/files/apng/jpg.png new file mode 100644 index 0000000000000000000000000000000000000000..dc221d89190765a79ad275a98423c7772e726a40 GIT binary patch literal 989 zcmex=Bm<7(6|-7&sUh7+4vk7+Dz@85kJ6 z7#J9&q3j?A1_ljAX0SLD0|SF0BNKxV0|P@80|NsiGemt30|X?1#26Sr#)H)dBxdH7 z==r&ZD3lrNnd%uDGAw9gWa!_>!0`VzgEIp&6B7tAGc&U=GqbRAu(GnSuyV1pvvKfn z@$hhSadY$X2?_A>3G#7s3y28_3X6z}it_M_ONfa`2#JV_fDB<|W?^AvVdZ3HECr+Na zbot8FYu9hwy!G(W<0ns_J%91?)yGetzkL1n{m0K=Ab&A3FoS&sA|O6P^Oqn46C)D~ z3o{ElD+>z)BU3rZ6d z`Dm`>mh%EX(z3gw>*Zek+9K6szH3*sM5?$>molf!?a8NeZ$JJyMO*ue)xPqmda@X-u4lDlueknb`SB-L zq7UzS-&SL~%&c)`Y}P;JOL`?A-Y2h!iS5ov+Tn38eL}~TO}!IWTN~#I9@lS~rxek? bc3^t5~j~EyjI1-aXd>9xQm>C!t7|y-j zb%23^Q7R201`+~m05L$A`33U}2F8@4l9>z)95X##978JRyuIt~FLGPz*vJ377dY;6 zl=3`r(A(tZu?F=9FBywNox*N?UoEWWzwY^BQFi3W$+Ra2I+Q*NE$(!4b#5}5p7$m% z;x*4Uxw}!kCb8Q?zy7mxxbD2R{ne{gQCl4}mre=t(tOJ5X>}lRho@Kk>mG~K9b0)M zHLN8B+%G!K=eTRJi$S1lZu0R1+k~^2W{YVZ+o0AqReA546Tz!E|9j4S?7C4)m`8Ng zAH@PGi>C`@o;~LdY52}}&(wR5rv65;XV0GpW_5?kreC>Y`M%}zIYXJ}+gu|=j~CxQ z{rM8N-O-(I*LZ|(oVHwZ+Vz=51%jtNE>$uZtl_X($Fr&8kKooNkAEnM9=#=zykqr2 zQDvV^Doh#yKi)d+MZV37*l*f{QI`W3LD0#wP>qyv|t=v<%XUB*ipSfe3q`LZ^T)u+*7o1a7v>mgr-*%lSvfAiifPZiFxk8<;^WR%J;-y^ko_`bzd~tMDpRpEio|Sw3 zjrnht1-HEseD}rvaq*?dMl@s>*LG z|MM5`R~DZ34f3+`zG9^{ZMRqX+EcxI{{PBuFP2#yy7S?>OLI%6X_SVnxy7DVTs?P{ zaG&s2{xfQ_%3key*REMED=K%j^_k`!{d|U(m0Jj3C@2JX=WP4`_|b*bnX$VC-hKX^ zS-2ue)NJRv{S4OWx_euF*Qw7{=E@acT{X=+vi6HA=Oc??+u3`AyS8<0webzqxxR>N za(L3tZ8pA!FJ%(5R!208rfvH4Bz=~}3+XimlS}#xWOqbJC-X=jf2wi#$;`r-dlKE{ z|5B###jJV9X1vd@=0hXv<+~@&1-^E0y(y9WJqDS8J7&LHl}a%nftkrHL(8q?|t_-NBI5u_qgWS?|u1^YfiB^ z7w-4;|NB7Zu${g9{Y??ATJ=XHUH+bXZkQ3dCSQM2DCeh2o^Mvitwf`_u6Aut7KqR> z_tbn79=H0NrN(coP@N4C@vp1iYU$RNNF7OnPg+@W+ws8#(rU z`d2IHIn~DhxYP3Dl^!hO-Zy$J+p~^d-pH}<&Vx_87dhSfVI{U!Z$jR2Sr^vNV9kqO zzhBzAK3TA4C!^V8h&^ldCcK(etqHMa<{w?HDPjBiTt9>_OjLcRpfzKCvS7p-<-K+P z3?rYsj$o1BI4?-lfB)^xE(;#LcA8av_fn-i#Mc)#MfCkyvvzuzUBBb$BvbD_oyHx% z=AKW!uqooH>NS^$UgMr$jxC|{uTQ%7d81^ZO}}=>FYEi?|IRzQX>Z;CLc7nk_uogZ zxx{Vv@FlC&ddV;^{U1|Gdi>t}W?6aQu|?Ag`Ar+nf7cC5o#3i$@|a`s0gkh$6uLd1 zzqqpRsMWuYS*8E}FWk4G@95+HKQ(Nx)7fYFY`lG#`GD&j*T&!5*6%G}kR)4sa@B#L zD~X@{5?;PIofBhi^Wn?-xpL2VZ-7!C zuc~HNek{4>F^e;2{q)VnTbj(eOaBO(rtgpsow28=?RVr4)gPO;Ex$26Zu5hd?Sd&M zRn_#C| zFFeiU8STH^PWu(VvE$@DMH{oW)_>tQ_b$G8F}tTreWvpdiLe=W1nk88(|&i{|H$+x zZ_=f*srA;!HySXd&gF>z<@M+I^_%AGS3h=Nf1-KSZQ^aEoK``N(=Io%M0U>o8szAa z|3zDWV_JWKd%fniG@jDx7Hd2E7Jk_u^x3x%NhUxV~w>t!duVzb$bs`n^iqKY#lAeX~aw zYyID-HK98jAG?Iy_Aq^zyz$6gn>j1kJJ&=oywke7mH$lAPLZc&LB}@)*qog6=jHS6 z%)DhhJZp=;JG5@H0yp>?}Muut2HTlA381|jMb~!biCqmmMM&Ee;*7h%lCU|VzcUvNP z$L@nY&7Dg&Z#l@R^Y)9u{H5tv4o~Ykcn5nOm9g;K^sc1Z?KZv}_ne#3+`T0AYuVwh z1CLK7n5AhPR}DS-E3$M>VDR%j!so1y7tFaWmKZ8?K3D(z=Q3?Iot3)R&LdT>PH{K? z-j-f;-@4+u&PTQW?FomTxo)g`s-{`H*7%W*@-?HB1#c1@WL&vxl}i5Crv@cnw=Cny zv0kXQA#-68Pj*s-<)zbJarwJ7z;!+a#7eWj{8`7#em;!Ztk1x}0Bg!Hg4#V$3~I?R zK$l zDjl}&c;k#aE&C7eKm6pG#KTQxM~{hWi>pZ_J@OLn70maTASf|m`pV~e59Sn~-^ugY z>HL+D@1G<6!);6cW>oE3tzmFXBDu%#n2V#6_yjj&ug3X3i+ffteIB{qKyBglj%!+qv`*Z|mz-B9n9C zTUH$q545|^|8qw~it)*ByAL*9UFg-zS$fB9=h{;d`oF~znIG=0xTi1jy6g#g|C0kWnIm>IlAX+vCRR)mGcf>_&Uc; zroAGpxHNA6jw5a#s+`0xoKM>>VJ(%w^_1`99BtPM^MWHqCmN0xZK}%3>#ccf6m0Qs z_3_qq%Y`3(sJJImw?XES{}R^M`C6v$DjzDIDtHkkr1-}2=~bu3srSmV87ek;eoUC) z&SCC+{gUXZX@Im@Zc z{hs;LExw70iMRip6EZu{?$2wNrN>%YUzNPtsSr@N>9GBzEMwt=TH%_N4>z{1GW;52 zQ4n$})XQ_r^j`^QHCiW2m+oGkb%w*l*Z#Gdk> z+W6UI@~ec}8>v>S&#ye^{P0$L?6UKh-LjfPZzdXL`(K&-)Y@J79wj>f7x7ad-QuMOcj@3w^@>AOkL`3Geif9e-~IaqY+ z?gi5nU+w2MuM$GDe7>BnuRST$`q8VmbF-ar>4qzX)+M}=SKr)mm({t_BqHgo~uk*ONNT=FhKZ=q%!y zR$f+rRbQ&tb;?1ZRFe|kXAz4xb{IuyyS`{Jfdf3Pu+Zg9soTV4Yj z^X$}b0{)^5`3JA9!urPYGZef1i%-!WefR+UYSt@$i}MdirmUvIKL z|17x3&aDvs+;qXh*C!ZkEwB47Rf^Ep+t~O)uS9mG`kv~-m0Q!>S8|9QI3RfX(T9bv zJ+updG%8Pv{#%pP^!1UDbj7`vb;?_9@}u@fId(O%2K+c^l%!oDr2a;yEh=-`e>qZ=T$^lIPlIq0p6^MRxVw@SMB!`OeEP{M{a*jX6~r8Kf5R3 zx9R!($eNB<2~V%v>@U?0neb5i+W%E=3|3k$yIdEn-&ebGbICmQSGwP?zU52$m@@g% zL4|&SytZ{qwWs(yx9@rLYhC5APljJpJ}epnsH(y7r2j zy2oBw$~~A7az5cjm7j8-v(4(AR_mSFUiDWwi3?2LY5M85tJ>V6aLd~7-{OQLz8|`M zF)LepO7&6f^_u!$Ej3NCH7rwBPbq4?6ZEj{Q0djnF(S2)|0P=S_&PXr?Fcr$J#$sW;Vy`n3TP< z{ZI57LTYPpl}!%eBmIy2#JQY_8H$%?A+<(8gDumLTO-^%Km#kst}`$(FmQN7TO%x> z*2sPDkXUi{5ASasKUwHjtmY}#t?|N-!}G-=?Uzb&6Pki5^uD}Zzj{wa;<5t*OS(U- z>OL^x!0rVVC**jF{}|fm*i`QoU;IS+bH=qD*X+`-J&%exy?6h)IhN_~Z#GcuIj)Ii z%#IOHY_>@qF0kQw-rb;m;4_1R<$>>wE7=`NK3q2J_|6#4zJtx(x?%l+-M$r3#}mqA z>w`i+pXYQeXM3HwwBz2H(uQ?S1+4S9W&i%|(@OZm`@#K<>HXww8*W~_KcC^Q@S31f zmV3QSHe4Sj9TYA8QU*szT^t4tmY^@&vL+g z!kqoRj8*L!^_sqn-lrbO=hx}BAA7)Z;x1G5^1J8s6`vpc$Ga)oJF}wOr*4DS)xwHh z+y(Q?wC6gOTGVyE{IjV{eZTU~gH_ui8_wG_EYH5Rb`Ixp`3JMkf7}ppG4E-(igCo# zyM_fOobRi*Mq37-d)xSk)#vN_jO|5HC%LX^zdJbnmI}w?TWdR#bRH+=F_ve<@18n; z>YN1KB01}=k_LL&+ZQ!`;AGypbBT?fm)nN8kgkk5)hy4rZ>?RS^sDR5_TxLm|0yr} z&>Xs|@4#i91AlL=ZD}k!_>c47tN-r1CqHhHT@&-ta+bt-&lF}f#~;)?L{FG2Y&_4 zesk-0$o}R+$@r|vw{m4f{WhH1og7>4zIQUmV~zKHncE9BdJ=4J>ohE0@b9ru=VDKr z+i%|d`Wr9ewxQ-(V=dDghWFJEjrq6SW^m#N%eK90`qnJy@MY5}t6ohP*>!MM{OPS? zFK70BDiQyjZFyF#r1`4E9FOy-&QBA3yzw z!t*Xk=5{KK)z@!rT;iObbo#b4d-RqwGapGut&g%g*tPgJN9{>Js|yDsx+n8r`xC|Y zq-m$~oT?|sOzad-v9FHfD8F4TwekFdID!A})A+-Vm>&E z51U1ICZ#k#y}KgA_Ve5iH@NqUZesQBeRJb9>+8&=8vlK(o>|Lfoo(dVnea7jUH4U^ zx18r1A4Dt0PFc)FPsJcbAKDE!;VQ zb6PTYm`2MpCyfZZ6S)s!Ju~!L^zO}#jc0XE(@SZ3TIsN)&vNF47>>P36JPZS7Ed`7 z!@+v`w%?qD6K7g|Hbg!DJ5zw;Z0nV|-(NhDH*|7&c4%AtOk+jSH4!WNEN7mGIlwf4v{Qepq4_?D8?2VNKBP-5(A{bYJGY zcI78;!@ZSP-f4YVXL{|^mu>Z~zr7WnUwyS)*0MjN-gEVyxDCtBtGRNKpx*|D2T6I1E&jK1RrnqX>pg3#I?AsdQJx{s zywg&k_G+H0VaL8C1@m@mkt^oET10ggYz@`<{npT7_pP-p{bdvXev8QRzMk14uEVe+ z{07T!^>3aZCC!sYEtzhJUaAvmcn#y1Mt;j5D%5ffGD-HwsT(rToE! zyI_BrcH)9qi#n~nSAJ|c#4pCMXNAZ`i|-s~#J5BTx_%M8ncweR``cPe@*q=Gy4<@f zX$N+!x!c8cNS)`&Gp9WP`RbMjnHs|HH0y8m6gajuTJUJZv7>jKGPaBKOsz8Nh5PIen9gti^ydARNz!Ya zm%g&Fxyx#y%I+8_+ZW%q;mgx<75fMVgZ6Go$wurwh zb+T$IU9rcuWk)*G18I+Q_m45`b>6an$xgSumnRwhSRQ;&&PnDj+fKs`=QFln7Tc+I zW4%IG{Bq5J_Dhm$oIhtXVPHU3@b}_CeI^9I5!m^4TA@ l^c{Rt6(qMC+En5B$8J$se9C%#>1xoJho`Hb%Q~loCIC5b+zkK# literal 0 HcmV?d00001 diff --git a/test/files/apng/normal_apng.png b/test/files/apng/normal_apng.png new file mode 100644 index 0000000000000000000000000000000000000000..f7964259239472f5febd069a1bc3ee816c190e62 GIT binary patch literal 6679 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 literal 0 HcmV?d00001 diff --git a/test/files/apng/not_apng.png b/test/files/apng/not_apng.png new file mode 100644 index 0000000000000000000000000000000000000000..70075493f33c7f2348599169c3a2f1c101430e24 GIT binary patch literal 400 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