mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-04 10:26:52 +02:00
style(cso): format lib/cso TypeScript with pinned Prettier
Mechanical reformat only. Minified transpile output is byte-identical for 21 of 22 files; witness.ts differs only in three regex flag orders (/mi -> /im), which JavaScript canonicalizes. Source-text assertions over lib/cso now compare whitespace-insensitively with the same tokens.
This commit is contained in:
1 parent
dcaea52800
commit
d93d61f7ba
27 files changed
+17942
-4164
No files matched your search
+153
-24
@@ -3,30 +3,159 @@ import * as fs from 'node:fs';
|
||||
import { connect } from 'node:net';
|
||||
import { HttpAssertion, VerificationObservation, object } from './contracts';
|
||||
|
||||
interface Config { phase:'before'|'after'; port:number; legitimate:HttpAssertion[]; security:HttpAssertion }
|
||||
function matches(status:number,body:string,oracle:HttpAssertion['expected']):boolean{return status===oracle.status&&(oracle.includes===undefined||body.includes(oracle.includes))&&(oracle.excludes===undefined||!body.includes(oracle.excludes));}
|
||||
export async function boundedResponseBody(response:Response,limit=65536):Promise<string>{
|
||||
if(!Number.isSafeInteger(limit)||limit<1)throw new Error('invalid response limit');
|
||||
const declared=response.headers.get('content-length');
|
||||
if(declared!==null&&(/^\d+$/.test(declared)?Number(declared)>limit:true)){await response.body?.cancel();throw new Error('response too large');}
|
||||
if(!response.body)return'';
|
||||
const reader=response.body.getReader(),chunks:Uint8Array[]=[];let total=0;
|
||||
try{
|
||||
for(;;){const next=await reader.read();if(next.done)break;if(!next.value)continue;total+=next.value.byteLength;if(total>limit){await reader.cancel();throw new Error('response too large');}chunks.push(next.value);}
|
||||
}finally{reader.releaseLock();}
|
||||
const bytes=new Uint8Array(total);let offset=0;for(const chunk of chunks){bytes.set(chunk,offset);offset+=chunk.byteLength;}return new TextDecoder().decode(bytes);
|
||||
interface Config {
|
||||
phase: 'before' | 'after';
|
||||
port: number;
|
||||
legitimate: HttpAssertion[];
|
||||
security: HttpAssertion;
|
||||
}
|
||||
async function request(a:HttpAssertion,port:number):Promise<{status:number;body:string}>{
|
||||
const controller=new AbortController(),timer=setTimeout(()=>controller.abort(),5000);
|
||||
try{const response=await fetch(`http://127.0.0.1:${port}${a.path}`,{method:a.method,headers:a.headers,body:['GET'].includes(a.method)?undefined:a.body,redirect:'manual',signal:controller.signal});return{status:response.status,body:await boundedResponseBody(response)};}finally{clearTimeout(timer);}
|
||||
function matches(status: number, body: string, oracle: HttpAssertion['expected']): boolean {
|
||||
return (
|
||||
status === oracle.status &&
|
||||
(oracle.includes === undefined || body.includes(oracle.includes)) &&
|
||||
(oracle.excludes === undefined || !body.includes(oracle.excludes))
|
||||
);
|
||||
}
|
||||
async function ready(port:number):Promise<boolean>{return await new Promise(resolve=>{const socket=connect({host:'127.0.0.1',port}),done=(value:boolean)=>{socket.removeAllListeners();socket.destroy();resolve(value);},timer=setTimeout(()=>done(false),500);socket.once('connect',()=>{clearTimeout(timer);done(true);});socket.once('error',()=>{clearTimeout(timer);done(false);});});}
|
||||
async function main(){
|
||||
const file=process.argv[2];if(!file||!file.startsWith('/policy/'))throw new Error('trusted policy path required');const raw=fs.readFileSync(file,'utf8');if(Buffer.byteLength(raw)>1024*1024)throw new Error('policy too large');const v=object(JSON.parse(raw),'verifier policy') as any;
|
||||
if(!['before','after'].includes(v.phase)||!Number.isInteger(v.port)||v.port<1024||v.port>65535||!Array.isArray(v.legitimate)||!v.security)throw new Error('invalid verifier policy');const config=v as Config;
|
||||
let booted=false;for(let attempt=0;attempt<60;attempt++){if(await ready(config.port)){booted=true;break;}await Bun.sleep(250);}
|
||||
let legitimate=false,security:VerificationObservation['security']='inconclusive',summary='application did not answer a legitimate control';
|
||||
if(booted){try{legitimate=(await Promise.all(config.legitimate.map(async a=>{const r=await request(a,config.port);return matches(r.status,r.body,a.expected);}))).every(Boolean);const r=await request(config.security,config.port),fixed=matches(r.status,r.body,config.security.expected),vulnerable=matches(r.status,r.body,config.security.vulnerable!);security=config.phase==='before'?(vulnerable&&!fixed?'intended_failure':fixed&&!vulnerable?'pass':'inconclusive'):(fixed&&!vulnerable?'pass':'inconclusive');summary=`boot=true legitimate=${legitimate} security=${security}`;}catch{summary='bounded verifier request failed';}}
|
||||
process.stdout.write(JSON.stringify({booted,legitimate,security,existingTests:false,output:summary,inputHash:''})+'\n');
|
||||
export async function boundedResponseBody(response: Response, limit = 65536): Promise<string> {
|
||||
if (!Number.isSafeInteger(limit) || limit < 1) throw new Error('invalid response limit');
|
||||
const declared = response.headers.get('content-length');
|
||||
if (declared !== null && (/^\d+$/.test(declared) ? Number(declared) > limit : true)) {
|
||||
await response.body?.cancel();
|
||||
throw new Error('response too large');
|
||||
}
|
||||
if (!response.body) return '';
|
||||
const reader = response.body.getReader(),
|
||||
chunks: Uint8Array[] = [];
|
||||
let total = 0;
|
||||
try {
|
||||
for (;;) {
|
||||
const next = await reader.read();
|
||||
if (next.done) break;
|
||||
if (!next.value) continue;
|
||||
total += next.value.byteLength;
|
||||
if (total > limit) {
|
||||
await reader.cancel();
|
||||
throw new Error('response too large');
|
||||
}
|
||||
chunks.push(next.value);
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
const bytes = new Uint8Array(total);
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
bytes.set(chunk, offset);
|
||||
offset += chunk.byteLength;
|
||||
}
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
if(import.meta.main)main().catch(()=>{process.stdout.write(JSON.stringify({booted:false,legitimate:false,security:'inconclusive',existingTests:false,output:'verifier setup failed',inputHash:''})+'\n');process.exitCode=1;});
|
||||
async function request(a: HttpAssertion, port: number): Promise<{ status: number; body: string }> {
|
||||
const controller = new AbortController(),
|
||||
timer = setTimeout(() => controller.abort(), 5000);
|
||||
try {
|
||||
const response = await fetch(`http://127.0.0.1:${port}${a.path}`, {
|
||||
method: a.method,
|
||||
headers: a.headers,
|
||||
body: ['GET'].includes(a.method) ? undefined : a.body,
|
||||
redirect: 'manual',
|
||||
signal: controller.signal,
|
||||
});
|
||||
return { status: response.status, body: await boundedResponseBody(response) };
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
async function ready(port: number): Promise<boolean> {
|
||||
return await new Promise((resolve) => {
|
||||
const socket = connect({ host: '127.0.0.1', port }),
|
||||
done = (value: boolean) => {
|
||||
socket.removeAllListeners();
|
||||
socket.destroy();
|
||||
resolve(value);
|
||||
},
|
||||
timer = setTimeout(() => done(false), 500);
|
||||
socket.once('connect', () => {
|
||||
clearTimeout(timer);
|
||||
done(true);
|
||||
});
|
||||
socket.once('error', () => {
|
||||
clearTimeout(timer);
|
||||
done(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
async function main() {
|
||||
const file = process.argv[2];
|
||||
if (!file || !file.startsWith('/policy/')) throw new Error('trusted policy path required');
|
||||
const raw = fs.readFileSync(file, 'utf8');
|
||||
if (Buffer.byteLength(raw) > 1024 * 1024) throw new Error('policy too large');
|
||||
const v = object(JSON.parse(raw), 'verifier policy') as any;
|
||||
if (
|
||||
!['before', 'after'].includes(v.phase) ||
|
||||
!Number.isInteger(v.port) ||
|
||||
v.port < 1024 ||
|
||||
v.port > 65535 ||
|
||||
!Array.isArray(v.legitimate) ||
|
||||
!v.security
|
||||
)
|
||||
throw new Error('invalid verifier policy');
|
||||
const config = v as Config;
|
||||
let booted = false;
|
||||
for (let attempt = 0; attempt < 60; attempt++) {
|
||||
if (await ready(config.port)) {
|
||||
booted = true;
|
||||
break;
|
||||
}
|
||||
await Bun.sleep(250);
|
||||
}
|
||||
let legitimate = false,
|
||||
security: VerificationObservation['security'] = 'inconclusive',
|
||||
summary = 'application did not answer a legitimate control';
|
||||
if (booted) {
|
||||
try {
|
||||
legitimate = (
|
||||
await Promise.all(
|
||||
config.legitimate.map(async (a) => {
|
||||
const r = await request(a, config.port);
|
||||
return matches(r.status, r.body, a.expected);
|
||||
}),
|
||||
)
|
||||
).every(Boolean);
|
||||
const r = await request(config.security, config.port),
|
||||
fixed = matches(r.status, r.body, config.security.expected),
|
||||
vulnerable = matches(r.status, r.body, config.security.vulnerable!);
|
||||
security =
|
||||
config.phase === 'before'
|
||||
? vulnerable && !fixed
|
||||
? 'intended_failure'
|
||||
: fixed && !vulnerable
|
||||
? 'pass'
|
||||
: 'inconclusive'
|
||||
: fixed && !vulnerable
|
||||
? 'pass'
|
||||
: 'inconclusive';
|
||||
summary = `boot=true legitimate=${legitimate} security=${security}`;
|
||||
} catch {
|
||||
summary = 'bounded verifier request failed';
|
||||
}
|
||||
}
|
||||
process.stdout.write(
|
||||
JSON.stringify({ booted, legitimate, security, existingTests: false, output: summary, inputHash: '' }) +
|
||||
'\n',
|
||||
);
|
||||
}
|
||||
if (import.meta.main)
|
||||
main().catch(() => {
|
||||
process.stdout.write(
|
||||
JSON.stringify({
|
||||
booted: false,
|
||||
legitimate: false,
|
||||
security: 'inconclusive',
|
||||
existingTests: false,
|
||||
output: 'verifier setup failed',
|
||||
inputHash: '',
|
||||
}) + '\n',
|
||||
);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in new issue
Block a user