From 9b73c03fa3392e076cfed8d72833bf999a989b7b Mon Sep 17 00:00:00 2001 From: Roy Li Date: Wed, 27 Nov 2019 12:01:04 +0800 Subject: [PATCH] fix(gateway): content-type validation --- lib/gateway/server.ts | 8 +- test/gateway/index.test.ts | 2 +- test/gateway/snapshots/index.test.ts.md | 122 ++++++++++++++++++++++ test/gateway/snapshots/index.test.ts.snap | Bin 4905 -> 5632 bytes 4 files changed, 129 insertions(+), 3 deletions(-) diff --git a/lib/gateway/server.ts b/lib/gateway/server.ts index 8d073801f..d731f7c4d 100644 --- a/lib/gateway/server.ts +++ b/lib/gateway/server.ts @@ -167,8 +167,11 @@ export class Server { const contentType: string = content.headers['content-type']; if ( - !contentType.includes('text/plain') || - !contentType.includes('application/javascript') + !contentType || + ( + !contentType.includes('text/plain') && + !contentType.includes('application/javascript') + ) ) { ctx.throw(400, '该文件不是一个可转换的脚本文件'); return; @@ -204,6 +207,7 @@ export class Server { const contentType: string = content.headers['content-type']; if ( + !contentType || !contentType.includes('text/plain') ) { ctx.throw(400, '该文件不是一个可转换的文件'); diff --git a/test/gateway/index.test.ts b/test/gateway/index.test.ts index 5de4fb24e..264c73dee 100644 --- a/test/gateway/index.test.ts +++ b/test/gateway/index.test.ts @@ -235,7 +235,7 @@ test('qx-rewrite-remote with binary', async t => { const surgioServer = gateway.createSurgioServer(fixture); const app = gateway.createKoaApp(surgioServer); - const res = await request(app.callback()) + await request(app.callback()) .get('/qx-rewrite-remote?url=https://github.com/crossutility/Quantumult-X/blob/master/sample.conf') .expect(400); diff --git a/test/gateway/snapshots/index.test.ts.md b/test/gateway/snapshots/index.test.ts.md index 4c8462b4b..ff411b166 100644 --- a/test/gateway/snapshots/index.test.ts.md +++ b/test/gateway/snapshots/index.test.ts.md @@ -387,3 +387,125 @@ Generated by [AVA](https://ava.li). `🇺🇸US 1 = custom, us.example.com, 443, chacha20-ietf-poly1305, password, https://raw.githubusercontent.com/ConnersHua/SSEncrypt/master/SSEncrypt.module, obfs=tls, obfs-host=gateway-carry.icloud.com␊ 🇺🇸US 2 = custom, us.example.com, 443, chacha20-ietf-poly1305, password, https://raw.githubusercontent.com/ConnersHua/SSEncrypt/master/SSEncrypt.module␊ 🇺🇲 US = custom, us.example.com, 443, chacha20-ietf-poly1305, password, https://raw.githubusercontent.com/ConnersHua/SSEncrypt/master/SSEncrypt.module, udp-relay=true, obfs=tls, obfs-host=gateway-carry.icloud.com, tfo=true` + +## qx-script + +> Snapshot 1 + + `/**␊ + * @supported 55BE3B10F8A1␊ + * THIS COMMENT IS GENERATED BY SURGIO␊ + */␊ + ␊ + /**␊ + * @supported 23AD6B11CD4B 55BE3B10F8A1␊ + * The above random generated device ID can be found at the bottom of Quantumult X additional menu, and may be changed when system restored.␊ + * Indicate what device are supported by the file. This is necessary when the file is not loaded from local("On My iPhone - Quantumult X - Scripts").␊ + */␊ + ␊ + // $request, $response, $notify(title, subtitle, message), console.log(message)␊ + // $request.scheme, $request.method, $request.url, $request.path, $request.headers␊ + // $response.statusCode, $response.headers, $response.body␊ + // You can optional change the response headers at the same time by using $done({body: modifiedBody, headers: modifiedHeaders}); only change the response headers is not allowed for script-response-body. The modifiedHeaders can be copied and modified from $response.headers, please do not change the content length, type and encoding field.␊ + // Response status can also be optional changed by using $done({body: modifiedBody, headers: modifiedHeaders, status: modifiedStatus}), the modifiedStatus should be like "HTTP/1.1 200 OK"␊ + ␊ + var body = $response.body;␊ + var obj = JSON.parse(body);␊ + ␊ + obj['result'] = 0;␊ + body = JSON.stringify(obj);␊ + ␊ + console.log(body);␊ + ␊ + $done(body);␊ + ` + +> Snapshot 2 + + `/**␊ + * @supported abcdef␊ + * THIS COMMENT IS GENERATED BY SURGIO␊ + */␊ + ␊ + /**␊ + * @supported 23AD6B11CD4B 55BE3B10F8A1␊ + * The above random generated device ID can be found at the bottom of Quantumult X additional menu, and may be changed when system restored.␊ + * Indicate what device are supported by the file. This is necessary when the file is not loaded from local("On My iPhone - Quantumult X - Scripts").␊ + */␊ + ␊ + // $request, $response, $notify(title, subtitle, message), console.log(message)␊ + // $request.scheme, $request.method, $request.url, $request.path, $request.headers␊ + // $response.statusCode, $response.headers, $response.body␊ + // You can optional change the response headers at the same time by using $done({body: modifiedBody, headers: modifiedHeaders}); only change the response headers is not allowed for script-response-body. The modifiedHeaders can be copied and modified from $response.headers, please do not change the content length, type and encoding field.␊ + // Response status can also be optional changed by using $done({body: modifiedBody, headers: modifiedHeaders, status: modifiedStatus}), the modifiedStatus should be like "HTTP/1.1 200 OK"␊ + ␊ + var body = $response.body;␊ + var obj = JSON.parse(body);␊ + ␊ + obj['result'] = 0;␊ + body = JSON.stringify(obj);␊ + ␊ + console.log(body);␊ + ␊ + $done(body);␊ + ` + +> Snapshot 3 + + `/**␊ + * @supported abcdef bcdefg␊ + * THIS COMMENT IS GENERATED BY SURGIO␊ + */␊ + ␊ + /**␊ + * @supported 23AD6B11CD4B 55BE3B10F8A1␊ + * The above random generated device ID can be found at the bottom of Quantumult X additional menu, and may be changed when system restored.␊ + * Indicate what device are supported by the file. This is necessary when the file is not loaded from local("On My iPhone - Quantumult X - Scripts").␊ + */␊ + ␊ + // $request, $response, $notify(title, subtitle, message), console.log(message)␊ + // $request.scheme, $request.method, $request.url, $request.path, $request.headers␊ + // $response.statusCode, $response.headers, $response.body␊ + // You can optional change the response headers at the same time by using $done({body: modifiedBody, headers: modifiedHeaders}); only change the response headers is not allowed for script-response-body. The modifiedHeaders can be copied and modified from $response.headers, please do not change the content length, type and encoding field.␊ + // Response status can also be optional changed by using $done({body: modifiedBody, headers: modifiedHeaders, status: modifiedStatus}), the modifiedStatus should be like "HTTP/1.1 200 OK"␊ + ␊ + var body = $response.body;␊ + var obj = JSON.parse(body);␊ + ␊ + obj['result'] = 0;␊ + body = JSON.stringify(obj);␊ + ␊ + console.log(body);␊ + ␊ + $done(body);␊ + ` + +> Snapshot 4 + + `/**␊ + * @supported abcdef␊ + * THIS COMMENT IS GENERATED BY SURGIO␊ + */␊ + ␊ + /**␊ + * @supported 23AD6B11CD4B 55BE3B10F8A1␊ + * The above random generated device ID can be found at the bottom of Quantumult X additional menu, and may be changed when system restored.␊ + * Indicate what device are supported by the file. This is necessary when the file is not loaded from local("On My iPhone - Quantumult X - Scripts").␊ + */␊ + ␊ + // $request, $response, $notify(title, subtitle, message), console.log(message)␊ + // $request.scheme, $request.method, $request.url, $request.path, $request.headers␊ + // $response.statusCode, $response.headers, $response.body␊ + // You can optional change the response headers at the same time by using $done({body: modifiedBody, headers: modifiedHeaders}); only change the response headers is not allowed for script-response-body. The modifiedHeaders can be copied and modified from $response.headers, please do not change the content length, type and encoding field.␊ + // Response status can also be optional changed by using $done({body: modifiedBody, headers: modifiedHeaders, status: modifiedStatus}), the modifiedStatus should be like "HTTP/1.1 200 OK"␊ + ␊ + var body = $response.body;␊ + var obj = JSON.parse(body);␊ + ␊ + obj['result'] = 0;␊ + body = JSON.stringify(obj);␊ + ␊ + console.log(body);␊ + ␊ + $done(body);␊ + ` diff --git a/test/gateway/snapshots/index.test.ts.snap b/test/gateway/snapshots/index.test.ts.snap index fab4fe9bfb7c37f437fcf5388a8debd0ec830bd4..0b9d19aa3abb981797089c339ca3cc47ccd5539f 100644 GIT binary patch literal 5632 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 aBfIvoYahGzv1=czE&d;yh0GIlmjD1iYDBRB literal 4905 zcmV+^6V~iORzVCWdn$!E)Y*mu@zh>aKG`LqmT&Wc_F6AJ4z`d}+^X#pUhtacA8S`}^04 zp`mp@|7`TnPwjo+)L-1~6+im7pRVJ*%QHhmiC>+)^56egJ@Xve`O}3RZ-2Rt_kLmx z4VC{T_R7Nh|FPxX=l`R#_eZbU;L*()X)Cczu(1sAKD{6cmEB1hW><8x9(d*e|FbDOni0e z%YsE+6B#&`i6NUvB}}rGq?%(fBhs{tvDm2O6>+q~uvu%4n9{CYqKDm)B|2fBu*Yue z8I^VyNWlwEm#~3HC2CTt&4%Y^y^^4SSX7!L$YLLO5N*6* zW-UNt1gDG{xDoA^$~fmu+_On^+LGo;se2|wE00}|VuzSHOPWZhjKYfUWL~$jNN4DF zmyKCY8#I#a5eyhRVPaPhoby;mNNka+j9Y>=MUzlsnGK@Lsd3iLn^{fTI6i(~YW)8D z@3}`jjoQ*IvUDUaa3o^l5{>ZM)^Xe0-~!F@@>J61%ACrq#stv*VCYMYrzBJ)s%w>i z9#zm+&@E}MyjwKLOwcHmN|NsFA~OV;`MgI9p=edr@=RAMvA4s!JfPOh7d%w<%%(cz z%Z|l(U}N4+Nt%_#W*&!3l#HU`hQcB)7c@vq^Vl!%wH%TsX3};nEom1lY?3)?hGb_@ zR-9+_O^{+PkS!`pdCwwdz9DPNK~&=WSAm5FYP{OTjzK(n4NWPMB9e;f6k{iAYeYkh zlP*zb*(VSn*)6Gv0Qd&w9KQv6igBZ76vXIKk{6PIA(Sm=Jr^ix-cs~%k!aWog+(Nr zs1!M_q>ctW!vrG=JRBV!9{x_O^5&(=l}{@N-m6?(s4bkVyt=n?;mgY5*JJVTmt$q3 z7Er;1uf*_=3$AO^$<4BCOu};%F`5-gjhnt@B;>Rlr!Hc8Pvy90Iv8m_o^!FO`L;!g zONwv;28JKcIRtvC&k0S$Wq&&15(DeN;Dk?IkQsXfKRZobc#z>2ECWyLgt`eoo_8$I zrhX0-M#l*`ywwLG=ZKEwkP07|VqjpHob`yVO@~b%6v4G*2a67kKi7xh3M!3dU{?-f z4cf>7+`8_o$kF&EHp*jP`L@jOr|in<=0n6=6EV-xV^npB?Is;eZOf!MnYFZXjI$58 zhv$&NMjzZL@0v@BYbW8h48P*L6Aykj{V@DHs3hfyTkx{Xl&F>otaRC3aIPsAXi(3| zWB4@?1yz89Qg#|?(hQA=EDk2pFJ%*;UJ5hCP3c)iTn4SeP7VQjxQu<^cDrEvaw*F! zg@@q4_H-S(q0uWCb>x|9f#GbZ@$#3ytnB?{`SP*K!7n0eyscpA*lDV;A}g|CE5Qt; zfLY{>;GIlG0klE$NV_#apRls!?F{4;zdMTZDG!;hXL!2%BWOJg+6_O?Vc>}y;I1A2 z_40)`8Tuw%3rsmGGwXug^rlTAZXlP>Lg5T58x|iA%48g=F3c@HQ~YVSY~x!b;x=b{7z2k^Hoy~EJMw!laKo>UuSjrd4N8xZ9oSVJxp);o9c0d~5payu6(+U= zB5dnrFu369tUUp8;#VOyZaZxYyj0|@Sz7~fMKHlsUw`}R@y{BQ3LI3=d|o+lrh_}n zSi&+PNsJ&Kic6yjCCG^cPAxwxOB>`k4{SseLNeFSS-K8vW8De)=J80-7qCepR>Vta zu;M{RMF95d-Y=^A4g=oh3x}qj{lQKVda}d~#Kf&$dy&{Fg(rCcoT;!V5nT3ST9KJJ z_<5gqQJcs{5YycvZ+5j#(qk+|75EWn4zmBHv-K0NK1*y4{I4+tAcC`4qB@Fd!H_i33XAiU3mlbuOydm_00-q?8s20t+D6~kIRAB)Y$zm z2B?J*V{y2`Y@T3GAarK!e4+8ZEOA6Q>_F&3+&^y0J50WH1B^_~u?WCL8VycIr5|}x zkbhIc1l%=QSZuIu8rp681ahmsbELj_tTz9%`squ{7k<-jkYYeqY{!%7hn|n-#6WzS zr?A8@f49uiUo0~v^VIMRwak^I3*ooj3F^*f>c#^U{sg7@h@O*j$t-JOHezvq_43CM z4po1?=juzxYxDbxfmM6ddZaxA6G@UQB6%K8ShhXQGK_L@Q|nbXagMr~445?v&1?)z zW59ySryqoRV-UP+S7frj=Gs7r9iYqc$d$fo5pz1UO_3AR!rfq9Ks-@d-_4xMZzi8! zC^s3!hndh7=H*;|sdDyUtA*h1Z2K~atXjn&Um%83k{J2+h`e_6w_ussGY_3kjo&xE zb3=>X@#J;rcLgeE=T-QEd6d}(u6wM(UelADWR&M-0$x5lwe893r4O$jT?CbD^_Hm0 zQ@jBr>SYCWEODgBX(=V#rexcR-o(sT!veSI!xOdn*OK5gUH#y2?eg38D~BuZo{Y7_ z=YkH_%>p?W0dkzt0xnPTwUq^}(R{bLHKM_+~}>NqTJ4i-}3j88$1f?@imzU4)VOHYk$KY1g z7Z@_6$PB-n)!M9N{iTbwA_rO2oXm>iqu!>Aft%!*qZb|^i z+FUt^`Kd|y&}1~S-6vqmxpyxDqPXqAgvXX+l}VqN1b&zw4IPG^N%WW^l*_wAHCVp< z&hmvr5LJ$`v_S3bXSH*0!Qs+J_4#wPpM73^>(ui3`O2Yv%jaK;2_{w-eqaCmLUrGl ztz45fFrx;+WO{4@Lf_4kiZ+_y`or8BFRK&qb(F~^*qbAK$xw_x`ni7+2O+&gGOrB`X1x*?gHC)6pXfaU#%VbZT-`8)uVft zFYa%PN0qkSDvgMQM6*m_Z+Z(LBRatM+|DUJrtqV-6-^PgY`Edm19yXNk+a9BNkyn` z!U%ye!UTdx#I7G32WlRJ0WFLoLKHWwV$;P7wh6(kjfIC7fK}w%KlinWDIX#Wv%EZ~ zTRV59w&!=TFj9U{$8+PGQseD7Q%bn~NtnTg32cdxdkZiv&*)8`y>j4XnB{6m-&i3* zzK?4~(ncDMs~PrkX%uC66pS)R(;S}P%Y;e1o&l@vqG3q3gJgH0tWaOj-YDJThEu!Vq>}ZJu;(+;*^dx= z25&pgj_&(*vK>7IBXfHr2LZ0=^2NiI4?n7&S%`s%qlmL)x6<6f8XB??=p@T(a!)-| z!pJT3fNkrDd7GtM&U1po0Q6%R+ksZsPV8B}c(H!^O6}zS%3E(#UO&0Kv{<=#Bo=?_ zN0n3WR2ENWy?m^G`q%YW-ebf5;OY9yOVwk)sD5^|ws5?9bYBP0CD`B_uINb{c$SVO zHhp%im4RzS?J;A=coL3+`H!my4_5Y`1gNz)_pcZTEG@;-xGIa_#O7s%<#Aac!nW5^ z@YqzUu>EXKc^dAvvE!DN$=(9I4H;7^PZowL*JmDThbm^bVE1)^Q4IG;%8|)5*rx1* zoL%DTnDt;AIlZ<_4HyHoPmpZ|5)OjTe^Gt^(2AY3;YRduAFah!DdScyM^u9Jq>F)U z1$&XJCSjt9Hy{a`nJqOlKNm^>=hXn_;B zN0#ndzy<(;vX+{jb@vWxdImK;gPNZ1nlh;A8PxRL)|#I05iNtVss<;wwjSK6HAXj%daYL- z-8kYqrVi=Gp|8Elr0bdUJG55m#zlJvRWEg(-ukAVsWbfktZ?e$>GWaEQwL5As-XI> z3aYaj4Cju?SH>H}YGk5!|zN(AU)>jo*oq5oI zHCSDAVNjLT2US^}W#6xLT3y2WJGyeKi?1=L<+^q)S7){js=L1N>aH$h^bW4~>Wp}8 zSAccK)E_lrU2^ArQYF?k^5~DcvCi=CbHtW8s)Hn5KUFM0t zsz~di2K`o})|m~1sBY>Uckm`aJCSQzuVRzU$W~AkM!%Wc zBEz|sYhm<+*TNy&LO8OOd8*0q^>X9paJLvU%qEAlg$=%LuXa&B$;w}#jggZhb1g(R z5O=(BWb01HVz$899HVpv)onL-Z>(r`=(xtKRgb!s(c$$s{YB7Q_!{qtvVMhVBd^nm zIh*A5PuCJct3*)D!8&M=C3d`xM4uId83_g#m?#)`kz)uHiGiH*NX%t9J~uNmoz;;! zI}$56IF}h&wPt+}M(P8ao=J^t6`Omu$Y^U1(KoX7>IbjXPwZPsR`Ak*7}Zr{t0PE5 zE=sC8v9lI(G_deNl?C-wy zzsFy@2%mGYXQpDAVC;;=AfAfY-LcrljT2)r@MhpYJ)R`k%_VJ1FQ+EPH?cxWN=ufb zjjdu$ZJWi{c^~zVJT>)@sXFE6(psZKGAzy0F_59zNwzP#oN*lw+EzA4GpQIZuT zXYw$kmQXp#HmH>)qUx5X38LMc#qS%QGO?}$I?8^#fV1grrU@i`(eR^BKfRMNh9m28 zYq&zuCZyWug#%DZ_XgrD393mYrj0_FhIvAUmk8iw348yYJIq)0M$Q}#4+_=Vg z85tbIN%$L%7}~MelmO4I%UCRGEm~S>z)Dl>3yuOoFf?Ng?$M3IEe2g381#Wb9~kt3 zK_3|OZ_c2%Slq|%8rX(gV;lNlW^2)`|GAqMkY<&rrOwh!E6&Xvrh(mprDd*%w!=KQ zIZBq=|F3M*jQsIW9BcVuqdi>WqayQ|Mmw!CzbF#02$?GN;92 b3EpgM8T+S)Znw_g;Mo6vjVJ>zwSoWuj<32+