90 lines
8.8 KiB
JavaScript
90 lines
8.8 KiB
JavaScript
// Read only saved, digest-verified REA exports; never run original code or touch its runtime.
|
|
import fs from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { createHash } from 'node:crypto'
|
|
import { parse } from '@babel/parser'
|
|
import generator from '@babel/generator'
|
|
const generate = generator.default ?? generator
|
|
const runtime = JSON.parse(await fs.readFile('.tmp/rea-runtime-scripts.json', 'utf8'))
|
|
const output = '.tmp/skills-audit-20261016'
|
|
await fs.mkdir(output, { recursive: true })
|
|
const digest = text => createHash('sha256').update(text).digest('hex')
|
|
const sources = runtime.items.filter(item => item.source?.artifact?.text).map(item => {
|
|
const artifact = item.source.artifact
|
|
if (digest(artifact.text) !== artifact.sha256) throw Error('REA export changed: ' + item.file_path)
|
|
return { file: path.basename(item.file_path.replaceAll('\\', '/')), ...artifact }
|
|
})
|
|
const verifiedRuntimeScripts=sources.length
|
|
const selected = JSON.parse(await fs.readFile('.tmp/rea-skills-market-selected-20261016.json','utf8'))
|
|
if (selected.normalized_result.root_sha256 !== '035254bc76310ea46317433e4840602094543ef323d2d5c5aff7c131729de918') throw Error('Selected source root changed')
|
|
for (const file of selected.normalized_result.files) {
|
|
if (digest(file.text) !== file.sha256) throw Error('Selected source digest changed')
|
|
if (file.path.endsWith('.js')) sources.push({file:path.basename(file.path),text:file.text,sha256:file.sha256,selectedEvidenceId:selected.evidence_id})
|
|
}
|
|
const pinned = { 'skills-panel-CvMlV3m8.js': 'b30c43c11ec476db639d6be58065d927411845487d34b0ea8e839d1c6eb7a238',
|
|
'skills-market-Cwf-NvXV.js':'f1434cadabf5ed3762f00fc2a356bc7418b0b4aab27db62dc9fbbe5add6f6dfb',
|
|
'standalone-skill-detail-modal-Bi5X8CZ4.js': 'ee7de2a7123f94110aa01258d3d6b3fcc3ce48f2ec5fd8f91cb3367c817a0f01' }
|
|
for (const [file, sha] of Object.entries(pinned)) if (sources.find(source => source.file === file)?.sha256 !== sha) throw Error('Pinned source changed: ' + file)
|
|
const report = { version: 'WorkBuddy 5.8.2', evidenceId: runtime.evidence_id, selectedEvidenceId:selected.evidence_id, verifiedRuntimeScripts,
|
|
rootSha256: '035254bc76310ea46317433e4840602094543ef323d2d5c5aff7c131729de918', modules: [], references: [], css: [] }
|
|
for (const source of sources) {
|
|
if (/^(?:editor\.|clipboard-|remark-|file-path-|local-file-path-|branch-switch-|load-i18n-|space-library-)/.test(source.file)) continue
|
|
const skillModule = /skill|entitlement/i.test(source.file), helperModule = !skillModule && /function\s+\w*(?:Skill|Slash|Command|Entitle|PaidSkill)/.test(source.text)
|
|
if (!skillModule && !helperModule) continue
|
|
const ast = parse(source.text, { sourceType: 'module' }), imports = [], exports = [], definitions = [], classes = [], hostMethods = [], calls = []
|
|
function visit(node, parent) {
|
|
if (!node || typeof node !== 'object') return
|
|
if (node.type === 'ImportDeclaration') imports.push({ file: node.source.value, bindings: node.specifiers.map(item => ({ local: item.local.name, imported: item.imported?.name })) })
|
|
if (node.type === 'ExportNamedDeclaration') exports.push(...node.specifiers.map(item => ({ local: item.local.name, exported: item.exported.name })))
|
|
const name = node.type === 'FunctionDeclaration' ? node.id?.name : node.type === 'AssignmentExpression' && ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.right.type) ? node.left.name : node.type === 'VariableDeclarator' && ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.init?.type) ? node.id.name : undefined
|
|
if (name && (skillModule || /skill|slash|command|entitle|scan/i.test(name))) definitions.push({ name, offset: node.start, length: node.end-node.start, text: source.text.slice(node.start, node.end), formatted: generate(node).code })
|
|
if (['ClassDeclaration','ClassExpression'].includes(node.type)) {
|
|
const name=node.id?.name??(parent?.type==='AssignmentExpression'?parent.left.name:parent?.type==='VariableDeclarator'?parent.id.name:undefined)
|
|
if(name&&/Skill|Market|Enterprise|Plugin|SearchService|UIService|AsyncSecurityScan|DetailService|KnotService/.test(name)) classes.push({name,offset:node.start,length:node.end-node.start,methods:node.body.body.filter(item=>item.key?.name).map(item=>({name:item.key.name,offset:item.start,length:item.end-item.start})),formatted:generate(node).code})
|
|
}
|
|
if(source.file.startsWith('workbuddy-provider-')&&node.type==='ClassMethod'&&/Skill|Command/.test(node.key?.name??''))hostMethods.push({name:node.key.name,offset:node.start,length:node.end-node.start,formatted:generate(node).code})
|
|
if (['CallExpression', 'OptionalCallExpression'].includes(node.type) && ['MemberExpression', 'OptionalMemberExpression'].includes(node.callee.type)) {
|
|
const method = node.callee.property.name ?? node.callee.property.value
|
|
if (typeof method === 'string' && /skill|scan|purchase|entitle|install|market|payment|subscription|import|upload|update|openUrl|openExternal|reveal|redeem|checkout|select.*file|folder|directory/i.test(method)) calls.push({ method, offset: node.start, expression: source.text.slice(node.start, node.end).slice(0,550) })
|
|
}
|
|
for (const [key, value] of Object.entries(node)) if (key !== 'loc') {
|
|
if (Array.isArray(value)) value.forEach(child => visit(child,node)); else if (value && typeof value === 'object') visit(value,node)
|
|
}
|
|
}
|
|
visit(ast)
|
|
if (skillModule) await fs.writeFile(`${output}/${source.file}`, generate(ast).code)
|
|
else await fs.writeFile(`${output}/${source.file}-skill-helpers.js`, definitions.map(item => `// ${item.name} offset=${item.offset} length=${item.length}\n${item.formatted}`).join('\n\n'))
|
|
if(classes.length) await fs.writeFile(`${output}/${source.file}-skill-classes.js`,classes.map(item=>`// ${item.name} offset=${item.offset} length=${item.length}\n${item.formatted}`).join('\n\n'))
|
|
if(hostMethods.length)await fs.writeFile(`${output}/${source.file}-skill-host-methods.js`,hostMethods.map(item=>`// ${item.name} offset=${item.offset} length=${item.length}\n${item.formatted}`).join('\n\n'))
|
|
report.modules.push({ file: source.file, evidenceId:source.selectedEvidenceId??runtime.evidence_id, sha256: source.sha256, bytes: Buffer.byteLength(source.text), skillModule, imports, exports,
|
|
definitions: definitions.map(({ text, formatted, ...item }) => item),classes:classes.map(({formatted,...item})=>item),hostMethods:hostMethods.map(({formatted,...item})=>item),calls })
|
|
for (const item of definitions) if (!skillModule) report.references.push({file:source.file,sha256:source.sha256,name:item.name,offset:item.offset,length:item.length})
|
|
}
|
|
for(const file of selected.normalized_result.files.filter(file=>file.path.endsWith('.css'))) {
|
|
report.css.push({file:path.basename(file.path),sha256:file.sha256,evidenceId:selected.evidence_id,sizeBytes:file.size_bytes})
|
|
await fs.writeFile(`${output}/${path.basename(file.path)}`,file.text)
|
|
}
|
|
const cssManifest = JSON.parse(await fs.readFile('.tmp/rea-css-recovered/manifest.json','utf8'))
|
|
for (const file of cssManifest.files.filter(file => /skill/.test(file.file))) {
|
|
const text = await fs.readFile('.tmp/rea-css-recovered/' + file.file, 'utf8')
|
|
if (digest(text) !== file.sha256) throw Error('REA CSS changed: ' + file.file)
|
|
report.css.push(file)
|
|
}
|
|
try {
|
|
const fresh=JSON.parse(await fs.readFile('.tmp/rea-expert-runtime-scripts-20261016.json','utf8'))
|
|
const items=fresh.items??fresh.normalized_result?.items??[]
|
|
for(const item of items){const a=item.source?.artifact;if(a?.text&&digest(a.text)!==a.sha256)throw Error('Fresh runtime digest changed')}
|
|
const market=items.find(item=>item.file_path?.endsWith('skills-market-Cwf-NvXV.js'))
|
|
report.liveLoadedMarket={evidenceId:fresh.evidence_id,verifiedScriptCount:items.length,sha256:market?.source?.artifact?.sha256}
|
|
}catch(error){if(error.code!=='ENOENT')throw error}
|
|
report.captures=[]
|
|
for(const [directory,imageFile,step]of [['rea-market-skills-20261016','frame-000.png','skills-ready'],['rea-installed-skills-static-20261016','workbuddy.png',null],['rea-add-skill-options-20261016','frame-000.png','add-skill-options-ready']]){
|
|
const capture=JSON.parse(await fs.readFile(`.tmp/${directory}/layout.json`,'utf8'))
|
|
const record=step?capture.probe.frames.find(frame=>frame.step_id===step):capture
|
|
const bytes=await fs.readFile(`.tmp/${directory}/${imageFile}`)
|
|
if(digest(bytes)!==record.artifact.sha256)throw Error('REA screenshot SHA changed: '+directory)
|
|
report.captures.push({directory,imageFile,evidenceId:capture.evidenceId,sha256:record.artifact.sha256,step,probeStatus:capture.probe?.status??'not-requested',steps:capture.probe?.steps??[],restoration:capture.probe?.restoration??[]})
|
|
}
|
|
await fs.writeFile(`${output}/report.json`,JSON.stringify(report,null,2))
|
|
console.log(JSON.stringify({verified:report.verifiedRuntimeScripts,modules:report.modules.map(module=>({file:module.file,definitions:module.definitions.length,calls:module.calls.length})),css:report.css.map(file=>file.file)}))
|