Prepare documented WhatsApp injector with portable Python bundle

This commit is contained in:
Ops Agent committed 2026-10-11 07:57:48 +02:00
commit d2b621bce7
49 files changed
+5737

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
// Fixture tests for pagination, decryption errors, and plaintext redaction.
const assert = require('node:assert/strict');
const vm = require('node:vm');
const fs = require('node:fs');
const probe = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
const rows = ['a','b','c','d','e'].map(id => ({id, body:'text-'+id, type:'chat', t:123, invisible:id==='b'}));
const events = [];
function req(result) {
const r = {};
queueMicrotask(() => {r.result=result; r.onsuccess();});
return r;
}
const store = {
count:()=>req(rows.length),
openKeyCursor(range,direction) {
const selected=rows.filter(r=>!range || ((range.lower===undefined || r.id>range.lower) && r.id<=range.upper));
if(direction==='prev')selected.reverse();
const request={};let index=0;
function next(){queueMicrotask(()=>{
request.result=index<selected.length?{primaryKey:selected[index++].id,continue:next}:null;
request.onsuccess();
});}
next();return request;
}
};
const db={objectStoreNames:{contains:n=>n==='message'},transaction:(name,mode)=>{
assert.equal(name,'message');assert.equal(mode,'readonly');return {objectStore:()=>store};
},close:()=>events.push('close')};
const modules={
WAWebSchemaMessage:{getMessageTable:()=>({get:async key=>{
events.push('decrypt:'+key);
if(key==='c')throw Error('simulated unavailable decryption key');
return {...rows.find(r=>r.id===key)};
}})},
WAWebDBMessageSerialization:{messageFromDbRow:row=>{
events.push('decode:'+row.id);
return {...row,id:{id:row.id,remote:'test-chat',fromMe:false}};
}}
};
async function run(options){return vm.runInNewContext(probe.replace('__OPTIONS__',JSON.stringify(options)),{
location:{origin:'https://web.whatsapp.com'}, require:n=>modules[n],
indexedDB:{databases:async()=>[{name:'model-storage'}],open:()=>req(db)},
IDBKeyRange:{upperBound:upper=>({upper}),bound:(lower,upper,openLower,openUpper)=>{
assert.equal(openLower,true);assert.equal(openUpper,false);return {lower,upper};
}}
});}
(async()=>{
const bounds=await run({mode:'bounds'});assert.equal(bounds.count,5);assert.equal(bounds.upper,'e');
let after=null,all=[];
while(after!=='e'){
const page=await run({mode:'batch',upper:'e',after,batch:2,redact:false});
assert.ok(page.keys.length<=2);all.push(...page.messages);after=page.last;
}
assert.deepEqual(all.map(m=>m.id),['a','b','c','d','e']); // UI-invisible b remains included.
assert.equal(all[2].error,'row_decryption_or_decoding_failed');
assert.equal(all[1].body,'text-b');
const redacted=await run({mode:'batch',upper:'b',after:null,batch:2,redact:true});
assert.ok(redacted.messages.every(m=>!('body' in m)&&!('caption' in m)));
assert.equal(redacted.messages[0].body_length,6);
assert.ok(events.indexOf('decrypt:a')<events.indexOf('decode:a'));
console.log('PASS: bounded pagination, upper bound, invisible rows, row errors, decryption order, redaction');
})().catch(error=>{console.error(error);process.exitCode=1;});