claude-flow 3.59.17 → 3.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/proven-config.json +1 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/README.md +2 -1
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/mcp/dist/session-manager.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/session-manager.js +4 -1
- package/node_modules/@claude-flow/mcp/dist/session-manager.js.map +1 -1
- package/node_modules/@claude-flow/mcp/package.json +1 -1
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/package.json +3 -2
- package/v3/@claude-flow/cli/README.md +2 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +4 -4
- package/v3/@claude-flow/cli/dist/src/commands/agntcy/publish.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/agntcy/runtime.d.ts +4 -1
- package/v3/@claude-flow/cli/dist/src/commands/agntcy/runtime.js +15 -2
- package/v3/@claude-flow/cli/dist/src/commands/agntcy/swarm-join.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/agntcy/transport.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/update.js +5 -3
- package/v3/@claude-flow/cli/dist/src/mcp-client.js +4 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentbbs-tools.js +7 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/capability-brain.js +17 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/daa-tools.js +20 -12
- package/v3/@claude-flow/cli/dist/src/mcp-tools/github-tools.js +78 -75
- package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +11 -17
- package/v3/@claude-flow/cli/dist/src/mcp-tools/rure-tools.d.ts +11 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/rure-tools.js +359 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +17 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +18 -9
- package/v3/@claude-flow/cli/dist/src/mods/claude-installs.d.ts +12 -0
- package/v3/@claude-flow/cli/dist/src/mods/claude-installs.js +21 -1
- package/v3/@claude-flow/cli/dist/src/rure/aidefence-gate.d.ts +25 -0
- package/v3/@claude-flow/cli/dist/src/rure/aidefence-gate.js +63 -0
- package/v3/@claude-flow/cli/dist/src/rure/analyze-js-app.d.ts +17 -0
- package/v3/@claude-flow/cli/dist/src/rure/analyze-js-app.js +26 -0
- package/v3/@claude-flow/cli/dist/src/rure/binary-parse.d.ts +38 -0
- package/v3/@claude-flow/cli/dist/src/rure/binary-parse.js +347 -0
- package/v3/@claude-flow/cli/dist/src/rure/dotnet-parse.d.ts +30 -0
- package/v3/@claude-flow/cli/dist/src/rure/dotnet-parse.js +299 -0
- package/v3/@claude-flow/cli/dist/src/rure/evm-parse.d.ts +52 -0
- package/v3/@claude-flow/cli/dist/src/rure/evm-parse.js +233 -0
- package/v3/@claude-flow/cli/dist/src/rure/extract-asar.d.ts +33 -0
- package/v3/@claude-flow/cli/dist/src/rure/extract-asar.js +70 -0
- package/v3/@claude-flow/cli/dist/src/rure/find-feature.d.ts +26 -0
- package/v3/@claude-flow/cli/dist/src/rure/find-feature.js +69 -0
- package/v3/@claude-flow/cli/dist/src/rure/ts-walk.d.ts +47 -0
- package/v3/@claude-flow/cli/dist/src/rure/ts-walk.js +158 -0
- package/v3/@claude-flow/cli/dist/src/rure/walk-files.d.ts +21 -0
- package/v3/@claude-flow/cli/dist/src/rure/walk-files.js +71 -0
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +17 -3
- package/v3/@claude-flow/cli/dist/src/update/checker.d.ts +1 -0
- package/v3/@claude-flow/cli/dist/src/update/checker.js +4 -2
- package/v3/@claude-flow/cli/package.json +4 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 1 — whole-app analysis orchestration (ADR-0002).
|
|
3
|
+
*
|
|
4
|
+
* Walks a directory of JS/Electron source, parses every analyzable file,
|
|
5
|
+
* and aggregates the per-module findings. Parses only; never executes.
|
|
6
|
+
*/
|
|
7
|
+
import { type ModuleAnalysis } from './ts-walk.js';
|
|
8
|
+
export interface AppAnalysis {
|
|
9
|
+
modules: ModuleAnalysis[];
|
|
10
|
+
filesScanned: number;
|
|
11
|
+
truncated: boolean;
|
|
12
|
+
isElectron: boolean;
|
|
13
|
+
/** Files the parser could not handle (e.g. pathological nesting); their boundaries are NOT reported. */
|
|
14
|
+
skippedFiles: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function analyzeJsApp(root: string): Promise<AppAnalysis>;
|
|
17
|
+
//# sourceMappingURL=analyze-js-app.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 1 — whole-app analysis orchestration (ADR-0002).
|
|
3
|
+
*
|
|
4
|
+
* Walks a directory of JS/Electron source, parses every analyzable file,
|
|
5
|
+
* and aggregates the per-module findings. Parses only; never executes.
|
|
6
|
+
*/
|
|
7
|
+
import { walkAnalyzableFiles } from './walk-files.js';
|
|
8
|
+
import { analyzeModuleSource } from './ts-walk.js';
|
|
9
|
+
export async function analyzeJsApp(root) {
|
|
10
|
+
const { files, truncated } = walkAnalyzableFiles(root);
|
|
11
|
+
const modules = [];
|
|
12
|
+
let skippedFiles = 0;
|
|
13
|
+
for (const f of files) {
|
|
14
|
+
try {
|
|
15
|
+
modules.push(await analyzeModuleSource(f.path, f.text));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Unparseable file (corrupt source, parser stack overflow) — skip it rather
|
|
19
|
+
// than fail the whole analysis, but count it so the gap is visible.
|
|
20
|
+
skippedFiles += 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const isElectron = modules.some((m) => m.electronBoundaries.length > 0);
|
|
24
|
+
return { modules, filesScanned: files.length, truncated, isElectron, skippedFiles };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=analyze-js-app.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 2 — container-level static inspection of ELF / PE / Mach-O
|
|
3
|
+
* (ADR-0003). Reads integers and C-strings from a Buffer at bounds-checked
|
|
4
|
+
* offsets. Never loads, maps, or executes the target.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MAX_BINARY_BYTES: number;
|
|
7
|
+
export interface SectionInfo {
|
|
8
|
+
name: string;
|
|
9
|
+
offset: number;
|
|
10
|
+
size: number;
|
|
11
|
+
}
|
|
12
|
+
export interface BinaryInfo {
|
|
13
|
+
format: 'elf' | 'pe' | 'macho' | 'macho-fat' | 'unknown';
|
|
14
|
+
arch: string;
|
|
15
|
+
bits: 32 | 64 | null;
|
|
16
|
+
endianness: 'little' | 'big';
|
|
17
|
+
kind: string;
|
|
18
|
+
entryPoint: number | null;
|
|
19
|
+
sections: SectionInfo[];
|
|
20
|
+
libraries: string[];
|
|
21
|
+
imports: string[];
|
|
22
|
+
exports: string[];
|
|
23
|
+
archs?: string[];
|
|
24
|
+
warnings: string[];
|
|
25
|
+
truncated: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Detects the container format from magic bytes and parses it. Never throws on hostile input. */
|
|
28
|
+
export declare function parseBinary(b: Buffer): BinaryInfo;
|
|
29
|
+
export interface ExtractedString {
|
|
30
|
+
value: string;
|
|
31
|
+
offset: number;
|
|
32
|
+
}
|
|
33
|
+
/** Printable-ASCII runs, capped. Deterministic, bounded by file size cap. */
|
|
34
|
+
export declare function extractStrings(b: Buffer, minLen?: number, maxCount?: number): {
|
|
35
|
+
strings: ExtractedString[];
|
|
36
|
+
truncated: boolean;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=binary-parse.d.ts.map
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 2 — container-level static inspection of ELF / PE / Mach-O
|
|
3
|
+
* (ADR-0003). Reads integers and C-strings from a Buffer at bounds-checked
|
|
4
|
+
* offsets. Never loads, maps, or executes the target.
|
|
5
|
+
*/
|
|
6
|
+
export const MAX_BINARY_BYTES = 128 * 1024 * 1024;
|
|
7
|
+
const MAX_SECTIONS = 4096;
|
|
8
|
+
const MAX_SYMBOLS = 2000;
|
|
9
|
+
const MAX_PE_SECTIONS = 96; // PE format limit
|
|
10
|
+
const ITERATION_BUDGET = 200_000;
|
|
11
|
+
// Total table-walk steps allowed per parseBinary call. Header-declared counts and
|
|
12
|
+
// overlapping regions can multiply work far beyond file size; this bounds it.
|
|
13
|
+
let budget = ITERATION_BUDGET;
|
|
14
|
+
function tick() {
|
|
15
|
+
if (--budget < 0)
|
|
16
|
+
throw new RangeError('iteration budget exhausted');
|
|
17
|
+
}
|
|
18
|
+
function empty(format) {
|
|
19
|
+
return {
|
|
20
|
+
format, arch: 'unknown', bits: null, endianness: 'little', kind: 'unknown', entryPoint: null,
|
|
21
|
+
sections: [], libraries: [], imports: [], exports: [], warnings: [], truncated: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function describeError(err) {
|
|
25
|
+
if (err instanceof RangeError)
|
|
26
|
+
return err.message === 'iteration budget exhausted' ? err.message : 'out-of-bounds read';
|
|
27
|
+
return String(err);
|
|
28
|
+
}
|
|
29
|
+
/** Runs one structure parse; a failure becomes a warning, not an abort. */
|
|
30
|
+
function guarded(info, label, fn) {
|
|
31
|
+
try {
|
|
32
|
+
fn();
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
info.warnings.push(`${label}: ${describeError(err)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function cstr(b, off, max = 512) {
|
|
39
|
+
if (!Number.isInteger(off) || off < 0 || off >= b.length)
|
|
40
|
+
throw new RangeError('string offset');
|
|
41
|
+
const limit = Math.min(b.length, off + max);
|
|
42
|
+
let end = off;
|
|
43
|
+
while (end < limit && b[end] !== 0)
|
|
44
|
+
end++;
|
|
45
|
+
return b.toString('latin1', off, end);
|
|
46
|
+
}
|
|
47
|
+
function safeNum(v) {
|
|
48
|
+
if (v > BigInt(Number.MAX_SAFE_INTEGER))
|
|
49
|
+
throw new RangeError('value exceeds safe integer');
|
|
50
|
+
return Number(v);
|
|
51
|
+
}
|
|
52
|
+
function pushCapped(info, list, value) {
|
|
53
|
+
if (!value)
|
|
54
|
+
return;
|
|
55
|
+
if (list.length >= MAX_SYMBOLS) {
|
|
56
|
+
info.truncated = true;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
list.push(value);
|
|
60
|
+
}
|
|
61
|
+
const ELF_MACHINE = {
|
|
62
|
+
3: 'x86', 8: 'mips', 20: 'ppc', 21: 'ppc64', 40: 'arm', 62: 'x86-64', 183: 'aarch64', 243: 'riscv',
|
|
63
|
+
};
|
|
64
|
+
function parseElf(b) {
|
|
65
|
+
const info = empty('elf');
|
|
66
|
+
const is64 = b[4] === 2;
|
|
67
|
+
const le = b[5] !== 2;
|
|
68
|
+
info.bits = is64 ? 64 : 32;
|
|
69
|
+
info.endianness = le ? 'little' : 'big';
|
|
70
|
+
const u16 = (o) => (le ? b.readUInt16LE(o) : b.readUInt16BE(o));
|
|
71
|
+
const u32 = (o) => (le ? b.readUInt32LE(o) : b.readUInt32BE(o));
|
|
72
|
+
const u64 = (o) => safeNum(le ? b.readBigUInt64LE(o) : b.readBigUInt64BE(o));
|
|
73
|
+
const word = (o) => (is64 ? u64(o) : u32(o));
|
|
74
|
+
info.arch = ELF_MACHINE[u16(18)] ?? `machine-${u16(18)}`;
|
|
75
|
+
const type = u16(16);
|
|
76
|
+
info.kind = type === 2 ? 'executable' : type === 3 ? 'shared-or-pie' : type === 1 ? 'object' : type === 4 ? 'core' : 'unknown';
|
|
77
|
+
guarded(info, 'entry point', () => { info.entryPoint = word(24); });
|
|
78
|
+
const shdrs = [];
|
|
79
|
+
guarded(info, 'section headers', () => {
|
|
80
|
+
const shoff = word(is64 ? 40 : 32);
|
|
81
|
+
const shentsize = u16(is64 ? 58 : 46);
|
|
82
|
+
let shnum = u16(is64 ? 60 : 48);
|
|
83
|
+
if (shoff === 0 || shentsize === 0) {
|
|
84
|
+
info.warnings.push('no section headers: libraries and symbols are unavailable');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (shnum > MAX_SECTIONS) {
|
|
88
|
+
shnum = MAX_SECTIONS;
|
|
89
|
+
info.truncated = true;
|
|
90
|
+
}
|
|
91
|
+
for (let i = 0; i < shnum; i++) {
|
|
92
|
+
const o = shoff + i * shentsize;
|
|
93
|
+
shdrs.push(is64
|
|
94
|
+
? { name: u32(o), type: u32(o + 4), offset: u64(o + 24), size: u64(o + 32), link: u32(o + 40), entsize: u64(o + 56) }
|
|
95
|
+
: { name: u32(o), type: u32(o + 4), offset: u32(o + 16), size: u32(o + 20), link: u32(o + 24), entsize: u32(o + 36) });
|
|
96
|
+
}
|
|
97
|
+
const strIdx = u16(is64 ? 62 : 50);
|
|
98
|
+
const strtab = shdrs[strIdx];
|
|
99
|
+
for (const s of shdrs) {
|
|
100
|
+
let name = '';
|
|
101
|
+
if (strtab) {
|
|
102
|
+
try {
|
|
103
|
+
name = cstr(b, strtab.offset + s.name, 128);
|
|
104
|
+
}
|
|
105
|
+
catch { /* unnamed */ }
|
|
106
|
+
}
|
|
107
|
+
info.sections.push({ name, offset: s.offset, size: s.size });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
guarded(info, 'dynamic', () => {
|
|
111
|
+
for (const d of shdrs.filter((s) => s.type === 6)) {
|
|
112
|
+
const dynstr = shdrs[d.link];
|
|
113
|
+
if (!dynstr)
|
|
114
|
+
continue;
|
|
115
|
+
const step = is64 ? 16 : 8;
|
|
116
|
+
for (let o = d.offset; o + step <= d.offset + d.size && o + step <= b.length; o += step) {
|
|
117
|
+
tick();
|
|
118
|
+
const tag = word(o);
|
|
119
|
+
if (tag === 0)
|
|
120
|
+
break;
|
|
121
|
+
if (tag === 1)
|
|
122
|
+
pushCapped(info, info.libraries, cstr(b, dynstr.offset + word(o + (is64 ? 8 : 4))));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
guarded(info, 'dynamic symbols', () => {
|
|
127
|
+
for (const d of shdrs.filter((s) => s.type === 11)) {
|
|
128
|
+
const strs = shdrs[d.link];
|
|
129
|
+
const minSym = is64 ? 24 : 16;
|
|
130
|
+
const step = d.entsize >= minSym ? d.entsize : minSym;
|
|
131
|
+
if (!strs)
|
|
132
|
+
continue;
|
|
133
|
+
for (let o = d.offset; o + step <= d.offset + d.size && o + step <= b.length; o += step) {
|
|
134
|
+
tick();
|
|
135
|
+
const nameOff = u32(o);
|
|
136
|
+
const shndx = u16(o + (is64 ? 6 : 14));
|
|
137
|
+
const bind = b[o + (is64 ? 4 : 12)] >> 4;
|
|
138
|
+
if (nameOff === 0)
|
|
139
|
+
continue;
|
|
140
|
+
const name = cstr(b, strs.offset + nameOff);
|
|
141
|
+
if (shndx === 0)
|
|
142
|
+
pushCapped(info, info.imports, name);
|
|
143
|
+
else if (bind === 1 || bind === 2)
|
|
144
|
+
pushCapped(info, info.exports, name);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return info;
|
|
149
|
+
}
|
|
150
|
+
const PE_MACHINE = { 0x14c: 'x86', 0x8664: 'x86-64', 0x1c0: 'arm', 0x1c4: 'arm-thumb2', 0xaa64: 'aarch64' };
|
|
151
|
+
function parsePe(b) {
|
|
152
|
+
const info = empty('pe');
|
|
153
|
+
const pe = b.readUInt32LE(0x3c);
|
|
154
|
+
if (b.toString('latin1', pe, pe + 4) !== 'PE\0\0') {
|
|
155
|
+
info.warnings.push('missing PE signature');
|
|
156
|
+
return info;
|
|
157
|
+
}
|
|
158
|
+
const coff = pe + 4;
|
|
159
|
+
const machine = b.readUInt16LE(coff);
|
|
160
|
+
const nSections = b.readUInt16LE(coff + 2);
|
|
161
|
+
const optSize = b.readUInt16LE(coff + 16);
|
|
162
|
+
const chars = b.readUInt16LE(coff + 18);
|
|
163
|
+
const opt = coff + 20;
|
|
164
|
+
const plus = b.readUInt16LE(opt) === 0x20b;
|
|
165
|
+
info.arch = PE_MACHINE[machine] ?? `machine-0x${machine.toString(16)}`;
|
|
166
|
+
info.bits = plus ? 64 : 32;
|
|
167
|
+
info.kind = chars & 0x2000 ? 'dll' : 'executable';
|
|
168
|
+
info.entryPoint = b.readUInt32LE(opt + 16);
|
|
169
|
+
const secs = [];
|
|
170
|
+
guarded(info, 'sections', () => {
|
|
171
|
+
const n = Math.min(nSections, MAX_PE_SECTIONS);
|
|
172
|
+
if (n < nSections)
|
|
173
|
+
info.truncated = true;
|
|
174
|
+
for (let i = 0; i < n; i++) {
|
|
175
|
+
const o = opt + optSize + i * 40;
|
|
176
|
+
const vsize = b.readUInt32LE(o + 8);
|
|
177
|
+
const va = b.readUInt32LE(o + 12);
|
|
178
|
+
const rawSize = b.readUInt32LE(o + 16);
|
|
179
|
+
const raw = b.readUInt32LE(o + 20);
|
|
180
|
+
info.sections.push({ name: b.toString('latin1', o, o + 8).replace(/\0+$/, ''), offset: raw, size: rawSize });
|
|
181
|
+
secs.push({ va, span: Math.max(vsize, rawSize), raw });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
const toOff = (rva) => {
|
|
185
|
+
const s = secs.find((x) => rva >= x.va && rva < x.va + x.span);
|
|
186
|
+
if (!s)
|
|
187
|
+
throw new RangeError('unmapped RVA');
|
|
188
|
+
return s.raw + (rva - s.va);
|
|
189
|
+
};
|
|
190
|
+
const dirBase = opt + (plus ? 112 : 96);
|
|
191
|
+
const dir = (i) => ({ rva: b.readUInt32LE(dirBase + i * 8), size: b.readUInt32LE(dirBase + i * 8 + 4) });
|
|
192
|
+
guarded(info, 'imports', () => {
|
|
193
|
+
const imp = dir(1);
|
|
194
|
+
if (!imp.rva)
|
|
195
|
+
return;
|
|
196
|
+
const step = plus ? 8 : 4;
|
|
197
|
+
for (let o = toOff(imp.rva), n = 0; n < MAX_SYMBOLS; o += 20, n++) {
|
|
198
|
+
tick();
|
|
199
|
+
const origThunk = b.readUInt32LE(o);
|
|
200
|
+
const nameRva = b.readUInt32LE(o + 12);
|
|
201
|
+
const firstThunk = b.readUInt32LE(o + 16);
|
|
202
|
+
if (!origThunk && !nameRva && !firstThunk)
|
|
203
|
+
break;
|
|
204
|
+
const lib = cstr(b, toOff(nameRva), 256);
|
|
205
|
+
pushCapped(info, info.libraries, lib);
|
|
206
|
+
let t = toOff(origThunk || firstThunk);
|
|
207
|
+
for (let k = 0; k < MAX_SYMBOLS; k++, t += step) {
|
|
208
|
+
tick();
|
|
209
|
+
const entry = plus ? b.readBigUInt64LE(t) : BigInt(b.readUInt32LE(t));
|
|
210
|
+
if (entry === 0n)
|
|
211
|
+
break;
|
|
212
|
+
const byOrdinal = entry >> BigInt(plus ? 63 : 31);
|
|
213
|
+
if (byOrdinal)
|
|
214
|
+
pushCapped(info, info.imports, `${lib}!#${entry & 0xffffn}`);
|
|
215
|
+
else
|
|
216
|
+
pushCapped(info, info.imports, `${lib}!${cstr(b, toOff(Number(entry & 0x7fffffffn)) + 2, 256)}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
guarded(info, 'exports', () => {
|
|
221
|
+
const exp = dir(0);
|
|
222
|
+
if (!exp.rva)
|
|
223
|
+
return;
|
|
224
|
+
const base = toOff(exp.rva);
|
|
225
|
+
const count = Math.min(b.readUInt32LE(base + 24), MAX_SYMBOLS);
|
|
226
|
+
const names = toOff(b.readUInt32LE(base + 32));
|
|
227
|
+
for (let i = 0; i < count; i++) {
|
|
228
|
+
tick();
|
|
229
|
+
pushCapped(info, info.exports, cstr(b, toOff(b.readUInt32LE(names + i * 4)), 256));
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
return info;
|
|
233
|
+
}
|
|
234
|
+
const MACHO_CPU = { 7: 'x86', 0x01000007: 'x86-64', 12: 'arm', 0x0100000c: 'aarch64', 18: 'ppc', 0x01000012: 'ppc64' };
|
|
235
|
+
const MACHO_FILETYPE = { 1: 'object', 2: 'executable', 6: 'dylib', 8: 'bundle', 10: 'dsym' };
|
|
236
|
+
const LC_DYLIBS = new Set([0xc, 0x80000018, 0x8000001f, 0x80000023]);
|
|
237
|
+
function parseMachO(b, base = 0) {
|
|
238
|
+
const info = empty('macho');
|
|
239
|
+
const magic = b.readUInt32BE(base);
|
|
240
|
+
const le = magic === 0xcefaedfe || magic === 0xcffaedfe;
|
|
241
|
+
const is64 = magic === 0xfeedfacf || magic === 0xcffaedfe;
|
|
242
|
+
info.bits = is64 ? 64 : 32;
|
|
243
|
+
info.endianness = le ? 'little' : 'big';
|
|
244
|
+
const u32 = (o) => (le ? b.readUInt32LE(base + o) : b.readUInt32BE(base + o));
|
|
245
|
+
const u64 = (o) => safeNum(le ? b.readBigUInt64LE(base + o) : b.readBigUInt64BE(base + o));
|
|
246
|
+
const cpu = u32(4);
|
|
247
|
+
info.arch = MACHO_CPU[cpu] ?? `cpu-0x${cpu.toString(16)}`;
|
|
248
|
+
info.kind = MACHO_FILETYPE[u32(12)] ?? 'unknown';
|
|
249
|
+
const ncmds = Math.min(u32(16), MAX_SECTIONS);
|
|
250
|
+
guarded(info, 'load commands', () => {
|
|
251
|
+
let o = is64 ? 32 : 28;
|
|
252
|
+
for (let i = 0; i < ncmds; i++) {
|
|
253
|
+
const cmd = u32(o);
|
|
254
|
+
const size = u32(o + 4);
|
|
255
|
+
if (size < 8)
|
|
256
|
+
throw new RangeError('bad load command size');
|
|
257
|
+
if (LC_DYLIBS.has(cmd))
|
|
258
|
+
pushCapped(info, info.libraries, cstr(b, base + o + u32(o + 8), 512));
|
|
259
|
+
else if (cmd === 0x80000028)
|
|
260
|
+
info.entryPoint = u64(o + 8);
|
|
261
|
+
else if (cmd === 0x1 || cmd === 0x19) {
|
|
262
|
+
const seg64 = cmd === 0x19;
|
|
263
|
+
const nsects = u32(o + (seg64 ? 64 : 48));
|
|
264
|
+
let so = o + (seg64 ? 72 : 56);
|
|
265
|
+
for (let s = 0; s < nsects && info.sections.length < MAX_SECTIONS; s++, so += seg64 ? 80 : 68) {
|
|
266
|
+
info.sections.push({
|
|
267
|
+
name: `${b.toString('latin1', base + so + 16, base + so + 32).replace(/\0+$/, '')},${b.toString('latin1', base + so, base + so + 16).replace(/\0+$/, '')}`,
|
|
268
|
+
offset: u32(so + (seg64 ? 48 : 40)),
|
|
269
|
+
size: seg64 ? u64(so + 40) : u32(so + 36),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
o += size;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
return info;
|
|
277
|
+
}
|
|
278
|
+
function parseFat(b) {
|
|
279
|
+
const info = empty('macho-fat');
|
|
280
|
+
info.endianness = 'big';
|
|
281
|
+
const n = Math.min(b.readUInt32BE(4), 16);
|
|
282
|
+
let first = null;
|
|
283
|
+
for (let i = 0; i < n; i++) {
|
|
284
|
+
const o = 8 + i * 20;
|
|
285
|
+
const cpu = b.readUInt32BE(o);
|
|
286
|
+
(info.archs ??= []).push(MACHO_CPU[cpu] ?? `cpu-0x${cpu.toString(16)}`);
|
|
287
|
+
if (first === null)
|
|
288
|
+
first = b.readUInt32BE(o + 8);
|
|
289
|
+
}
|
|
290
|
+
info.arch = (info.archs ?? []).join('+') || 'unknown';
|
|
291
|
+
if (first !== null) {
|
|
292
|
+
guarded(info, 'first slice', () => {
|
|
293
|
+
const slice = parseMachO(b, first);
|
|
294
|
+
Object.assign(info, { bits: slice.bits, kind: slice.kind, entryPoint: slice.entryPoint, sections: slice.sections, libraries: slice.libraries });
|
|
295
|
+
info.warnings.push(...slice.warnings);
|
|
296
|
+
info.warnings.push('fat binary: sections and libraries are from the first slice only');
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
return info;
|
|
300
|
+
}
|
|
301
|
+
/** Detects the container format from magic bytes and parses it. Never throws on hostile input. */
|
|
302
|
+
export function parseBinary(b) {
|
|
303
|
+
budget = ITERATION_BUDGET;
|
|
304
|
+
try {
|
|
305
|
+
if (b.length >= 20 && b[0] === 0x7f && b.toString('latin1', 1, 4) === 'ELF')
|
|
306
|
+
return parseElf(b);
|
|
307
|
+
if (b.length >= 0x40 && b[0] === 0x4d && b[1] === 0x5a)
|
|
308
|
+
return parsePe(b);
|
|
309
|
+
if (b.length >= 32) {
|
|
310
|
+
const m = b.readUInt32BE(0);
|
|
311
|
+
if (m === 0xfeedface || m === 0xfeedfacf || m === 0xcefaedfe || m === 0xcffaedfe)
|
|
312
|
+
return parseMachO(b);
|
|
313
|
+
if (m === 0xcafebabe && b.readUInt32BE(4) >= 1 && b.readUInt32BE(4) <= 16)
|
|
314
|
+
return parseFat(b);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
const info = empty('unknown');
|
|
319
|
+
info.warnings.push(`header parse failed: ${describeError(err)}`);
|
|
320
|
+
return info;
|
|
321
|
+
}
|
|
322
|
+
const info = empty('unknown');
|
|
323
|
+
info.warnings.push('unrecognized container format (expected ELF, PE, or Mach-O)');
|
|
324
|
+
return info;
|
|
325
|
+
}
|
|
326
|
+
/** Printable-ASCII runs, capped. Deterministic, bounded by file size cap. */
|
|
327
|
+
export function extractStrings(b, minLen = 6, maxCount = 200) {
|
|
328
|
+
const strings = [];
|
|
329
|
+
let start = -1;
|
|
330
|
+
for (let i = 0; i <= b.length; i++) {
|
|
331
|
+
const printable = i < b.length && b[i] >= 0x20 && b[i] < 0x7f;
|
|
332
|
+
if (printable) {
|
|
333
|
+
if (start < 0)
|
|
334
|
+
start = i;
|
|
335
|
+
}
|
|
336
|
+
else if (start >= 0) {
|
|
337
|
+
if (i - start >= minLen) {
|
|
338
|
+
if (strings.length >= maxCount)
|
|
339
|
+
return { strings, truncated: true };
|
|
340
|
+
strings.push({ value: b.toString('latin1', start, Math.min(i, start + 256)), offset: start });
|
|
341
|
+
}
|
|
342
|
+
start = -1;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return { strings, truncated: false };
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=binary-parse.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 3 — .NET assembly metadata inspection (ADR-0004).
|
|
3
|
+
* Reads ECMA-335 metadata from a Buffer. Never loads or executes the assembly.
|
|
4
|
+
*/
|
|
5
|
+
export interface DotNetInfo {
|
|
6
|
+
isManaged: boolean;
|
|
7
|
+
runtimeVersion: string | null;
|
|
8
|
+
assembly: {
|
|
9
|
+
name: string;
|
|
10
|
+
version: string;
|
|
11
|
+
} | null;
|
|
12
|
+
assemblyRefs: {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
}[];
|
|
16
|
+
typeCount: number;
|
|
17
|
+
types: string[];
|
|
18
|
+
methodCount: number;
|
|
19
|
+
pinvokes: {
|
|
20
|
+
module: string;
|
|
21
|
+
function: string;
|
|
22
|
+
}[];
|
|
23
|
+
userStrings: string[];
|
|
24
|
+
entryPointToken: number | null;
|
|
25
|
+
warnings: string[];
|
|
26
|
+
truncated: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Inspects a managed assembly's metadata. Never throws on hostile input. */
|
|
29
|
+
export declare function parseDotNet(b: Buffer): DotNetInfo;
|
|
30
|
+
//# sourceMappingURL=dotnet-parse.d.ts.map
|