claude-flow 3.59.16 → 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/init/executor.js +2 -1
- 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,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
|
|
@@ -0,0 +1,299 @@
|
|
|
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
|
+
const MAX_LIST = 500;
|
|
6
|
+
const MAX_USER_STRINGS = 200;
|
|
7
|
+
const MAX_STR = 256;
|
|
8
|
+
const CODED = {
|
|
9
|
+
TypeDefOrRef: { bits: 2, tables: [2, 1, 27] },
|
|
10
|
+
HasConstant: { bits: 2, tables: [4, 8, 23] },
|
|
11
|
+
HasCustomAttribute: { bits: 5, tables: [6, 4, 1, 2, 8, 9, 10, 0, 14, 23, 20, 17, 26, 27, 32, 35, 38, 39, 40, 42, 44, 43] },
|
|
12
|
+
HasFieldMarshal: { bits: 1, tables: [4, 8] },
|
|
13
|
+
HasDeclSecurity: { bits: 2, tables: [2, 6, 32] },
|
|
14
|
+
MemberRefParent: { bits: 3, tables: [2, 1, 26, 6, 27] },
|
|
15
|
+
HasSemantics: { bits: 1, tables: [20, 23] },
|
|
16
|
+
MethodDefOrRef: { bits: 1, tables: [6, 10] },
|
|
17
|
+
MemberForwarded: { bits: 1, tables: [4, 6] },
|
|
18
|
+
Implementation: { bits: 2, tables: [38, 35, 39] },
|
|
19
|
+
CustomAttributeType: { bits: 3, tables: [6, 10] },
|
|
20
|
+
ResolutionScope: { bits: 2, tables: [0, 26, 35, 1] },
|
|
21
|
+
TypeOrMethodDef: { bits: 1, tables: [2, 6] },
|
|
22
|
+
};
|
|
23
|
+
// ECMA-335 II.22 row layouts, indexed by table id.
|
|
24
|
+
const SCHEMA = {
|
|
25
|
+
0: [2, 'S', 'G', 'G', 'G'],
|
|
26
|
+
1: ['C:ResolutionScope', 'S', 'S'],
|
|
27
|
+
2: [4, 'S', 'S', 'C:TypeDefOrRef', 'T:4', 'T:6'],
|
|
28
|
+
3: ['T:4'],
|
|
29
|
+
4: [2, 'S', 'B'],
|
|
30
|
+
5: ['T:6'],
|
|
31
|
+
6: [4, 2, 2, 'S', 'B', 'T:8'],
|
|
32
|
+
7: ['T:8'],
|
|
33
|
+
8: [2, 2, 'S'],
|
|
34
|
+
9: ['T:2', 'C:TypeDefOrRef'],
|
|
35
|
+
10: ['C:MemberRefParent', 'S', 'B'],
|
|
36
|
+
11: [1, 1, 'C:HasConstant', 'B'],
|
|
37
|
+
12: ['C:HasCustomAttribute', 'C:CustomAttributeType', 'B'],
|
|
38
|
+
13: ['C:HasFieldMarshal', 'B'],
|
|
39
|
+
14: [2, 'C:HasDeclSecurity', 'B'],
|
|
40
|
+
15: [2, 4, 'T:2'],
|
|
41
|
+
16: [4, 'T:4'],
|
|
42
|
+
17: ['B'],
|
|
43
|
+
18: ['T:2', 'T:20'],
|
|
44
|
+
19: ['T:20'],
|
|
45
|
+
20: [2, 'S', 'C:TypeDefOrRef'],
|
|
46
|
+
21: ['T:2', 'T:23'],
|
|
47
|
+
22: ['T:23'],
|
|
48
|
+
23: [2, 'S', 'B'],
|
|
49
|
+
24: [2, 'T:6', 'C:HasSemantics'],
|
|
50
|
+
25: ['T:2', 'C:MethodDefOrRef', 'C:MethodDefOrRef'],
|
|
51
|
+
26: ['S'],
|
|
52
|
+
27: ['B'],
|
|
53
|
+
28: [2, 'C:MemberForwarded', 'S', 'T:26'],
|
|
54
|
+
29: [4, 'T:4'],
|
|
55
|
+
30: [4, 4],
|
|
56
|
+
31: [4],
|
|
57
|
+
32: [4, 2, 2, 2, 2, 4, 'B', 'S', 'S'],
|
|
58
|
+
33: [4],
|
|
59
|
+
34: [4, 4, 4],
|
|
60
|
+
35: [2, 2, 2, 2, 4, 'B', 'S', 'S', 'B'],
|
|
61
|
+
36: [4, 'T:35'],
|
|
62
|
+
37: [4, 4, 4, 'T:35'],
|
|
63
|
+
38: [4, 'S', 'B'],
|
|
64
|
+
39: [4, 4, 'S', 'S', 'C:Implementation'],
|
|
65
|
+
40: [4, 4, 'S', 'C:Implementation'],
|
|
66
|
+
41: ['T:2', 'T:2'],
|
|
67
|
+
42: [2, 2, 'C:TypeOrMethodDef', 'S'],
|
|
68
|
+
43: ['C:MethodDefOrRef', 'B'],
|
|
69
|
+
44: ['T:42', 'C:TypeDefOrRef'],
|
|
70
|
+
};
|
|
71
|
+
function emptyInfo() {
|
|
72
|
+
return {
|
|
73
|
+
isManaged: false, runtimeVersion: null, assembly: null, assemblyRefs: [], typeCount: 0, types: [],
|
|
74
|
+
methodCount: 0, pinvokes: [], userStrings: [], entryPointToken: null, warnings: [], truncated: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function inBounds(b, off, len) {
|
|
78
|
+
if (!Number.isInteger(off) || !Number.isInteger(len) || off < 0 || len < 0 || off + len > b.length) {
|
|
79
|
+
throw new RangeError('out-of-bounds metadata range');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function readPeLayout(b) {
|
|
83
|
+
if (b.length < 0x40 || b[0] !== 0x4d || b[1] !== 0x5a)
|
|
84
|
+
return null;
|
|
85
|
+
const pe = b.readUInt32LE(0x3c);
|
|
86
|
+
if (b.toString('latin1', pe, pe + 4) !== 'PE\0\0')
|
|
87
|
+
return null;
|
|
88
|
+
const coff = pe + 4;
|
|
89
|
+
const nSections = Math.min(b.readUInt16LE(coff + 2), 96); // PE format limit
|
|
90
|
+
const optSize = b.readUInt16LE(coff + 16);
|
|
91
|
+
const opt = coff + 20;
|
|
92
|
+
const plus = b.readUInt16LE(opt) === 0x20b;
|
|
93
|
+
const secs = [];
|
|
94
|
+
for (let i = 0; i < nSections; i++) {
|
|
95
|
+
const o = opt + optSize + i * 40;
|
|
96
|
+
const vsize = b.readUInt32LE(o + 8);
|
|
97
|
+
secs.push({ va: b.readUInt32LE(o + 12), span: Math.max(vsize, b.readUInt32LE(o + 16)), raw: b.readUInt32LE(o + 20) });
|
|
98
|
+
}
|
|
99
|
+
const toOff = (rva) => {
|
|
100
|
+
const s = secs.find((x) => rva >= x.va && rva < x.va + x.span);
|
|
101
|
+
if (!s)
|
|
102
|
+
throw new RangeError('unmapped RVA');
|
|
103
|
+
return s.raw + (rva - s.va);
|
|
104
|
+
};
|
|
105
|
+
const dirBase = opt + (plus ? 112 : 96);
|
|
106
|
+
return { toOff, clrRva: b.readUInt32LE(dirBase + 14 * 8), clrSize: b.readUInt32LE(dirBase + 14 * 8 + 4) };
|
|
107
|
+
}
|
|
108
|
+
function readCompressedUInt(b, off, end) {
|
|
109
|
+
if (off >= end)
|
|
110
|
+
throw new RangeError('compressed int');
|
|
111
|
+
const first = b[off];
|
|
112
|
+
if ((first & 0x80) === 0)
|
|
113
|
+
return { value: first, len: 1 };
|
|
114
|
+
if ((first & 0xc0) === 0x80) {
|
|
115
|
+
if (off + 2 > end)
|
|
116
|
+
throw new RangeError('compressed int');
|
|
117
|
+
return { value: ((first & 0x3f) << 8) | b[off + 1], len: 2 };
|
|
118
|
+
}
|
|
119
|
+
if (off + 4 > end)
|
|
120
|
+
throw new RangeError('compressed int');
|
|
121
|
+
return { value: ((first & 0x1f) << 24) | (b[off + 1] << 16) | (b[off + 2] << 8) | b[off + 3], len: 4 };
|
|
122
|
+
}
|
|
123
|
+
function guarded(info, label, fn) {
|
|
124
|
+
try {
|
|
125
|
+
fn();
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
info.warnings.push(`${label}: ${err instanceof RangeError ? err.message : String(err)}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Inspects a managed assembly's metadata. Never throws on hostile input. */
|
|
132
|
+
export function parseDotNet(b) {
|
|
133
|
+
const info = emptyInfo();
|
|
134
|
+
try {
|
|
135
|
+
parseInto(b, info);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
info.warnings.push(`metadata parse failed: ${err instanceof RangeError ? err.message : String(err)}`);
|
|
139
|
+
}
|
|
140
|
+
return info;
|
|
141
|
+
}
|
|
142
|
+
function parseInto(b, info) {
|
|
143
|
+
const layout = readPeLayout(b);
|
|
144
|
+
if (!layout) {
|
|
145
|
+
info.warnings.push('not a PE file');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (!layout.clrRva)
|
|
149
|
+
return; // native PE: isManaged stays false
|
|
150
|
+
info.isManaged = true;
|
|
151
|
+
const cor = layout.toOff(layout.clrRva);
|
|
152
|
+
inBounds(b, cor, 24);
|
|
153
|
+
info.entryPointToken = b.readUInt32LE(cor + 20) || null;
|
|
154
|
+
const meta = layout.toOff(b.readUInt32LE(cor + 8));
|
|
155
|
+
const metaSize = b.readUInt32LE(cor + 12);
|
|
156
|
+
inBounds(b, meta, Math.min(metaSize, 32));
|
|
157
|
+
if (b.readUInt32LE(meta) !== 0x424a5342)
|
|
158
|
+
throw new RangeError('missing BSJB metadata signature');
|
|
159
|
+
const verLen = b.readUInt32LE(meta + 12);
|
|
160
|
+
if (verLen > 256)
|
|
161
|
+
throw new RangeError('implausible version string length');
|
|
162
|
+
inBounds(b, meta + 16, verLen);
|
|
163
|
+
info.runtimeVersion = b.toString('latin1', meta + 16, meta + 16 + verLen).replace(/\0+$/, '');
|
|
164
|
+
const nStreams = b.readUInt16LE(meta + 18 + verLen);
|
|
165
|
+
const streams = new Map();
|
|
166
|
+
let p = meta + 20 + verLen;
|
|
167
|
+
for (let i = 0; i < Math.min(nStreams, 16); i++) {
|
|
168
|
+
inBounds(b, p, 9);
|
|
169
|
+
const off = b.readUInt32LE(p);
|
|
170
|
+
const size = b.readUInt32LE(p + 4);
|
|
171
|
+
let q = p + 8;
|
|
172
|
+
while (q < b.length && b[q] !== 0 && q - p < 40)
|
|
173
|
+
q++;
|
|
174
|
+
const name = b.toString('latin1', p + 8, q);
|
|
175
|
+
inBounds(b, meta + off, size);
|
|
176
|
+
streams.set(name, { off: meta + off, size });
|
|
177
|
+
p = p + 8 + Math.ceil((q - (p + 8) + 1) / 4) * 4;
|
|
178
|
+
}
|
|
179
|
+
const strHeap = streams.get('#Strings');
|
|
180
|
+
const us = streams.get('#US');
|
|
181
|
+
const tbl = streams.get('#~') ?? streams.get('#-');
|
|
182
|
+
if (!tbl || !strHeap)
|
|
183
|
+
throw new RangeError('missing #~ or #Strings stream');
|
|
184
|
+
const str = (idx) => {
|
|
185
|
+
if (idx >= strHeap.size)
|
|
186
|
+
throw new RangeError('string index out of range');
|
|
187
|
+
const start = strHeap.off + idx;
|
|
188
|
+
let end = start;
|
|
189
|
+
const limit = Math.min(strHeap.off + strHeap.size, start + MAX_STR);
|
|
190
|
+
while (end < limit && b[end] !== 0)
|
|
191
|
+
end++;
|
|
192
|
+
return b.toString('utf8', start, end);
|
|
193
|
+
};
|
|
194
|
+
guarded(info, 'tables', () => readTables(b, info, tbl, str));
|
|
195
|
+
if (us) {
|
|
196
|
+
guarded(info, 'user strings', () => {
|
|
197
|
+
const end = us.off + us.size;
|
|
198
|
+
let o = us.off + 1;
|
|
199
|
+
while (o < end && info.userStrings.length < MAX_USER_STRINGS) {
|
|
200
|
+
const { value, len } = readCompressedUInt(b, o, end);
|
|
201
|
+
o += len;
|
|
202
|
+
if (value === 0)
|
|
203
|
+
continue;
|
|
204
|
+
if (o + value > end)
|
|
205
|
+
break;
|
|
206
|
+
const text = b.toString('utf16le', o, o + Math.max(0, value - 1));
|
|
207
|
+
if (text.trim())
|
|
208
|
+
info.userStrings.push(text.slice(0, MAX_STR));
|
|
209
|
+
o += value;
|
|
210
|
+
}
|
|
211
|
+
if (o < end)
|
|
212
|
+
info.truncated = true;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function readTables(b, info, tbl, str) {
|
|
217
|
+
inBounds(b, tbl.off, 24);
|
|
218
|
+
const heapSizes = b[tbl.off + 6];
|
|
219
|
+
const valid = b.readBigUInt64LE(tbl.off + 8);
|
|
220
|
+
const sIdx = heapSizes & 1 ? 4 : 2;
|
|
221
|
+
const gIdx = heapSizes & 2 ? 4 : 2;
|
|
222
|
+
const bIdx = heapSizes & 4 ? 4 : 2;
|
|
223
|
+
const rows = new Array(64).fill(0);
|
|
224
|
+
let p = tbl.off + 24;
|
|
225
|
+
for (let t = 0; t < 64; t++) {
|
|
226
|
+
if ((valid >> BigInt(t)) & 1n) {
|
|
227
|
+
inBounds(b, p, 4);
|
|
228
|
+
rows[t] = b.readUInt32LE(p);
|
|
229
|
+
p += 4;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const tableIdx = (t) => (rows[t] < 0x10000 ? 2 : 4);
|
|
233
|
+
const codedIdx = (name) => {
|
|
234
|
+
const c = CODED[name];
|
|
235
|
+
const max = Math.max(...c.tables.map((t) => rows[t]));
|
|
236
|
+
return max < 2 ** (16 - c.bits) ? 2 : 4;
|
|
237
|
+
};
|
|
238
|
+
const tables = {};
|
|
239
|
+
let off = p;
|
|
240
|
+
for (let t = 0; t < 64; t++) {
|
|
241
|
+
if (!rows[t])
|
|
242
|
+
continue;
|
|
243
|
+
const schema = SCHEMA[t];
|
|
244
|
+
if (!schema)
|
|
245
|
+
throw new RangeError(`unknown metadata table ${t}`);
|
|
246
|
+
const colSize = schema.map((c) => (typeof c === 'number' ? c : c === 'S' ? sIdx : c === 'G' ? gIdx : c === 'B' ? bIdx : c.startsWith('T:') ? tableIdx(Number(c.slice(2))) : codedIdx(c.slice(2))));
|
|
247
|
+
const colOff = [];
|
|
248
|
+
let rowSize = 0;
|
|
249
|
+
for (const s of colSize) {
|
|
250
|
+
colOff.push(rowSize);
|
|
251
|
+
rowSize += s;
|
|
252
|
+
}
|
|
253
|
+
tables[t] = { rows: rows[t], rowSize, off, colOff, colSize };
|
|
254
|
+
off += rows[t] * rowSize;
|
|
255
|
+
if (off > tbl.off + tbl.size)
|
|
256
|
+
throw new RangeError('table extents exceed #~ stream');
|
|
257
|
+
}
|
|
258
|
+
const cell = (t, row, col) => {
|
|
259
|
+
const tb = tables[t];
|
|
260
|
+
const o = tb.off + row * tb.rowSize + tb.colOff[col];
|
|
261
|
+
const size = tb.colSize[col];
|
|
262
|
+
return size === 1 ? b[o] : size === 2 ? b.readUInt16LE(o) : b.readUInt32LE(o);
|
|
263
|
+
};
|
|
264
|
+
if (tables[32]) {
|
|
265
|
+
info.assembly = { name: str(cell(32, 0, 7)), version: `${cell(32, 0, 1)}.${cell(32, 0, 2)}.${cell(32, 0, 3)}.${cell(32, 0, 4)}` };
|
|
266
|
+
}
|
|
267
|
+
if (tables[35]) {
|
|
268
|
+
const n = Math.min(tables[35].rows, MAX_LIST);
|
|
269
|
+
if (n < tables[35].rows)
|
|
270
|
+
info.truncated = true;
|
|
271
|
+
for (let i = 0; i < n; i++) {
|
|
272
|
+
info.assemblyRefs.push({ name: str(cell(35, i, 6)), version: `${cell(35, i, 0)}.${cell(35, i, 1)}.${cell(35, i, 2)}.${cell(35, i, 3)}` });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (tables[2]) {
|
|
276
|
+
info.typeCount = tables[2].rows;
|
|
277
|
+
const n = Math.min(tables[2].rows, MAX_LIST);
|
|
278
|
+
if (n < tables[2].rows)
|
|
279
|
+
info.truncated = true;
|
|
280
|
+
for (let i = 0; i < n; i++) {
|
|
281
|
+
const name = str(cell(2, i, 1));
|
|
282
|
+
const ns = str(cell(2, i, 2));
|
|
283
|
+
info.types.push(ns ? `${ns}.${name}` : name);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
info.methodCount = tables[6]?.rows ?? 0;
|
|
287
|
+
if (tables[28] && tables[26]) {
|
|
288
|
+
const n = Math.min(tables[28].rows, MAX_LIST);
|
|
289
|
+
if (n < tables[28].rows)
|
|
290
|
+
info.truncated = true;
|
|
291
|
+
for (let i = 0; i < n; i++) {
|
|
292
|
+
const scope = cell(28, i, 3); // 1-based ModuleRef row
|
|
293
|
+
if (scope < 1 || scope > tables[26].rows)
|
|
294
|
+
continue;
|
|
295
|
+
info.pinvokes.push({ module: str(cell(26, scope - 1, 0)), function: str(cell(28, i, 2)) });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=dotnet-parse.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 4 — EVM bytecode static inventory (ADR-0005).
|
|
3
|
+
* Aligned disassembly + pattern scan over bytes. There is no interpreter:
|
|
4
|
+
* nothing is executed, no RPC, no network.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MAX_EVM_BYTES: number;
|
|
7
|
+
export declare function opcodeName(op: number): string | null;
|
|
8
|
+
export interface EvmInfo {
|
|
9
|
+
byteLength: number;
|
|
10
|
+
instructionCount: number;
|
|
11
|
+
jumpdestCount: number;
|
|
12
|
+
invalidOpcodeCount: number;
|
|
13
|
+
selectors: {
|
|
14
|
+
selector: string;
|
|
15
|
+
name: string | null;
|
|
16
|
+
offset: number;
|
|
17
|
+
}[];
|
|
18
|
+
hazards: Record<string, {
|
|
19
|
+
count: number;
|
|
20
|
+
offsets: number[];
|
|
21
|
+
}>;
|
|
22
|
+
addresses: string[];
|
|
23
|
+
eventTopics: {
|
|
24
|
+
topic: string;
|
|
25
|
+
name: string | null;
|
|
26
|
+
offset: number;
|
|
27
|
+
}[];
|
|
28
|
+
minimalProxy: {
|
|
29
|
+
implementation: string;
|
|
30
|
+
} | null;
|
|
31
|
+
metadata: {
|
|
32
|
+
solc?: string;
|
|
33
|
+
ipfs?: string;
|
|
34
|
+
bzzr?: string;
|
|
35
|
+
experimental?: boolean;
|
|
36
|
+
} | null;
|
|
37
|
+
strings: string[];
|
|
38
|
+
disassembly?: string[];
|
|
39
|
+
warnings: string[];
|
|
40
|
+
truncated: boolean;
|
|
41
|
+
}
|
|
42
|
+
export type HexParse = {
|
|
43
|
+
ok: true;
|
|
44
|
+
bytes: Buffer;
|
|
45
|
+
} | {
|
|
46
|
+
ok: false;
|
|
47
|
+
error: string;
|
|
48
|
+
};
|
|
49
|
+
/** Accepts `0x`-optional hex; rejects odd length, non-hex, empty, and over-cap input. */
|
|
50
|
+
export declare function parseHex(text: string): HexParse;
|
|
51
|
+
export declare function analyzeEvm(b: Buffer, includeDisassembly?: boolean): EvmInfo;
|
|
52
|
+
//# sourceMappingURL=evm-parse.d.ts.map
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 4 — EVM bytecode static inventory (ADR-0005).
|
|
3
|
+
* Aligned disassembly + pattern scan over bytes. There is no interpreter:
|
|
4
|
+
* nothing is executed, no RPC, no network.
|
|
5
|
+
*/
|
|
6
|
+
export const MAX_EVM_BYTES = 256 * 1024;
|
|
7
|
+
const MAX_LIST = 200;
|
|
8
|
+
const MAX_OFFSETS = 20;
|
|
9
|
+
const MAX_STRINGS = 100;
|
|
10
|
+
const SIMPLE = {
|
|
11
|
+
0x00: 'STOP', 0x01: 'ADD', 0x02: 'MUL', 0x03: 'SUB', 0x04: 'DIV', 0x05: 'SDIV', 0x06: 'MOD', 0x07: 'SMOD', 0x08: 'ADDMOD',
|
|
12
|
+
0x09: 'MULMOD', 0x0a: 'EXP', 0x0b: 'SIGNEXTEND', 0x10: 'LT', 0x11: 'GT', 0x12: 'SLT', 0x13: 'SGT', 0x14: 'EQ', 0x15: 'ISZERO',
|
|
13
|
+
0x16: 'AND', 0x17: 'OR', 0x18: 'XOR', 0x19: 'NOT', 0x1a: 'BYTE', 0x1b: 'SHL', 0x1c: 'SHR', 0x1d: 'SAR', 0x20: 'KECCAK256',
|
|
14
|
+
0x30: 'ADDRESS', 0x31: 'BALANCE', 0x32: 'ORIGIN', 0x33: 'CALLER', 0x34: 'CALLVALUE', 0x35: 'CALLDATALOAD', 0x36: 'CALLDATASIZE',
|
|
15
|
+
0x37: 'CALLDATACOPY', 0x38: 'CODESIZE', 0x39: 'CODECOPY', 0x3a: 'GASPRICE', 0x3b: 'EXTCODESIZE', 0x3c: 'EXTCODECOPY',
|
|
16
|
+
0x3d: 'RETURNDATASIZE', 0x3e: 'RETURNDATACOPY', 0x3f: 'EXTCODEHASH', 0x40: 'BLOCKHASH', 0x41: 'COINBASE', 0x42: 'TIMESTAMP',
|
|
17
|
+
0x43: 'NUMBER', 0x44: 'PREVRANDAO', 0x45: 'GASLIMIT', 0x46: 'CHAINID', 0x47: 'SELFBALANCE', 0x48: 'BASEFEE', 0x49: 'BLOBHASH',
|
|
18
|
+
0x4a: 'BLOBBASEFEE', 0x50: 'POP', 0x51: 'MLOAD', 0x52: 'MSTORE', 0x53: 'MSTORE8', 0x54: 'SLOAD', 0x55: 'SSTORE', 0x56: 'JUMP',
|
|
19
|
+
0x57: 'JUMPI', 0x58: 'PC', 0x59: 'MSIZE', 0x5a: 'GAS', 0x5b: 'JUMPDEST', 0x5c: 'TLOAD', 0x5d: 'TSTORE', 0x5e: 'MCOPY', 0x5f: 'PUSH0',
|
|
20
|
+
0xf0: 'CREATE', 0xf1: 'CALL', 0xf2: 'CALLCODE', 0xf3: 'RETURN', 0xf4: 'DELEGATECALL', 0xf5: 'CREATE2', 0xfa: 'STATICCALL',
|
|
21
|
+
0xfd: 'REVERT', 0xfe: 'INVALID', 0xff: 'SELFDESTRUCT',
|
|
22
|
+
};
|
|
23
|
+
export function opcodeName(op) {
|
|
24
|
+
if (SIMPLE[op])
|
|
25
|
+
return SIMPLE[op];
|
|
26
|
+
if (op >= 0x60 && op <= 0x7f)
|
|
27
|
+
return `PUSH${op - 0x5f}`;
|
|
28
|
+
if (op >= 0x80 && op <= 0x8f)
|
|
29
|
+
return `DUP${op - 0x7f}`;
|
|
30
|
+
if (op >= 0x90 && op <= 0x9f)
|
|
31
|
+
return `SWAP${op - 0x8f}`;
|
|
32
|
+
if (op >= 0xa0 && op <= 0xa4)
|
|
33
|
+
return `LOG${op - 0xa0}`;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const HAZARDS = {
|
|
37
|
+
0xff: 'SELFDESTRUCT', 0xf4: 'DELEGATECALL', 0xf2: 'CALLCODE', 0xf0: 'CREATE', 0xf5: 'CREATE2', 0x32: 'ORIGIN',
|
|
38
|
+
0xf1: 'CALL', 0xfa: 'STATICCALL',
|
|
39
|
+
};
|
|
40
|
+
const KNOWN_SELECTORS = {
|
|
41
|
+
'0xa9059cbb': 'transfer(address,uint256)', '0x70a08231': 'balanceOf(address)', '0x23b872dd': 'transferFrom(address,address,uint256)',
|
|
42
|
+
'0x095ea7b3': 'approve(address,uint256)', '0xdd62ed3e': 'allowance(address,address)', '0x18160ddd': 'totalSupply()',
|
|
43
|
+
'0x06fdde03': 'name()', '0x95d89b41': 'symbol()', '0x313ce567': 'decimals()', '0x8da5cb5b': 'owner()',
|
|
44
|
+
'0xf2fde38b': 'transferOwnership(address)', '0x715018a6': 'renounceOwnership()', '0x3659cfe6': 'upgradeTo(address)',
|
|
45
|
+
'0x5c60da1b': 'implementation()', '0x01ffc9a7': 'supportsInterface(bytes4)', '0x42842e0e': 'safeTransferFrom(address,address,uint256)',
|
|
46
|
+
'0x6352211e': 'ownerOf(uint256)', '0x40c10f19': 'mint(address,uint256)', '0x42966c68': 'burn(uint256)',
|
|
47
|
+
'0x8456cb59': 'pause()', '0x3f4ba83a': 'unpause()',
|
|
48
|
+
};
|
|
49
|
+
const KNOWN_TOPICS = {
|
|
50
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef': 'Transfer(address,address,uint256)',
|
|
51
|
+
'0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925': 'Approval(address,address,uint256)',
|
|
52
|
+
};
|
|
53
|
+
/** Accepts `0x`-optional hex; rejects odd length, non-hex, empty, and over-cap input. */
|
|
54
|
+
export function parseHex(text) {
|
|
55
|
+
const t = text.trim().replace(/^0x/i, '');
|
|
56
|
+
if (!t)
|
|
57
|
+
return { ok: false, error: 'empty bytecode' };
|
|
58
|
+
if (t.length % 2 !== 0)
|
|
59
|
+
return { ok: false, error: 'odd number of hex digits' };
|
|
60
|
+
if (t.length / 2 > MAX_EVM_BYTES)
|
|
61
|
+
return { ok: false, error: `bytecode exceeds the ${MAX_EVM_BYTES}-byte cap` };
|
|
62
|
+
if (!/^[0-9a-fA-F]+$/.test(t))
|
|
63
|
+
return { ok: false, error: 'not hexadecimal' };
|
|
64
|
+
return { ok: true, bytes: Buffer.from(t, 'hex') };
|
|
65
|
+
}
|
|
66
|
+
function hex(b, start, end) {
|
|
67
|
+
return '0x' + b.toString('hex', start, end);
|
|
68
|
+
}
|
|
69
|
+
function parseMetadata(b) {
|
|
70
|
+
if (b.length < 4)
|
|
71
|
+
return null;
|
|
72
|
+
const len = (b[b.length - 2] << 8) | b[b.length - 1];
|
|
73
|
+
const start = b.length - 2 - len;
|
|
74
|
+
if (len < 3 || len > 128 || start < 0)
|
|
75
|
+
return null;
|
|
76
|
+
const end = b.length - 2;
|
|
77
|
+
let o = start;
|
|
78
|
+
if ((b[o] & 0xe0) !== 0xa0)
|
|
79
|
+
return null;
|
|
80
|
+
const pairs = b[o] & 0x1f;
|
|
81
|
+
o += 1;
|
|
82
|
+
const out = {};
|
|
83
|
+
for (let i = 0; i < pairs; i++) {
|
|
84
|
+
if (o >= end || (b[o] & 0xe0) !== 0x60)
|
|
85
|
+
return null;
|
|
86
|
+
const klen = b[o] & 0x1f;
|
|
87
|
+
if (klen >= 24 || o + 1 + klen > end)
|
|
88
|
+
return null;
|
|
89
|
+
const key = b.toString('latin1', o + 1, o + 1 + klen);
|
|
90
|
+
o += 1 + klen;
|
|
91
|
+
if (o >= end)
|
|
92
|
+
return null;
|
|
93
|
+
const head = b[o];
|
|
94
|
+
if (head === 0xf5 || head === 0xf4) {
|
|
95
|
+
if (key === 'experimental')
|
|
96
|
+
out.experimental = head === 0xf5;
|
|
97
|
+
o += 1;
|
|
98
|
+
}
|
|
99
|
+
else if ((head & 0xe0) === 0x40) {
|
|
100
|
+
let vlen = head & 0x1f;
|
|
101
|
+
let vo = o + 1;
|
|
102
|
+
if (vlen === 24) {
|
|
103
|
+
vlen = b[vo];
|
|
104
|
+
vo += 1;
|
|
105
|
+
}
|
|
106
|
+
else if (vlen > 24)
|
|
107
|
+
return null;
|
|
108
|
+
if (vo + vlen > end)
|
|
109
|
+
return null;
|
|
110
|
+
if (key === 'solc' && vlen === 3)
|
|
111
|
+
out.solc = `${b[vo]}.${b[vo + 1]}.${b[vo + 2]}`;
|
|
112
|
+
else if (key === 'ipfs')
|
|
113
|
+
out.ipfs = b.toString('hex', vo, vo + vlen);
|
|
114
|
+
else if (key.startsWith('bzzr'))
|
|
115
|
+
out.bzzr = b.toString('hex', vo, vo + vlen);
|
|
116
|
+
o = vo + vlen;
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return o === end && (out.solc || out.ipfs || out.bzzr) ? out : null;
|
|
122
|
+
}
|
|
123
|
+
const EIP1167_PREFIX = Buffer.from('363d3d373d3d3d363d73', 'hex');
|
|
124
|
+
const EIP1167_SUFFIX = Buffer.from('5af43d82803e903d91602b57fd5bf3', 'hex');
|
|
125
|
+
function detectMinimalProxy(b) {
|
|
126
|
+
if (b.length < 45 || !b.subarray(0, 10).equals(EIP1167_PREFIX) || !b.subarray(30, 45).equals(EIP1167_SUFFIX))
|
|
127
|
+
return null;
|
|
128
|
+
return { implementation: hex(b, 10, 30) };
|
|
129
|
+
}
|
|
130
|
+
/** Aligned disassembly: PUSH immediates are data, never opcodes. Truncated trailing PUSH data is clamped. */
|
|
131
|
+
function disassemble(b) {
|
|
132
|
+
const out = [];
|
|
133
|
+
for (let i = 0; i < b.length;) {
|
|
134
|
+
const op = b[i];
|
|
135
|
+
const n = op >= 0x60 && op <= 0x7f ? op - 0x5f : 0;
|
|
136
|
+
out.push({ off: i, op, dataStart: i + 1, dataLen: Math.min(n, b.length - i - 1) });
|
|
137
|
+
i += 1 + n;
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
export function analyzeEvm(b, includeDisassembly = false) {
|
|
142
|
+
const info = {
|
|
143
|
+
byteLength: b.length, instructionCount: 0, jumpdestCount: 0, invalidOpcodeCount: 0, selectors: [], hazards: {},
|
|
144
|
+
addresses: [], eventTopics: [], minimalProxy: null, metadata: null, strings: [], warnings: [], truncated: false,
|
|
145
|
+
};
|
|
146
|
+
if (b.length > MAX_EVM_BYTES) {
|
|
147
|
+
info.warnings.push(`bytecode exceeds the ${MAX_EVM_BYTES}-byte cap; nothing analyzed`);
|
|
148
|
+
return info;
|
|
149
|
+
}
|
|
150
|
+
info.minimalProxy = detectMinimalProxy(b);
|
|
151
|
+
info.metadata = parseMetadata(b);
|
|
152
|
+
const ins = disassemble(b);
|
|
153
|
+
info.instructionCount = ins.length;
|
|
154
|
+
const addrSeen = new Set();
|
|
155
|
+
const strSeen = new Set();
|
|
156
|
+
const seenSel = new Set();
|
|
157
|
+
if (includeDisassembly) {
|
|
158
|
+
info.disassembly = ins.slice(0, 500).map((x) => {
|
|
159
|
+
const name = opcodeName(x.op) ?? `UNKNOWN(0x${x.op.toString(16)})`;
|
|
160
|
+
return x.dataLen ? `${x.off} ${name} ${hex(b, x.dataStart, x.dataStart + x.dataLen)}` : `${x.off} ${name}`;
|
|
161
|
+
});
|
|
162
|
+
if (ins.length > 500)
|
|
163
|
+
info.truncated = true;
|
|
164
|
+
}
|
|
165
|
+
for (let k = 0; k < ins.length; k++) {
|
|
166
|
+
const x = ins[k];
|
|
167
|
+
if (x.op === 0x5b)
|
|
168
|
+
info.jumpdestCount += 1;
|
|
169
|
+
if (opcodeName(x.op) === null)
|
|
170
|
+
info.invalidOpcodeCount += 1;
|
|
171
|
+
const hazard = HAZARDS[x.op];
|
|
172
|
+
if (hazard) {
|
|
173
|
+
const h = (info.hazards[hazard] ??= { count: 0, offsets: [] });
|
|
174
|
+
h.count += 1;
|
|
175
|
+
if (h.offsets.length < MAX_OFFSETS)
|
|
176
|
+
h.offsets.push(x.off);
|
|
177
|
+
}
|
|
178
|
+
if (x.op === 0x63 && x.dataLen === 4) {
|
|
179
|
+
const next = ins[k + 1];
|
|
180
|
+
const after = ins[k + 2];
|
|
181
|
+
const isEq = next?.op === 0x14 || (next?.op === 0x81 && after?.op === 0x14);
|
|
182
|
+
const sel = hex(b, x.dataStart, x.dataStart + 4);
|
|
183
|
+
if (isEq && !seenSel.has(sel)) {
|
|
184
|
+
seenSel.add(sel);
|
|
185
|
+
if (info.selectors.length < MAX_LIST)
|
|
186
|
+
info.selectors.push({ selector: sel, name: KNOWN_SELECTORS[sel] ?? null, offset: x.off });
|
|
187
|
+
else
|
|
188
|
+
info.truncated = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (x.op === 0x73 && x.dataLen === 20) {
|
|
192
|
+
const a = hex(b, x.dataStart, x.dataStart + 20);
|
|
193
|
+
const nonTrivial = /[1-9a-f]/.test(a.slice(2)) && !/^0x(f{40})$/.test(a);
|
|
194
|
+
if (nonTrivial && !addrSeen.has(a)) {
|
|
195
|
+
addrSeen.add(a);
|
|
196
|
+
if (info.addresses.length < MAX_LIST)
|
|
197
|
+
info.addresses.push(a);
|
|
198
|
+
else
|
|
199
|
+
info.truncated = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (x.op >= 0xa1 && x.op <= 0xa4) {
|
|
203
|
+
for (let j = k - 1; j >= Math.max(0, k - 12); j--) {
|
|
204
|
+
if (ins[j].op === 0x7f && ins[j].dataLen === 32) {
|
|
205
|
+
const topic = hex(b, ins[j].dataStart, ins[j].dataStart + 32);
|
|
206
|
+
if (!info.eventTopics.some((t) => t.topic === topic) && info.eventTopics.length < MAX_LIST) {
|
|
207
|
+
info.eventTopics.push({ topic, name: KNOWN_TOPICS[topic] ?? null, offset: x.off });
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (x.op >= 0x60 && x.op <= 0x7f && x.dataLen >= 8) {
|
|
214
|
+
const raw = b.subarray(x.dataStart, x.dataStart + x.dataLen);
|
|
215
|
+
let end = raw.length;
|
|
216
|
+
while (end > 0 && raw[end - 1] === 0)
|
|
217
|
+
end--;
|
|
218
|
+
let start = 0;
|
|
219
|
+
while (start < end && raw[start] === 0)
|
|
220
|
+
start++;
|
|
221
|
+
const text = raw.subarray(start, end);
|
|
222
|
+
if (text.length >= 8 && text.every((c) => c >= 0x20 && c < 0x7f)) {
|
|
223
|
+
const s = text.toString('latin1');
|
|
224
|
+
if (!strSeen.has(s) && info.strings.length < MAX_STRINGS) {
|
|
225
|
+
strSeen.add(s);
|
|
226
|
+
info.strings.push(s);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return info;
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=evm-parse.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ruRE Phase 1 — ASAR extraction with resource caps (ADR-0002).
|
|
3
|
+
*
|
|
4
|
+
* @electron/asar already guards path traversal internally
|
|
5
|
+
* (lib/path-validation.ts's ensureWithin); this module adds the
|
|
6
|
+
* decompression-bomb guard ADR-0002 requires, by reading the archive header
|
|
7
|
+
* (no extraction) and summing declared sizes BEFORE calling extractAll.
|
|
8
|
+
*
|
|
9
|
+
* ruRE never writes into a path derived from archive entry names — the
|
|
10
|
+
* destination is always a ruRE-owned scratch directory.
|
|
11
|
+
*/
|
|
12
|
+
export interface AsarExtractLimits {
|
|
13
|
+
/** Maximum total declared uncompressed size, in bytes. */
|
|
14
|
+
maxTotalBytes: number;
|
|
15
|
+
/** Maximum number of files. */
|
|
16
|
+
maxFileCount: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const DEFAULT_ASAR_LIMITS: AsarExtractLimits;
|
|
19
|
+
export interface AsarExtractResult {
|
|
20
|
+
unpackedPath: string;
|
|
21
|
+
fileCount: number;
|
|
22
|
+
totalBytes: number;
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
reason?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Extracts an .asar archive to a fresh ruRE-owned scratch directory, after
|
|
28
|
+
* verifying the archive's declared size/file-count against `limits`. Never
|
|
29
|
+
* extracts (even partially) when the pre-check fails — `truncated: true`
|
|
30
|
+
* with no files written, not a partial extraction passed off as complete.
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractAsarWithCaps(archivePath: string, limits?: AsarExtractLimits): Promise<AsarExtractResult>;
|
|
33
|
+
//# sourceMappingURL=extract-asar.d.ts.map
|