Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6876d0d249 | ||
|
|
3e6508f4c5 | ||
|
|
dabb22c09d | ||
|
|
c101193eb3 | ||
|
|
172503355d | ||
|
|
8d72e2d75e | ||
|
|
e3a6ce40f5 | ||
|
|
3ce311757d | ||
|
|
c350343c33 | ||
|
|
2c3568c375 | ||
|
|
c298d54e88 | ||
|
|
b09c6b0752 | ||
|
|
689f738c95 | ||
|
|
8328a9559f | ||
|
|
77be87db99 | ||
|
|
0c49878b9a | ||
|
|
abf7d300b3 | ||
|
|
0f3a15de0d | ||
|
|
781760a195 | ||
|
|
977c9727e6 | ||
|
|
4a041ae753 | ||
|
|
c2068b94d7 | ||
|
|
e00ee310f5 | ||
|
|
72ba1cd92b | ||
|
|
98ec00f406 | ||
|
|
bdf15b9bd5 | ||
|
|
d68d03e88f | ||
|
|
d87e73f2db | ||
|
|
b5c42dfc2a | ||
|
|
b869fa762a | ||
|
|
21a20306b4 | ||
|
|
f774dc420b | ||
|
|
e5965adb29 | ||
|
|
ace47789a8 | ||
|
|
60242ec82d | ||
|
|
1d896e03b7 | ||
|
|
915e56e3e5 | ||
|
|
850abe5e6f | ||
|
|
ddfd060210 | ||
|
|
aa9043f6b6 | ||
|
|
6843dbea9b | ||
|
|
21097b192b | ||
|
|
f9fabdfcb2 | ||
|
|
ead9fdcda9 | ||
|
|
c2caae88f8 | ||
|
|
3ad465a666 | ||
|
|
4ef916da03 | ||
|
|
f508a1a47e | ||
|
|
f2eb27206f | ||
|
|
39aca92239 | ||
|
|
ae24b7879c | ||
|
|
dd198d037f | ||
|
|
8736fe3226 | ||
|
|
0f411508c3 | ||
|
|
fbe77dc479 | ||
|
|
ba6cec31f5 | ||
|
|
1564fc8d9b | ||
|
|
41e10b240c | ||
|
|
d662fa54a6 | ||
|
|
008931a178 | ||
|
|
cd10ee1f8a | ||
|
|
80b7bbc8bd | ||
|
|
f3269feaad | ||
|
|
cad14f3b35 | ||
|
|
7f70797b98 | ||
|
|
9f5dd56e2f | ||
|
|
2bd981b017 | ||
|
|
6021efd371 | ||
|
|
7958e15892 | ||
|
|
e09b06369d | ||
|
|
bd1b2c74ae | ||
|
|
e2463372ea | ||
|
|
315b23403f | ||
|
|
cc18e9ad0d | ||
|
|
2f2b07f750 | ||
|
|
37f24125ad | ||
|
|
263a98c6ce | ||
|
|
6298c84eea | ||
|
|
9df94a62cd | ||
|
|
2968d1e932 | ||
|
|
fb9f35cf7b | ||
|
|
584120e802 | ||
|
|
254f9d9ea2 | ||
|
|
044c9705ea | ||
|
|
3da7454ed8 | ||
|
|
f975f98ef4 | ||
|
|
85e38ac199 | ||
|
|
33f384120a | ||
|
|
7eaa7920a7 | ||
|
|
9c8f404421 | ||
|
|
959ab0c10b | ||
|
|
06822aed63 | ||
|
|
96ad94f69e | ||
|
|
676d29f9ee | ||
|
|
87745af52a | ||
|
|
b6519aa410 | ||
|
|
8defd220a9 | ||
|
|
3ae7daa101 | ||
|
|
3d3ee837c4 | ||
|
|
54f5c55f62 | ||
|
|
eea3271a33 | ||
|
|
31de177404 | ||
|
|
45d0c3e299 | ||
|
|
673a659074 | ||
|
|
1d2b26e2d5 | ||
|
|
446333cb7f | ||
|
|
d81d6b6707 | ||
|
|
54646e8dc4 | ||
|
|
8681752c7b | ||
|
|
9adc61243b | ||
|
|
09c2a2d241 | ||
|
|
f1dc595272 | ||
|
|
0ee3a35bd0 | ||
|
|
a417209080 | ||
|
|
06bafdef14 | ||
|
|
3c0b26df67 | ||
|
|
9d4293be27 | ||
|
|
1c07bee5b6 | ||
|
|
e8b2273ab6 | ||
|
|
b7c1d79da3 | ||
|
|
c765b3b413 | ||
|
|
f2dfda4b36 | ||
|
|
e6f252509f | ||
|
|
6ed96f4372 | ||
|
|
2ff85c0e4c | ||
|
|
0ba81bd9e4 | ||
|
|
3e6ac23b50 | ||
|
|
aeeb4b72c1 | ||
|
|
4394e8b592 | ||
|
|
eb721ebbf1 | ||
|
|
d3a33fcfca | ||
|
|
5502a563d5 | ||
|
|
be2a0b98ae | ||
|
|
fcd5192743 | ||
|
|
d442ac752e | ||
|
|
5940600a22 | ||
|
|
c2455152c9 | ||
|
|
4643844ea5 | ||
|
|
2496256cb7 | ||
|
|
cc4f805541 | ||
|
|
27976a8fcb | ||
|
|
67d02f4787 | ||
|
|
662e79330f | ||
|
|
04ba5caf63 | ||
|
|
c010fafe45 | ||
|
|
0de322eb91 | ||
|
|
4f6166e04d | ||
|
|
d421e3eeef | ||
|
|
36f26a1bfe | ||
|
|
a4e4863029 | ||
|
|
64f261bc6f | ||
|
|
48821e82a1 | ||
|
|
d1585fc600 | ||
|
|
4118004348 | ||
|
|
934f538f64 | ||
|
|
002f9df43a | ||
|
|
7f3e32c69b | ||
|
|
4becc220b8 | ||
|
|
9d569c344a | ||
|
|
800454c0ee | ||
|
|
5c5f8e7cc4 | ||
|
|
c6104d5095 | ||
|
|
396dcb2b0f | ||
|
|
4d22c83b07 | ||
|
|
de9d52cb5d | ||
|
|
1be8e9601b | ||
|
|
9a165287ad | ||
|
|
455f816d0b | ||
|
|
46a5f55f74 | ||
|
|
f42ade1dda | ||
|
|
7c88ef14af | ||
|
|
11feab01da | ||
|
|
6323013d83 | ||
|
|
07005b8fe6 | ||
|
|
59553c76be | ||
|
|
c3ad8fc024 | ||
|
|
54a663ddd5 | ||
|
|
e389d6ea4e | ||
|
|
4d88e4f07e | ||
|
|
1a48a82020 | ||
|
|
bf6e49a8f4 | ||
|
|
76500f12b5 | ||
|
|
64fa1db130 | ||
|
|
79805f244a | ||
|
|
f90d40720f | ||
|
|
6b3eee1ccf | ||
|
|
f11892265d | ||
|
|
f65a98f9ad | ||
|
|
4610843efd | ||
|
|
a062529bb2 | ||
|
|
fc2dddc1d9 | ||
|
|
36f528efcb | ||
|
|
9d7779d365 | ||
|
|
231e458246 | ||
|
|
3d4f55652b | ||
|
|
0785223582 | ||
|
|
794932e033 | ||
|
|
13fcf58276 | ||
|
|
edc805eca2 | ||
|
|
0134e15ab1 | ||
|
|
091d6209af | ||
|
|
60bb202ca3 | ||
|
|
f8ff1579ad | ||
|
|
1a808dd2a2 | ||
|
|
91c28e2735 | ||
|
|
2886cc1efe | ||
|
|
dbf8e1eacc | ||
|
|
3472cc4570 | ||
|
|
d7efb40997 | ||
|
|
1081008c9d | ||
|
|
667ab5ae08 | ||
|
|
0b1af48a37 | ||
|
|
b6e107aded | ||
|
|
d5df7fd944 | ||
|
|
52aeeee764 | ||
|
|
30db499db3 | ||
|
|
9dde3fd6a4 | ||
|
|
488c21ab2e | ||
|
|
cef641414d | ||
|
|
59022b6a3a | ||
|
|
836cbd3244 | ||
|
|
a5de93a214 | ||
|
|
3914e72f1b | ||
|
|
ba9c5bc686 | ||
|
|
1252f17f0e | ||
|
|
0a91b6c67b | ||
|
|
960b742581 | ||
|
|
36cae90161 | ||
|
|
bcd18d2898 | ||
|
|
097b604039 | ||
|
|
81727c3da3 | ||
|
|
f470350528 | ||
|
|
15edc71914 | ||
|
|
1778a3d494 | ||
|
|
360ced26d1 | ||
|
|
b227c301e5 | ||
|
|
658c29c9d6 | ||
|
|
5ddd7dc0d4 | ||
|
|
ac6e0e258a | ||
|
|
8d3ed58cb7 | ||
|
|
f4179f511f | ||
|
|
271ef32f0c | ||
|
|
eac392d085 | ||
|
|
5909430771 | ||
|
|
872d46a0d9 | ||
|
|
0adcd83b1a | ||
|
|
c25ee582c5 | ||
|
|
53601d0bdf | ||
|
|
e8a44f8679 | ||
|
|
a2235b09ac | ||
|
|
a8609d562f | ||
|
|
a8fc3307cd | ||
|
|
462b73ac9b | ||
|
|
1081f81484 | ||
|
|
d13a036e34 | ||
|
|
651afa4455 | ||
|
|
aa4572828f | ||
|
|
d1fdaaf386 | ||
|
|
1849a828b2 | ||
|
|
fc2d91a45f | ||
|
|
33d00a0601 | ||
|
|
e05beb39c2 | ||
|
|
250e209f32 | ||
|
|
d65c027c2d | ||
|
|
57b1f82367 | ||
|
|
d29ae43aeb | ||
|
|
beb41f8145 | ||
|
|
b03a15e6d1 | ||
|
|
2b8e388bbe | ||
|
|
5f4bd6ba34 | ||
|
|
171f56a5ed | ||
|
|
6d4cc6e6a4 | ||
|
|
b9212b5979 | ||
|
|
9ecf48bd9d | ||
|
|
2a4c70e3b7 | ||
|
|
7356434202 | ||
|
|
87a5be3000 | ||
|
|
3e3c9eadb5 | ||
|
|
3b94f2c2ef | ||
|
|
52ca222bd0 | ||
|
|
27b077edea | ||
|
|
5a099ef9ef | ||
|
|
c8be9df211 | ||
|
|
640a0cf423 | ||
|
|
c2cc81927a | ||
|
|
6e40f9f054 | ||
|
|
a51a9228be | ||
|
|
85292fdb26 | ||
|
|
fd254b5e37 | ||
|
|
7e8c7865d4 | ||
|
|
f2db494bfc | ||
|
|
9c0c2765c5 | ||
|
|
bc1c5073df | ||
|
|
9e6c6241d7 | ||
|
|
a91412aaf9 | ||
|
|
9cd46d6192 | ||
|
|
094d39926b | ||
|
|
96e1a60749 | ||
|
|
1dff1b15fb | ||
|
|
a31b4a393a | ||
|
|
19f367257d | ||
|
|
aa521d4348 | ||
|
|
d2dc3cfc34 | ||
|
|
55437c1431 | ||
|
|
f97747c2fb | ||
|
|
afc72f32e3 | ||
|
|
1872fef5c0 | ||
|
|
cc90771af1 | ||
|
|
9fb63ad066 | ||
|
|
bfa60d596e | ||
|
|
6b0c4b43a4 | ||
|
|
bbf7943ba6 | ||
|
|
75dfd1c747 | ||
|
|
6739b3cd80 | ||
|
|
20ff68914b | ||
|
|
9877ab7d1d | ||
|
|
a3eae6e236 | ||
|
|
a03096cb85 | ||
|
|
7d941e2799 | ||
|
|
d9a855f5cb | ||
|
|
0929fa4209 | ||
|
|
85b8624a98 | ||
|
|
1e688e4201 | ||
|
|
8b3902d377 | ||
|
|
d397ac7427 | ||
|
|
23664efdbb | ||
|
|
ecfac46c8d | ||
|
|
80d4a74a06 | ||
|
|
b7ebccf8d3 | ||
|
|
d69752fd52 | ||
|
|
5477a6b83e | ||
|
|
ce3c27fe92 | ||
|
|
09ba16873c | ||
|
|
c5d86a164d | ||
|
|
df00588325 | ||
|
|
aad3d88cc0 | ||
|
|
5796220f23 | ||
|
|
84c327ee65 | ||
|
|
c6ac60acfe | ||
|
|
b3433d0187 | ||
|
|
701519a5ce | ||
|
|
1bf9191bd0 | ||
|
|
d9d77726ff | ||
|
|
77ebfb5f8a | ||
|
|
6f8dc9c139 | ||
|
|
cc983b3235 | ||
|
|
ee2433a408 | ||
|
|
1cc16128ea | ||
|
|
62f7b5e4cd | ||
|
|
af3b039d31 | ||
|
|
98ddd04433 | ||
|
|
18acae9188 | ||
|
|
53405fbe44 | ||
|
|
fc11134242 | ||
|
|
fc52858966 | ||
|
|
fc706570d9 | ||
|
|
94081b0103 | ||
|
|
c6bc25f593 | ||
|
|
76381b29d4 | ||
|
|
c86288db1b | ||
|
|
cab6527125 | ||
|
|
a2d0af6023 | ||
|
|
301993db04 | ||
|
|
e909a927c4 | ||
|
|
22d3236c31 | ||
|
|
0a9bcbed42 | ||
|
|
59a00d77fb | ||
|
|
779242aef0 | ||
|
|
88bbf9c006 | ||
|
|
6ff682505c | ||
|
|
dfafc04f6d | ||
|
|
f33ab9bf38 | ||
|
|
ce9cb72559 | ||
|
|
7eb9eb021c | ||
|
|
f8248417d7 | ||
|
|
4e7fb1ee8a | ||
|
|
59f0eb4244 | ||
|
|
60a0acb396 | ||
|
|
ef00b2115a | ||
|
|
d98105d650 | ||
|
|
f83d97fe92 | ||
|
|
e1d9ebd3cf | ||
|
|
3f519c5587 | ||
|
|
782c6c6636 | ||
|
|
f01f56156e | ||
|
|
77ed32fabc | ||
|
|
b2cb9b6596 | ||
|
|
f6b525d3af | ||
|
|
6c307e9c86 | ||
|
|
8b7de17833 | ||
|
|
f713087132 | ||
|
|
09b188bb0d | ||
|
|
fc404f008d | ||
|
|
cd128795eb | ||
|
|
254439750a | ||
|
|
584791fedf | ||
|
|
9b7ba06661 | ||
|
|
2fe5d972cc | ||
|
|
ef64631f07 | ||
|
|
f3df653d9b | ||
|
|
9c4d194f14 | ||
|
|
562966500f | ||
|
|
a954e9f488 | ||
|
|
c0e35c2e93 | ||
|
|
5f3b92531e | ||
|
|
0321bb91a7 | ||
|
|
6103fddd45 | ||
|
|
ca01cf5a1f | ||
|
|
292f1058d9 | ||
|
|
b791f029aa | ||
|
|
8bb466d0dc | ||
|
|
4b029a693d | ||
|
|
3f5d879414 | ||
|
|
a4e9b61e9f | ||
|
|
da0480a665 | ||
|
|
9fd78a74f8 | ||
|
|
f36893a1a2 | ||
|
|
6aae8efd35 | ||
|
|
e94d5a5c74 | ||
|
|
57aa21af26 | ||
|
|
3a62b96194 | ||
|
|
79323128f2 | ||
|
|
84f4eb42e1 | ||
|
|
25b28ffe8b | ||
|
|
27e927f70f | ||
|
|
48af28bf71 | ||
|
|
7685deecf6 | ||
|
|
271fecf53a | ||
|
|
9becdfe8f4 | ||
|
|
e00b2eda0b | ||
|
|
cb689fae96 | ||
|
|
0326ad95f6 | ||
|
|
cae03361cf | ||
|
|
b165b0e830 | ||
|
|
4d3a7a0589 | ||
|
|
7479f0a464 | ||
|
|
62d19a7289 | ||
|
|
cbfc2bd9c9 | ||
|
|
661aef55a9 | ||
|
|
1c57eed275 | ||
|
|
dbbaf2abb0 | ||
|
|
b488a10d96 | ||
|
|
45abd4d44e | ||
|
|
02c5e13cee | ||
|
|
5ecd7deea7 | ||
|
|
51612c804f | ||
|
|
c313f33674 | ||
|
|
590bab9ada | ||
|
|
1275817c75 | ||
|
|
b29aa6043f | ||
|
|
209c42c346 | ||
|
|
b4502be51a | ||
|
|
e8374f4e58 | ||
|
|
d56a25e64f | ||
|
|
b5d83d2bff | ||
|
|
80bb0b4f01 | ||
|
|
693ecec082 | ||
|
|
dd55e2c7d5 | ||
|
|
f1a9d85de5 | ||
|
|
2206822109 | ||
|
|
0bbd635ab8 | ||
|
|
15190ffc49 | ||
|
|
254a8e7018 | ||
|
|
bded9c628d | ||
|
|
c8f8ec656c | ||
|
|
dac544be20 | ||
|
|
d0375a1ed0 | ||
|
|
63370e9300 | ||
|
|
d93c70c11b | ||
|
|
c3dcc69c2d | ||
|
|
612bbee947 | ||
|
|
03ae514069 | ||
|
|
4990a0b9f1 | ||
|
|
4a24b7e154 | ||
|
|
5f21aeeeb3 | ||
|
|
88e378063d | ||
|
|
15450643f7 | ||
|
|
0a0f98ca4c | ||
|
|
08e8a568f8 | ||
|
|
87c5967c86 | ||
|
|
756701dfe5 | ||
|
|
25d7135f60 | ||
|
|
9d8ede44d0 | ||
|
|
ca8b6727c3 | ||
|
|
9b656dda29 | ||
|
|
cf5e4f6fa3 | ||
|
|
34552ff0df | ||
|
|
e597e090b0 | ||
|
|
0729aba6d7 | ||
|
|
98c3d2ff93 | ||
|
|
2d06992219 | ||
|
|
b413332bd8 | ||
|
|
0f5ece1d58 | ||
|
|
3e112595da | ||
|
|
324ca9453e | ||
|
|
9bea4d10de | ||
|
|
6b56ba2d63 | ||
|
|
1a38745295 | ||
|
|
21ff4911bf | ||
|
|
e21b131f25 | ||
|
|
965aff805d | ||
|
|
990d1de4d0 | ||
|
|
bd8e1fb2c1 | ||
|
|
b885f44bef | ||
|
|
546b2a6cf6 | ||
|
|
e713c75329 | ||
|
|
9dac74c568 | ||
|
|
c70f5f3327 | ||
|
|
f1dfd10bb0 | ||
|
|
2f6e0506f0 | ||
|
|
5ee5e49cb4 | ||
|
|
c20b707680 | ||
|
|
787726817c | ||
|
|
0211b65a81 | ||
|
|
c394fb105d | ||
|
|
19d4a314f8 | ||
|
|
f49e2eeacc | ||
|
|
65e423eba6 | ||
|
|
9038589e76 | ||
|
|
35aa8ef839 | ||
|
|
a2d2e705d3 | ||
|
|
549f240210 | ||
|
|
9b4deb2655 | ||
|
|
1bc27d2378 | ||
|
|
f8876e5405 | ||
|
|
2721650884 | ||
|
|
2e31d13e07 | ||
|
|
8b93eff452 | ||
|
|
7fba9919a2 | ||
|
|
d8e90fe621 | ||
|
|
fc05f68406 | ||
|
|
adafef2c9e | ||
|
|
f19aebab76 | ||
|
|
0c4bf68ce8 | ||
|
|
39f362dcee | ||
|
|
3602cc851f | ||
|
|
e0da397dee | ||
|
|
6eb9d09268 | ||
|
|
4c776d5918 | ||
|
|
463c4993b4 | ||
|
|
fd08a22742 | ||
|
|
c3e4c116d5 | ||
|
|
97e1b93f13 | ||
|
|
905ae18451 | ||
|
|
44efd920c6 | ||
|
|
ec0259de89 | ||
|
|
9927cd1677 | ||
|
|
f3bb6b1ed0 | ||
|
|
6cfb998af4 | ||
|
|
ebe4e879e8 | ||
|
|
6c4313b93e | ||
|
|
4b2a4e8eae | ||
|
|
1ddb32956a | ||
|
|
bbc75545ec | ||
|
|
5f2a06f1f8 | ||
|
|
0d849b1ae8 | ||
|
|
c85e6d9513 | ||
|
|
f461146403 | ||
|
|
2596667baf | ||
|
|
80bc0f5ede | ||
|
|
0a1ad0d465 | ||
|
|
7298bd08d1 | ||
|
|
115a78fe24 | ||
|
|
1f0eb228e5 | ||
|
|
ba781330d6 | ||
|
|
91f06f55a3 | ||
|
|
2ebaadfa84 | ||
|
|
ad629b2e31 | ||
|
|
b7eededf5a | ||
|
|
bc2d494030 | ||
|
|
22cf9646a7 | ||
|
|
4fb936fe86 | ||
|
|
816a9dc988 | ||
|
|
d3700f709b | ||
|
|
4e2a566d6c | ||
|
|
34563fcc9b | ||
|
|
5eada7e410 | ||
|
|
f91b7f8009 | ||
|
|
1707d171d7 | ||
|
|
5934387f41 | ||
|
|
a0fcc60d89 | ||
|
|
3f9357b599 | ||
|
|
93de7bc7e3 | ||
|
|
99ccfb6e70 | ||
|
|
171b33b2c4 | ||
|
|
e639edf620 | ||
|
|
7784fdbad7 | ||
|
|
4cb1f1d820 | ||
|
|
928ba76882 | ||
|
|
f783f0f1e8 | ||
|
|
af8f0badf6 | ||
|
|
b3c3b21e43 | ||
|
|
5a7bddb3ae | ||
|
|
75daf0c7df | ||
|
|
baf16784af | ||
|
|
f2e5fd148a | ||
|
|
9974512600 | ||
|
|
38ecc0248e | ||
|
|
ee0ad17a1a | ||
|
|
5f13ddd755 | ||
|
|
5c16a85a67 | ||
|
|
b5e43edf7f | ||
|
|
e2eacf6f29 | ||
|
|
ddb3987bb4 | ||
|
|
4345a45982 | ||
|
|
e76f308d26 | ||
|
|
7b77db7eff | ||
|
|
f5b11131b3 | ||
|
|
a35b9335a0 | ||
|
|
8425c36da2 | ||
|
|
f6a1e2c892 | ||
|
|
03077d54af | ||
|
|
9fb8c114a0 | ||
|
|
66671e8748 | ||
|
|
c46064c3f6 | ||
|
|
06032237c6 | ||
|
|
a47788a6a4 | ||
|
|
635f673a8c | ||
|
|
e737e6c1c6 | ||
|
|
75a757d8ef | ||
|
|
25aaf89287 | ||
|
|
3787ec5f83 | ||
|
|
d1fbebb578 | ||
|
|
fba4faf88a | ||
|
|
5cbcd58550 | ||
|
|
1998640669 | ||
|
|
3f49545320 | ||
|
|
049bc2d055 | ||
|
|
ce61680c04 | ||
|
|
dba4178465 | ||
|
|
06fe7eecbc | ||
|
|
23ab2c4069 | ||
|
|
e327baa7b8 | ||
|
|
d3305887cb | ||
|
|
c10a360cab | ||
|
|
5cded74b6d | ||
|
|
597aecfb7d | ||
|
|
75ca7c02ec | ||
|
|
b31f52946f | ||
|
|
2aa8d73714 | ||
|
|
57e3e0b04d | ||
|
|
8e57b7672b | ||
|
|
0f07810f7a | ||
|
|
dab18c1a3f | ||
|
|
8ba739fd3c | ||
|
|
26031bcd26 | ||
|
|
db923eba44 | ||
|
|
c062788b21 | ||
|
|
243790adc9 | ||
|
|
a732bd0656 | ||
|
|
cebd46d15b | ||
|
|
5e8f8b3163 | ||
|
|
5d2c8c2b78 | ||
|
|
1de720e02e | ||
|
|
878da39caf | ||
|
|
80d5c8dc80 | ||
|
|
6911a609f1 | ||
|
|
907f4d99f5 | ||
|
|
6fbe67d66e | ||
|
|
5e4004c413 | ||
|
|
d1fea89e1e | ||
|
|
162b9e5863 | ||
|
|
8ad3e0ea30 | ||
|
|
7665bf0831 | ||
|
|
a8758da09b | ||
|
|
5feac72c79 | ||
|
|
4f3d486d8f | ||
|
|
1e53933786 | ||
|
|
88bb8d5fe3 | ||
|
|
2732d538e8 | ||
|
|
a8276d1289 | ||
|
|
6e324b2244 | ||
|
|
48f700ee35 | ||
|
|
37122745b6 | ||
|
|
aedd394471 | ||
|
|
78f8eca531 | ||
|
|
8f29c7ab66 | ||
|
|
693a3851ec | ||
|
|
357e354ac7 | ||
|
|
f5d791f2a1 | ||
|
|
a0c22ff012 | ||
|
|
edf7b3d32c | ||
|
|
211e3533b3 | ||
|
|
b3c912ab79 | ||
|
|
0c6fed90b7 | ||
|
|
5e4ec0fa1b | ||
|
|
8aa40e9a61 | ||
|
|
f8b65e7872 | ||
|
|
592c46b001 | ||
|
|
a496131e5a | ||
|
|
cee14e1fda | ||
|
|
92f28b2f56 | ||
|
|
ef475fcf8b | ||
|
|
39a91d4db9 | ||
|
|
d137a4cdde | ||
|
|
f8f52f205a | ||
|
|
2443b949a4 | ||
|
|
d0432d63c9 | ||
|
|
f276bcbf6c | ||
|
|
6cd3fa09b1 | ||
|
|
3e7e102a51 | ||
|
|
42e785925c | ||
|
|
d4415444c4 | ||
|
|
6fdcac6cad | ||
|
|
ccba355fa9 | ||
|
|
d78892eb4b | ||
|
|
33f90f95b6 | ||
|
|
a32d731ed7 | ||
|
|
8d8d85ba22 | ||
|
|
4526862728 | ||
|
|
6c6e2ad62b | ||
|
|
929a0a76c8 | ||
|
|
4c5fae5179 | ||
|
|
fc63fe8682 | ||
|
|
96ea32b7e4 | ||
|
|
b784912811 | ||
|
|
87fa1b613d | ||
|
|
ddd7ac7b84 | ||
|
|
f719dc46c5 | ||
|
|
9a3780229b | ||
|
|
678c4cfa68 | ||
|
|
866c72c907 | ||
|
|
7ba05210fe | ||
|
|
51e5938776 | ||
|
+12 |
169512a35b | ||
|
|
8bf882f401 | ||
|
|
9066444f6b | ||
|
|
7fbc2b5652 | ||
|
|
ecd997e512 | ||
|
|
c409a8bbe8 | ||
|
|
17cd07342d | ||
|
|
52b0a54afd | ||
|
|
7fd722e15b | ||
|
|
567b559474 | ||
|
|
607f509505 | ||
|
|
40c235c642 | ||
|
|
a06dbd509d | ||
|
|
3d24683b06 | ||
|
|
b3995a2ae5 | ||
|
|
5af154fc4b | ||
|
|
7f613149be | ||
|
|
50af8de5e8 | ||
|
|
e2efd10b70 | ||
|
|
6a34be416b | ||
|
|
b33f68deb7 | ||
|
|
60accad2de | ||
|
|
c343b787f0 | ||
|
|
6ba6aea5eb | ||
|
|
bd4f5e9c19 | ||
|
|
a718037033 | ||
|
|
1b3d57ca8a | ||
|
|
fd782cf2b1 | ||
|
|
b5dba08dba | ||
|
|
89fea8130a | ||
|
|
e8dad898da | ||
|
|
8f91e60558 | ||
|
|
c9204b96d4 | ||
|
|
f290b27299 | ||
|
|
eab61d02a1 | ||
|
|
6c1b7f86d1 | ||
|
|
98ca6c8270 | ||
|
|
99d2d7ad22 | ||
|
|
a68cfd7db0 | ||
|
|
5f702dd6cc | ||
|
|
4193bc5bf8 | ||
|
|
610966b836 | ||
|
|
2bcd7e6d13 | ||
|
|
7fd6e486ae | ||
|
|
9b598076e3 | ||
|
|
e966b9f9a5 | ||
|
|
1a430f055d | ||
|
|
529038f557 | ||
|
|
66e25e7912 | ||
|
|
ae013ad23c | ||
|
|
0f04043a5a | ||
|
|
8cd10d4727 | ||
|
|
7a93cfd118 | ||
|
|
6cb0684372 | ||
|
|
f09dde0cc9 | ||
|
|
fabb68bb9b | ||
|
|
97f6678578 | ||
|
|
72270ac275 | ||
|
|
f40bd0c4fb | ||
|
|
4f0c33e8c4 | ||
|
|
5e16e30c6b | ||
|
|
b305104e4a | ||
|
|
a8b8ff2acf | ||
|
|
5bcf71c660 | ||
|
|
c4a59101ac | ||
|
|
045c7dd63a | ||
|
|
131c1f1219 | ||
|
|
8520d425d0 | ||
|
|
f6230e2f9d | ||
|
|
43e7917ec2 | ||
|
|
869d2bc2ec | ||
|
|
78ce91f1c2 | ||
|
|
4bad48d873 | ||
|
|
857a75baac | ||
|
|
992e930b86 | ||
|
|
47900f99c5 | ||
|
|
bdf13a63c4 | ||
|
|
d31e42699f | ||
|
|
d2739960af | ||
|
|
386359060b | ||
|
|
aa52ff6f4b | ||
|
|
eed99a588e | ||
|
|
02940796f1 | ||
|
|
bc411c24be | ||
|
|
76b56957e9 | ||
|
|
3c5c887a2c | ||
|
|
5d6b8a0ab6 | ||
|
|
8c4f96ef47 | ||
|
|
a7f967cf24 | ||
|
|
23e68cbdfb | ||
|
|
aa357d6d91 | ||
|
|
22df16186f | ||
|
|
5d0d243a45 | ||
|
|
1280e7e513 | ||
|
|
5c7755636d | ||
|
|
d20c7b09a6 | ||
|
|
139fd52477 | ||
|
|
72dd48a44c | ||
|
|
9200adea61 | ||
|
|
16e64d58ff | ||
|
|
8f9b2ab948 | ||
|
|
42086fdba7 | ||
|
|
8695ca8257 | ||
|
|
b443b5a89b | ||
|
|
79b3f4694d | ||
|
|
0e3e577568 | ||
|
|
5bb01ca9a3 | ||
|
|
86a0428d5f | ||
|
|
99a5ffae3b | ||
|
|
5a5bcb6b54 | ||
|
|
749ae11b15 | ||
|
|
007f0d214d | ||
|
|
e90782283c | ||
|
|
486111725e | ||
|
|
40717c931b | ||
|
|
142cf188a7 | ||
|
|
c70eefad50 | ||
|
|
74c23005c0 | ||
|
|
9c4dd7eb23 | ||
|
|
1ece9630e6 | ||
|
|
4524bbd8df | ||
|
|
d97db6ebaf | ||
|
|
25aa298c61 | ||
|
|
3a2074b633 | ||
|
|
ef05713d86 | ||
|
|
48cd33af18 | ||
|
|
80d3fd40f1 | ||
|
|
55185abec8 | ||
|
|
d0a8e3c60d | ||
|
|
0d44afed7e | ||
|
|
520f8e8042 | ||
|
|
19520d0008 | ||
|
|
23def04096 | ||
|
|
cc8835c7a3 | ||
|
|
3784a8e547 | ||
|
|
c6ab965062 | ||
|
|
667402846c | ||
|
|
f921cb943e | ||
|
|
a281455f0b | ||
|
|
73d85f6299 | ||
|
|
0473a3091c | ||
|
|
3ed9f4b7e4 | ||
|
|
00a9e95de9 | ||
|
|
0205f870db | ||
|
|
6442329717 | ||
|
|
f250c8cca5 | ||
|
|
2dfcc6463b | ||
|
|
d0e93fa34b | ||
|
|
401d72997b | ||
|
|
cde858d546 | ||
|
|
ba7c3dafab | ||
|
|
553262443a | ||
|
|
98a2322998 | ||
|
|
abbb1d7135 | ||
|
|
379f37307a | ||
|
|
ce48f43550 | ||
|
|
ea579ebfae | ||
|
|
c74bc2108c | ||
|
|
65168f77fb | ||
|
|
18225b9bd6 | ||
|
|
54c666a5ae | ||
|
|
275095a9c6 | ||
|
|
b6833b9f61 | ||
|
|
cc99d896be | ||
|
|
630e6becdf | ||
|
|
99be47a9a3 | ||
|
|
572d0c2340 | ||
|
|
19d975751f | ||
|
|
21b836a327 | ||
|
|
fe0325c6d1 | ||
|
|
e6537df806 | ||
|
|
7af6fa01f5 | ||
|
|
aee249b1dd | ||
|
|
81de82a1a4 | ||
|
|
d53584a10a | ||
|
|
a4bf6f83a6 | ||
|
|
50241b368b | ||
|
|
3ba1942631 | ||
|
|
642f6571ea | ||
|
|
446b284239 | ||
|
|
0154141304 | ||
|
|
fa56001f18 | ||
|
|
667f73aa54 | ||
|
|
f28c0fdfb0 | ||
|
|
0c99194b8c | ||
|
|
e36df2c8de | ||
|
|
461541348e | ||
|
|
45a411e8f8 | ||
|
|
60981e0801 | ||
|
|
012959e3cc | ||
|
|
46111dd091 | ||
|
|
39d1b2d395 | ||
|
|
06828b2e65 | ||
|
|
be85386e27 | ||
|
|
933aa480d3 | ||
|
|
f8f4a5c3b8 | ||
|
|
56685c6ec6 | ||
|
|
6bb4f4bf7d | ||
|
|
b58aa63796 | ||
|
|
e44899d510 | ||
|
|
860b3dd6b0 | ||
|
|
e44ff4666a | ||
|
|
bcd1f95e68 | ||
|
|
14e745950e | ||
|
|
6c4d880f2d | ||
|
|
524b84792f | ||
|
|
1635eadfcc | ||
|
|
5f4e39fea2 | ||
|
|
02c8532901 | ||
|
|
2944afa5b8 | ||
|
|
28a33cae8f | ||
|
|
49c9c9d4a1 | ||
|
|
e073f64be0 | ||
|
|
4c547008c5 | ||
|
|
592c3e2793 | ||
|
|
5d81363391 | ||
|
|
37e92e430b | ||
|
|
4cfc61147b | ||
|
|
6d6cbdf8c4 | ||
|
|
c106cf20cb | ||
|
|
b395190e42 | ||
|
|
b6a0f32b14 | ||
|
|
0407bbfbe6 | ||
|
|
6dff37594c | ||
|
|
7f9a03d6ab | ||
|
|
4f8465d8db | ||
|
|
3e85da9195 | ||
|
|
c8d58d2e7c | ||
|
|
8b1e3143a2 | ||
|
|
e32a0494d4 | ||
|
|
f850b7c894 | ||
|
|
9eb829aca3 | ||
|
|
71a024c789 | ||
|
|
f4b2ba1313 | ||
|
|
ed069bb9d4 | ||
|
|
da9a580a0d | ||
|
|
74ed67da7f | ||
|
|
b7f8bbb49f | ||
|
|
6b7aa124ed | ||
|
|
0281d10130 | ||
|
|
0b17cb621d | ||
|
|
db2a3174f6 | ||
|
|
47353e494d | ||
|
|
0f09630645 | ||
|
|
371f3d91ae | ||
|
|
8b478d0f15 | ||
|
|
15ec8e98db | ||
|
|
c2fe0c6818 | ||
|
|
b5442df02d | ||
|
|
81b94cebec | ||
|
|
c4b7a2e003 | ||
|
|
7af1b20686 | ||
|
|
2775135254 | ||
|
|
6d73515bf1 | ||
|
|
0344d578b2 | ||
|
|
2a3e162246 | ||
|
|
3984d12a30 | ||
|
|
ee0ec027db | ||
|
|
b1ff86832c | ||
|
|
e83e3ceadd | ||
|
|
bd2a84ccba | ||
|
|
6b8f4f7528 | ||
|
|
146dcf46f5 | ||
|
|
1d5357aaad | ||
|
|
861e17a3a7 | ||
|
|
5a311d8afe | ||
|
|
7695466081 | ||
|
|
c717c8a3ec | ||
|
|
37cbeff9f3 | ||
|
|
0d596fa8f7 | ||
|
|
42e1a17d6e | ||
|
|
3afec59523 | ||
|
|
bea6b98e84 | ||
|
|
46217c69bc | ||
|
|
3e87889bfb | ||
|
|
746a41937d | ||
|
|
93a384db77 | ||
|
|
7091be291c | ||
|
|
3571002422 | ||
|
|
86655eac66 | ||
|
|
536f5ce38b | ||
|
|
b6e6d7cd1a | ||
|
|
6714fb1d02 | ||
|
|
92a9591a41 | ||
|
|
2425493e10 | ||
|
|
c592ea6bac | ||
|
|
740f107ff9 | ||
|
|
f33b7bd617 | ||
|
|
4a90059fe2 | ||
|
|
aa521379dd | ||
|
|
5d3762456f | ||
|
|
1f9d1b5de1 | ||
|
|
02e2c9cd7f | ||
|
|
3f35eacad2 | ||
|
|
1ba178befb | ||
|
|
02a0e72f70 | ||
|
|
b4bc6cf3ee | ||
|
|
1684c73d4f | ||
|
|
b5c47f5255 | ||
|
|
7e8ec0e456 | ||
|
|
7436ac8e20 | ||
|
|
de00fdc17d | ||
|
|
75575f2006 | ||
|
|
db8556e724 | ||
|
|
e3a00bb5f9 | ||
|
|
af42f856ec | ||
|
|
809f3b7b5b | ||
|
|
9a21cd78ce | ||
|
|
be4498d394 | ||
|
|
5d3ae66e63 | ||
|
|
26573c3aa4 | ||
|
|
b2048d46d1 | ||
|
|
8ecbb74b00 | ||
|
|
9c81191b1d | ||
|
|
220414682e | ||
|
|
d11fca46fc | ||
|
|
5c8d5b2a8a | ||
|
|
87657c6297 | ||
|
|
af73951405 | ||
|
|
ab2119e9b8 | ||
|
|
6da3839b57 | ||
|
|
54ea293e7c | ||
|
|
25a0f3c3b8 | ||
|
|
6b55802df6 | ||
|
|
4b4babf1e7 | ||
|
|
26bc77ce56 | ||
|
|
7aee876d81 | ||
|
|
49c804b7ec | ||
|
|
295e7efddf | ||
|
|
9a426033ff | ||
|
|
6dc0f60faa | ||
|
|
44a4c1ba87 | ||
|
|
0d5c7b6d79 | ||
|
|
77c72b96aa | ||
|
|
510838bd70 | ||
|
|
49bb6c84b0 | ||
|
|
5fbea61e5a | ||
|
|
791f6328b0 | ||
|
|
e28c11f87d | ||
|
|
b9ca37b51f | ||
|
|
09416de0cd | ||
|
|
888b2afc0d | ||
|
|
106779208d | ||
|
|
7501911f17 | ||
|
|
a6b9312577 | ||
|
|
b43daa166f | ||
|
|
fd5cd1b058 | ||
|
|
304c978368 | ||
|
|
54f51f0d7d | ||
|
|
566a9a1551 | ||
|
|
957bcfa149 | ||
|
|
71dc6fe5bd | ||
|
|
5bda0a7dc3 | ||
|
|
6d977db1b5 | ||
|
|
7ec9b6e56e | ||
|
|
2127095e3b | ||
|
|
584937c761 | ||
|
|
306ed79932 | ||
|
|
5375ff0f9a | ||
|
|
9512a80804 | ||
|
|
3d472bde23 | ||
|
|
8761d4251f | ||
|
|
2846cc0570 | ||
|
|
30fef57f75 | ||
|
|
5d7d6ad9ab | ||
|
|
2589c0074d | ||
|
|
e780148305 | ||
|
|
edccb430c4 | ||
|
|
293832bafc | ||
|
|
e93e3b1e7d | ||
|
|
2c3dd946d5 | ||
|
|
4043e71f6c | ||
|
|
932fbb7d84 | ||
|
|
a4a548718d | ||
|
|
3a42a9e356 | ||
|
|
46cc5ce056 | ||
|
|
3b2d2863da | ||
|
|
aff5e23ecf | ||
|
|
2d91e2d578 | ||
|
|
690a63abd6 | ||
|
|
117ded88f0 | ||
|
|
bfba99827e | ||
|
|
44827a5020 | ||
|
|
055d1a9e1b | ||
|
|
ca4ca65c5e | ||
|
|
31dc035041 | ||
|
|
21f9af9d93 | ||
|
|
c7efd3a388 | ||
|
|
66aacc6fba | ||
|
|
79dea89f1f | ||
|
|
7d0efef62d | ||
|
|
933af60eba | ||
|
|
04fd41b288 | ||
|
|
002e1b68c6 | ||
|
|
73b756430f | ||
|
|
2452efa921 | ||
|
|
b6007e0a58 | ||
|
|
c9b4cae9dc | ||
|
|
86357bad54 | ||
|
|
1639189f26 | ||
|
|
c083fedaf4 | ||
|
|
c02945bc44 |
No files matched your search
@@ -2,13 +2,13 @@
|
||||
name: reverse-engineer-anything
|
||||
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
|
||||
metadata:
|
||||
version: "44"
|
||||
version: "46"
|
||||
---
|
||||
|
||||
# REA
|
||||
|
||||
Use REA when a claim depends on a shipped binary or package, decompilation,
|
||||
passive application runtime evidence, or comparison with
|
||||
requested runtime evidence, or comparison with
|
||||
behavior not established by available source. For ordinary analysis of a
|
||||
complete source repository, use normal repository tools and do not run REA
|
||||
readiness or provider commands.
|
||||
@@ -32,6 +32,15 @@ original task after repair. Client approval, shell PATH, registration and engine
|
||||
availability have different remedies; avoid repeating setup for all of them.
|
||||
If the host times out without a REA result, do not repeat the unchanged call or
|
||||
assume work stopped. Read the recovery guide's deadline distinction before retrying.
|
||||
If the MCP connection drops, retry that call once only when it is safe to
|
||||
repeat or known not to have started. If it may already have changed the
|
||||
target, or it fails again, tell the user and ask them to restart the MCP
|
||||
client. If REA could not start its connection, retry once, then tell the user
|
||||
and ask them to restart the MCP client. Do not retry a startup failure that
|
||||
returns the same unchanged result. Run `rea doctor` when REA could not start
|
||||
or tools stay missing after that restart. For `cleanup_incomplete`, a retryable
|
||||
capacity limit, or `configuration_invalid`, follow the recovery guide's ladder
|
||||
before asking for a restart. Read that guide before a second repair attempt.
|
||||
|
||||
1. Diagnose without changing REA or client configuration. `npx` may download
|
||||
the package and write to npm's cache. For Codex, run
|
||||
@@ -62,9 +71,8 @@ assume work stopped. Read the recovery guide's deadline distinction before retry
|
||||
|
||||
REA setup never installs or upgrades Node.js, npm, Homebrew, Java, Ghidra, IDA,
|
||||
JADX, Kuna, Binwalk, or Unblob. Use existing prerequisites; do not install unrelated
|
||||
software to repair
|
||||
MCP registration. For an unsupported client, use manual stdio registration
|
||||
with a version-pinned `rea-agents` package or continue through the CLI.
|
||||
software to repair MCP registration. For an unsupported client, use manual stdio
|
||||
registration with a version-pinned `rea-agents` package or continue through the CLI.
|
||||
|
||||
A concrete CLI fallback for an operator-supplied JavaScript tree or ASAR is:
|
||||
|
||||
@@ -80,8 +88,7 @@ Prefer an existing `rea` CLI. When none is installed and REA package acquisition
|
||||
is within the authorized scope, use `npx -y rea-agents@latest` in its place.
|
||||
Configuration repair starts with a read-only setup plan; show paths, backups and
|
||||
skill replacement before applying authorized changes. Preserve existing approval
|
||||
and target-execution boundaries. REA setup never installs or upgrades unrelated
|
||||
software, including Node.js, npm, Homebrew, Java, Ghidra, IDA, JADX, Kuna, Binwalk or Unblob.
|
||||
and target-execution boundaries.
|
||||
|
||||
## Route the target first
|
||||
|
||||
@@ -115,6 +122,12 @@ explicit path or endpoint and do not need it.
|
||||
- .NET NativeAOT PE/ELF: native Ghidra analysis. Native code can yield pseudocode;
|
||||
consult the NativeAOT workflow in `references/native-and-artifacts.md` for
|
||||
optional metadata recovery and its supported host/layout boundary.
|
||||
- Native program behavior, live calls, instrumentation, or command-run effects:
|
||||
choose the matching [native runtime workflow](references/runtime-observation.md#native-runtime-questions).
|
||||
`open_binary` and `trace_native_values` establish static facts; they do not
|
||||
observe execution. Runtime tools have distinct launch, attach, mutation and
|
||||
cleanup effects, and do not require a preliminary static session unless their
|
||||
own contract says so.
|
||||
- User-owned browser page already open: `list_browser_targets`.
|
||||
- Browser action the agent must perform (click, type, navigate):
|
||||
`capture_browser_scenario` with declared actions. See
|
||||
@@ -126,7 +139,10 @@ explicit path or endpoint and do not need it.
|
||||
connect Everywhere's separate MCP server for live traffic.
|
||||
- Retained HAR/SAZ/native mitmproxy/aioquic qlog capture: `inspect_web_network_capture`. Select
|
||||
original record ordinals when useful; inspect producer fields and byte/number
|
||||
sidecars without fetching recorded URLs or claiming live attribution. See
|
||||
sidecars without fetching recorded URLs or claiming live attribution. HAR/SAZ/qlog
|
||||
old-generation heap is `REA_CAPTURE_DECODER_OLD_GENERATION_MIB` (default 192 MiB,
|
||||
floor 8, maximum 16384), set where REA starts. Host memory and `NODE_OPTIONS` do not
|
||||
raise it. See
|
||||
[historical captures](https://github.com/morluto/rea/blob/main/docs/web-network-captures.md) for the exact upstream
|
||||
profile and credential exclusions. Qlog support starts with aioquic 1.5.0
|
||||
JSON 0.3; stream coverage and endpoint loss declarations do not recover bodies
|
||||
@@ -166,6 +182,14 @@ explicit path or endpoint and do not need it.
|
||||
Select JavaScript glue and candidate paths explicitly and preserve URL/path
|
||||
ambiguity. No implicit fetch or target execution; WAT is not original source.
|
||||
See the [WASM guide](https://github.com/morluto/rea/blob/main/docs/wasm-artifacts.md).
|
||||
- Explicit local Android APK (or ZIP/AAB) signing: `inspect_apk_signature`.
|
||||
Prefer its summary or selected signer/digest/lineage page; retain complete
|
||||
Evidence and use `inspect_analysis_view` kind `apk-signature` for follow-ups.
|
||||
Reports which schemes are present (v1 JAR, v2, v3, v3.1), each signer's
|
||||
certificate SHA-256 fingerprint (v1 included, from its PKCS#7 block), v3 key-rotation lineage, source stamp, and
|
||||
whether each signed digest still matches the bytes (self-consistency). This
|
||||
is identity and integrity, not a trust or authenticity verdict.
|
||||
See the [APK signature guide](https://github.com/morluto/rea/blob/main/docs/apk-signature.md).
|
||||
- Explicit local EVM bytecode carrier: `inspect_evm_interface` with caller-selected
|
||||
`raw` or `hex` encoding. The owned worker deadline is `timeout_ms` (default
|
||||
30000 ms, range 1..2147483647, next call). It does not raise the fixed byte,
|
||||
@@ -219,12 +243,51 @@ explicit path or endpoint and do not need it.
|
||||
delivery requires `--evidence_output`.
|
||||
See the
|
||||
[Rust binary guide](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
|
||||
- PE Authenticode certificate table: `inspect_pe_certificates` with the explicit
|
||||
local PE32/PE32+ file. This built-in target-free inspection requires no
|
||||
Windows SDK. Preserve WIN_CERTIFICATE ranges, types, payload SHA-256, and
|
||||
PKCS#7 signer/X.509 identity fields, including `subject_key_identifier_hex`
|
||||
when the signer uses that form. A missing security directory is observed
|
||||
absence of that table, not proof the image is unsigned in every catalog sense.
|
||||
Signatures are not verified. See the
|
||||
[PE certificate guide](https://github.com/morluto/rea/blob/main/docs/pe-certificates.md).
|
||||
- PE import and delay-load tables: `inspect_pe_imports` with the explicit
|
||||
local PE32/PE32+ file. This built-in target-free inspection requires no
|
||||
Windows SDK. Preserve DLL names, named and ordinal functions, ILT/IAT RVAs,
|
||||
delay-load descriptors, and the pefile import hash of the standard import
|
||||
table. A missing import directory is observed absence of that table, not
|
||||
proof the image never imported APIs through another table. Bound snapshots
|
||||
and loaded IAT contents remain unobserved. See the
|
||||
[PE import guide](https://github.com/morluto/rea/blob/main/docs/pe-imports.md).
|
||||
- ELF note tables: `inspect_elf_notes` with the explicit local ELF32/ELF64
|
||||
file. This built-in target-free inspection requires no disassembler. Preserve
|
||||
PT_NOTE and SHT_NOTE ranges, owners, types, descriptor SHA-256, and GNU
|
||||
build-id or ABI-tag fields. A missing table is observed absence of those
|
||||
declarations, not proof the image never carried notes. See the
|
||||
[ELF notes guide](https://github.com/morluto/rea/blob/main/docs/elf-notes.md).
|
||||
- Apple `.ips` crash reports: `inspect_apple_ips` with the explicit local
|
||||
file. This built-in target-free inspection requires no Xcode, atos, or
|
||||
pwntools. Preserve first-line metadata, `bug_type` as a string, process and
|
||||
exception facts, lossless integer registers, usedImages UUIDs, and
|
||||
documented classification matches. Only `bug_type` `"309"` yields a crash
|
||||
body; other types, including `"288"` stackshots, return no crash result.
|
||||
Opened Mach-O and dSYM UUID joins remain unobserved. See the
|
||||
[Apple IPS guide](https://github.com/morluto/rea/blob/main/docs/apple-ips.md).
|
||||
- CPython 3.7–3.13 `.pyc` header, nested marshal code objects and bytecode
|
||||
identity: `inspect_python_pyc` with the explicit local file. This built-in
|
||||
target-free inspection requires no Python installation. Preserve magic,
|
||||
PEP 552 hash or mtime/size, nested names, and bytecode SHA-256. Bytecode is
|
||||
not decompiled, and a decoded header does not prove the matching `.py` is
|
||||
present. See the
|
||||
[CPython pyc guide](https://github.com/morluto/rea/blob/main/docs/python-pyc.md).
|
||||
- Erlang BEAM IFF container: `inspect_erlang_beam` with the explicit local
|
||||
`.beam` file. This built-in target-free inspection requires no Erlang
|
||||
installation. Preserve chunk names, atom order, export/import/lambda tables,
|
||||
and the Code header. For selected facts, prefer `detail: "summary"` plus a
|
||||
`beam` view (collection, names/indices, fields and paging), retaining the full
|
||||
Evidence. CLI summary requires `--evidence_output`. Missing table counts stay
|
||||
null; decoded empty tables count zero. Bytecode is not executed. See the
|
||||
[Erlang BEAM guide](https://github.com/morluto/rea/blob/main/docs/erlang-beam.md).
|
||||
|
||||
- Explicit local SQLite database snapshot: `inspect_sqlite_database`. Inspect
|
||||
with Node.js 24.x >=24.15 or 26+ providing native SQLite limits. Read schema
|
||||
@@ -247,6 +310,8 @@ explicit path or endpoint and do not need it.
|
||||
- PE resource type/name/language trees and static icon group references:
|
||||
`inspect_pe_resources` with an explicit path. This portable operation requires
|
||||
no active target or engine; opaque payloads retain file ranges and SHA-256.
|
||||
- PE export directories and ELF dynamic-symbol exports: `inspect_native_exports`
|
||||
with an explicit path; portable, no active target or engine.
|
||||
- Native executable, library, or analysis database: `open_binary`, then
|
||||
use focused analysis tools directly; call `binary_overview` when metadata or
|
||||
inventory context is useful and available from the selected provider.
|
||||
@@ -307,7 +372,13 @@ Start with the default result and use its inline Evidence and graph context.
|
||||
For a JavaScript application or ELF layout whose size is unknown or large,
|
||||
call `analyze_javascript_application` or `inspect_binary_layout` with
|
||||
`"detail": "summary"`, then inspect the retained `parent_evidence_id` with
|
||||
`inspect_analysis_view`.
|
||||
`inspect_analysis_view`. Over MCP, `analyze_function` is summary-first without
|
||||
a flag: its default reply is the summary, and the complete dossier stays
|
||||
retained under `parent_evidence_id` for facet paging. Pass that ID as a
|
||||
`retained-evidence` reference to `compare_functions`, or inside
|
||||
`match_native_functions` `left_observations`/`right_observations`; request
|
||||
`"detail": "complete"` only when the dossier must travel inline, as
|
||||
`build_call_path` requires.
|
||||
Page module identities and select the returned `node_id` before requesting an
|
||||
item; a single file path can represent several graph roles. Do not repeat an
|
||||
identical analysis or rejected call without a changed input or resolved blocker.
|
||||
@@ -395,8 +466,8 @@ as complete while required questions remain open.
|
||||
[references/javascript-applications.md](references/javascript-applications.md)
|
||||
- Android APK declarations, classes, methods, and static references:
|
||||
[references/android-applications.md](references/android-applications.md)
|
||||
- Browser/Electron observation (passive and scenario) and static/runtime
|
||||
reconciliation:
|
||||
- Native instrumentation, debugger and process capture; browser/Electron
|
||||
observation; static/runtime comparison:
|
||||
[references/runtime-observation.md](references/runtime-observation.md)
|
||||
- Evidence paging, comparisons, residual unknowns, and verification:
|
||||
[references/evidence-workflows.md](references/evidence-workflows.md)
|
||||
|
||||
@@ -15,9 +15,11 @@ instead of guessing.
|
||||
|
||||
The provider requires a separately supplied headless JADX MCP JAR 0.7.1 and
|
||||
Java 17 or newer. `REA_JADX_MCP_JAR` selects the absolute JAR path; `JAVA_HOME`
|
||||
can select Java. REA setup does not install or configure that JAR, and doctor
|
||||
provider scopes cover Hopper/Ghidra rather than JADX. Use existing tools and
|
||||
report missing prerequisites for the Android task specifically.
|
||||
can select Java. Use `rea doctor --provider jadx --json` to check the JADX
|
||||
configuration and obtain remediation for missing prerequisites. Java and the
|
||||
headless JADX MCP JAR remain caller-supplied; REA setup does not install or
|
||||
configure them. Configuration readiness does not establish successful analysis
|
||||
of a particular APK.
|
||||
|
||||
Linux and macOS use the POSIX owned-process boundary. Windows x64 requires the
|
||||
matching bundled native controls and uses an owned Job Object with bidirectional
|
||||
|
||||
@@ -7,18 +7,23 @@ operation and the next useful step; after repair, continue the investigation.
|
||||
Commands below use an installed `rea`. If none is available, use the package
|
||||
invocation in `SKILL.md` when acquiring REA is authorized. Follow the connected
|
||||
server's schemas and the installed CLI's help rather than assuming repository
|
||||
main and an older release expose the same arguments.
|
||||
main and an older release expose the same arguments. Tool-name prefixes belong
|
||||
to the client: find the canonical REA operation in its actual tool listing and
|
||||
server association before concluding that it is absent. A shared prefix alone
|
||||
does not establish which server an approval wildcard covers. See
|
||||
[client tool names and discovery](https://github.com/morluto/rea/blob/main/docs/client-configuration.md#client-tool-names-and-discovery).
|
||||
|
||||
## Choose the remedy from the observed failure
|
||||
|
||||
| Observation | Meaning and next step |
|
||||
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| No REA tools in the active session | Inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
|
||||
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
|
||||
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
|
||||
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
|
||||
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. |
|
||||
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
|
||||
| Observation | Meaning and next step |
|
||||
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| No REA tools in the active session | Inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
|
||||
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
|
||||
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
|
||||
| MCP connection dropped or REA could not start | If the connection drops, retry that call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or the same failure returns, tell the user REA's connection failed and ask them to restart the MCP client. If REA could not start its connection, retry once, then tell the user and ask them to restart the MCP client. Do not retry a startup failure that returns the same unchanged result. Run `rea doctor` when REA could not start or tools stay missing after restart. Doctor cannot restore a live connection by itself. |
|
||||
| `MCP tool call requires approval, but approval policy is never` | Codex rejected the call before REA executed it. Do not install an engine or repeat setup to solve this client-policy decision. See the approval section below. |
|
||||
| REA returns `provider_unavailable` or an unavailable tool entry | Read that operation's provider diagnostics or `binary_session.result.tool_availability`. Expand each group's tool names and inherit the shared client requirements unless overridden. Use the operation's availability, reason, and remediation; a complete tool catalog does not imply every operation is available. |
|
||||
| `rea` is missing in a child shell, or Node is incompatible | Inspect the actual shell PATH and launcher/runtime. A prefix installation may be absent from a login shell's PATH; this does not establish a broken MCP registration. |
|
||||
|
||||
If the client cannot launch its shell/sandbox helper or read the installed skill,
|
||||
inspect its launch diagnostic. For example, Codex can refuse helper aliases when
|
||||
@@ -56,8 +61,13 @@ authorized CLI fallback. Do not silently broaden permissions or switch targets.
|
||||
|
||||
A returned REA `provider_timeout` instead means the **server/provider** reached
|
||||
its deadline. Follow its `remediation.action`, reuse accessible partial Evidence,
|
||||
and resolve reported incomplete cleanup before retrying. Raising only the
|
||||
client deadline does not change this server budget. For static JavaScript,
|
||||
and resolve reported incomplete cleanup before retrying. Change a request
|
||||
field or a `REA_*` setting only when the returned remediation explicitly
|
||||
recommends that change. Restart REA when that text says the value is read at
|
||||
startup. A mention that a control does not raise the deadline is not a
|
||||
recommendation to change it. Do not invent a setting the remediation does not
|
||||
name. Raising only the client deadline does not change
|
||||
this server budget. For static JavaScript,
|
||||
`analysis_timeout_ms` (CLI `rea analyze-javascript-application <path>
|
||||
--analysis-timeout-ms <milliseconds>`) controls each source's
|
||||
startup, analysis and transfer deadline in **milliseconds**. Increase that
|
||||
@@ -75,6 +85,37 @@ installed CLI's command help. Refresh the catalog if stale; do not guess fields
|
||||
from an older skill or return the entire catalog to repair one field. Correct
|
||||
all reported fields before retrying.
|
||||
|
||||
## Recovery ladder
|
||||
|
||||
Follow the returned remediation. Change a request field or a `REA_*` setting
|
||||
only when that text explicitly recommends the change. Restart REA when it says
|
||||
the value is read at startup. A mention that a control does not raise a limit
|
||||
is not a recommendation to change it. Do not invent a setting. Host memory and
|
||||
`NODE_OPTIONS` do not raise a REA admission unless that remediation says so.
|
||||
|
||||
- MCP connection dropped: retry that call once only when it is safe to repeat
|
||||
or known not to have started. If it may already have changed the target, or
|
||||
it fails again, tell the user REA's connection failed and ask them to restart
|
||||
the MCP client. Do not run `rea doctor` for the drop itself.
|
||||
- REA could not start its connection: retry once, then tell the user and ask
|
||||
them to restart the MCP client. Run `rea doctor` when REA could not start or
|
||||
tools stay missing after that restart. Do not retry a startup failure that
|
||||
returns the same unchanged result.
|
||||
- `cleanup_incomplete`, or retained cleanup owners in the error details: retry
|
||||
that exact cleanup owner. Do not start new work that needs those resources
|
||||
until that owner confirms release. Repeating the original call is not that
|
||||
retry.
|
||||
- Capacity, when the result is `resource_constraint` and `retryable` is true:
|
||||
wait for the active admission to finish, then retry within the reported
|
||||
limits. Do not restart REA to free the slot.
|
||||
- `configuration_invalid`: correct the named setting in the environment that
|
||||
starts REA (the shell or the MCP registration), then restart REA. Run
|
||||
`rea doctor` to see the `runtime-configuration:` check. Re-run `rea setup`
|
||||
for that client when the registration must carry the new setting, then
|
||||
reconnect.
|
||||
- `invalid_request`: correct the reported fields from the current schema. Do
|
||||
not repeat the unchanged call.
|
||||
|
||||
## Repair registration when needed
|
||||
|
||||
For Codex:
|
||||
|
||||
@@ -48,7 +48,9 @@ or absence:
|
||||
- `compare_artifacts` compares inventory pages by occurrence path, content,
|
||||
metadata, and graph relations.
|
||||
- `compare_functions` compares explicit function Evidence, not fuzzy
|
||||
whole-binary matches.
|
||||
whole-binary matches. It rejects an `analyze_function` summary reply; pass
|
||||
the summary's `parent_evidence_id` as a `retained-evidence` reference or
|
||||
the complete dossier.
|
||||
- `compare_bundles` compares canonical bundle membership and unknown history.
|
||||
- `find_changed_behavior` combines existing comparisons; static differences
|
||||
remain candidates, not causal proof.
|
||||
@@ -104,3 +106,14 @@ each plan states why in its `reason`. It replays the capture instead with
|
||||
`--replay-only` or when the state is unavailable (not derivable, or a catalog
|
||||
over the discovery budget), and its stopped document's `state` says which
|
||||
applied.
|
||||
|
||||
For a PE/ELF export question, `inspect_native_exports` accepts `detail: "summary"`
|
||||
and retains the complete table. Query `inspect_analysis_view` with
|
||||
`view: {kind: "native-exports", collection: "exports", names: ["selected_name"],
|
||||
fields: ["name"], offset: 0, limit: 32}` and the reported parent Evidence ID.
|
||||
All same-name versions and aliases survive; use `source_index` with the parent ID
|
||||
for stable identity. `collection: "issues"` pages interpretation failures. Page
|
||||
coverage describes selected retained rows; `summary.coverage` or `exports.coverage`
|
||||
still describes the source table, so an empty partial page does not prove absence.
|
||||
CLI `inspect-native-exports --detail summary --evidence_output PATH` saves the
|
||||
complete bundle for later `inspect-analysis-view --evidence-bundle PATH` requests.
|
||||
@@ -17,9 +17,10 @@ the complete artifact inventory and identity but reads source text and analyzes
|
||||
only selected files. The result and summary record `source_selection`; files
|
||||
outside scope and their relationships remain unknown, so partial application
|
||||
coverage does not mean the selected source failed. Use repeated `--source-path`
|
||||
on either static application CLI route. Omit the selection when the question
|
||||
requires complete application analysis; `source_paths: []` is invalid, not an
|
||||
unfiltered request. `detail: "summary"` reduces delivery
|
||||
on either static application CLI route, or `--source-paths-json` with a JSON
|
||||
file for selections too long for one command line. Omit the selection when the
|
||||
question requires complete application analysis; `source_paths: []` is invalid,
|
||||
not an unfiltered request. `detail: "summary"` reduces delivery
|
||||
and does not by itself reduce analysis work.
|
||||
|
||||
ASAR header/index admission defaults to 16 MiB / 16777216 bytes;
|
||||
@@ -59,6 +60,20 @@ in the same session. A returned `error.details.partial_observation` reference
|
||||
can be supplied directly to `inspect_analysis_view`. If the SDK cancels locally, retrieve its recorded ID from
|
||||
the Evidence bundle before inspecting the partial analysis.
|
||||
|
||||
## Inventory route candidates without returning the application graph
|
||||
|
||||
When the advertised schema supports `view`, use `detail: "summary"` with
|
||||
`view: {kind: "javascript-routes", collection: "routes", fields: ["path", "confidence"], limit: 20}`.
|
||||
The same view works with `analyze_web_bundle` on already selected captured sources.
|
||||
The complete source Evidence is retained; use its `parent_evidence_id` with
|
||||
`inspect_analysis_view` for `summary`, `routes`, `occurrences`, `sources`, or
|
||||
`unknowns`. Filter and batch IDs before requesting more fields, and follow
|
||||
`coverage.next_offset`. CLI selections require `--evidence_output` to keep the
|
||||
complete record. Static patterns, inferred deployment bases, heuristic system
|
||||
classification, and unavailable sources never establish route reachability or
|
||||
the complete application route denominator. See the
|
||||
[route inventory guide](https://github.com/morluto/rea/blob/main/docs/javascript-route-inventory.md).
|
||||
|
||||
## Read a module without repeating analysis
|
||||
|
||||
Use the actual returned parent ID, on the same connection, as `source.evidence_id`.
|
||||
|
||||
@@ -4,8 +4,10 @@ For release comparisons, export `list_procedures` Evidence from each target befo
|
||||
closing it. Close clears session records: import both bundles after collecting the
|
||||
two targets, or supply portable inline Evidence. `match_native_functions` pairs unique exact names and, when supplied,
|
||||
complete observed body bytes. Reuse optional same-context `list_names`,
|
||||
`procedure_info`, `read_bytes` and existing `analyze_function` Evidence; avoid
|
||||
decompiling every function merely to match it. Its summary retains a parent for
|
||||
`procedure_info`, `read_bytes` and existing `analyze_function` dossiers as
|
||||
`left_observations`/`right_observations`; an MCP `analyze_function` summary
|
||||
reply contributes its `parent_evidence_id` as a `retained-evidence` reference,
|
||||
not the summary itself. Avoid decompiling every function merely to match it. Its summary retains a parent for
|
||||
`inspect_analysis_view` `function-matches` collections `matches` and `functions`.
|
||||
Select columns, batch IDs and page individual facts; aliases remain unresolved.
|
||||
Unpaired observations do not prove additions/removals. Existing matched dossier
|
||||
@@ -96,6 +98,33 @@ dependencies. An omitted package kind stays null. A rustc commit hash is
|
||||
inferred from a read-only `/rustc/<40-hex>/` path and does not by itself
|
||||
identify the compiler. See [Rust binaries](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
|
||||
|
||||
## PE Authenticode certificate tables
|
||||
|
||||
Use `inspect_pe_certificates` for an explicit local PE32/PE32+ image when the
|
||||
question concerns the Authenticode certificate table, WIN_CERTIFICATE types, or
|
||||
embedded PKCS#7 signer and X.509 identity fields. No `open_binary`, Windows SDK,
|
||||
or target execution is required. The CLI equivalent is
|
||||
`inspect-pe-certificates PATH`. Preserve artifact SHA-256, table file ranges,
|
||||
payload hashes, original validity-time spellings, and
|
||||
`subject_key_identifier_hex` when the signer uses that form. A missing
|
||||
`IMAGE_DIRECTORY_ENTRY_SECURITY` directory is observed absence of that table,
|
||||
not proof the image is unsigned in every catalog or overlay sense. Signatures,
|
||||
chains, timestamps, and Authenticode digest coverage remain unobserved. See
|
||||
[PE Authenticode certificate tables](https://github.com/morluto/rea/blob/main/docs/pe-certificates.md).
|
||||
|
||||
## PE import and delay-load tables
|
||||
|
||||
Use `inspect_pe_imports` for an explicit local PE32/PE32+ image when the
|
||||
question concerns imported DLL and function names, ordinals, delay-load
|
||||
descriptors, or the pefile import hash. No `open_binary`, Windows SDK, or
|
||||
target execution is required. The CLI equivalent is `inspect-pe-imports PATH`.
|
||||
Preserve artifact SHA-256, directory file ranges, original DLL spellings, and
|
||||
ILT/IAT RVAs. A missing `IMAGE_DIRECTORY_ENTRY_IMPORT` directory is observed
|
||||
absence of that table, not proof the image never imported APIs through another
|
||||
table or overlay. Bound snapshots, loaded IAT contents, and export-forward
|
||||
resolution remain unobserved. See
|
||||
[PE import and delay-load tables](https://github.com/morluto/rea/blob/main/docs/pe-imports.md).
|
||||
|
||||
## ELF note tables
|
||||
|
||||
Use `inspect_elf_notes` for an explicit local ELF32/ELF64 image when the
|
||||
@@ -107,6 +136,42 @@ absence of those declarations, not proof the image never carried notes in
|
||||
another container. See
|
||||
[ELF note tables](https://github.com/morluto/rea/blob/main/docs/elf-notes.md).
|
||||
|
||||
## Apple IPS crash reports
|
||||
|
||||
Use `inspect_apple_ips` for an explicit local Apple `.ips` file when the
|
||||
question concerns crash metadata, exception and termination facts, threads,
|
||||
usedImages, lossless registers, or documented crash classes. No `open_binary`,
|
||||
Xcode, atos, or target execution is required. The CLI equivalent is
|
||||
`inspect-apple-ips PATH`. Preserve artifact SHA-256, `bug_type` as a string,
|
||||
uppercase image UUIDs, and integer literals that exceed IEEE-754 range. Only
|
||||
`bug_type` `"309"` yields a crash body; other DiagnosticReports types return
|
||||
no crash result. Opened Mach-O and dSYM UUID joins remain unobserved. See
|
||||
[Apple IPS crash reports](https://github.com/morluto/rea/blob/main/docs/apple-ips.md).
|
||||
|
||||
## CPython pyc files
|
||||
|
||||
Use `inspect_python_pyc` for an explicit local CPython 3.7–3.13 `.pyc` when the
|
||||
question concerns the header magic, PEP 552 source hash or timestamp, nested
|
||||
code-object names, or bytecode identity. No `open_binary`, Python installation,
|
||||
or target execution is required. The CLI equivalent is `inspect-python-pyc PATH`.
|
||||
Preserve artifact SHA-256, the 16-byte header fields, nested code names, and
|
||||
bytecode SHA-256. Bytecode is not decompiled. A decoded header does not prove
|
||||
the matching `.py` is present. See [CPython pyc inspection](https://github.com/morluto/rea/blob/main/docs/python-pyc.md).
|
||||
|
||||
## Erlang BEAM files
|
||||
|
||||
Use `inspect_erlang_beam` for an explicit local `FOR1`/`BEAM` file when the
|
||||
question concerns IFF chunks, atom names, exports, imports, lambdas, or the
|
||||
Code header. No `open_binary`, Erlang installation, or bytecode execution is
|
||||
required. The CLI equivalent is `inspect-erlang-beam PATH`. Preserve artifact
|
||||
SHA-256, chunk file ranges, 1-based atom indices, and original atom spellings.
|
||||
Missing optional tables stay null. For a few facts, use `detail: "summary"` with
|
||||
a `beam` view selecting a collection, exact names/indices and fields; read later
|
||||
pages from `parent_evidence_id` or the complete Evidence bundle. Same-name
|
||||
exports retain every arity and original atom index. CLI summary delivery requires
|
||||
`--evidence_output`. Null table counts remain distinct from decoded empty tables. See
|
||||
[Erlang BEAM files](https://github.com/morluto/rea/blob/main/docs/erlang-beam.md).
|
||||
|
||||
## SQLite snapshots
|
||||
|
||||
Use `inspect_sqlite_database` with an explicit local database snapshot for its
|
||||
@@ -161,6 +226,30 @@ and tool profile. Text offsets count UTF-16 code units; follow `next_offset`.
|
||||
The complete source remains retained after an oversized MCP reply. No target
|
||||
execution or network acquisition is implied, and WAT is decoded representation.
|
||||
|
||||
## Offline APK signatures
|
||||
|
||||
Use `inspect_apk_signature` for a selected local APK/ZIP/AAB. Default MCP summaries
|
||||
retain complete Evidence. Select `view.collection: signers/digests/lineage`,
|
||||
with scheme or ID batches, fields and paging; use `inspect_analysis_view` kind
|
||||
`apk-signature` for retained reads after deletion. IDs retain artifact/scheme
|
||||
scope and signer links; `fields: []` keeps identity links. Empty pages preserve
|
||||
partial decoding reasons and unknown authenticity. CLI compact delivery needs
|
||||
`--evidence_output` to preserve the complete parent.
|
||||
It reads the APK
|
||||
Signing Block (v2/v3/v3.1) and v1 `META-INF` signing without executing anything,
|
||||
and reports each signer's certificate facts (for v1, the certificate the PKCS#7
|
||||
block's SignerInfo names), the declared v3 rotation lineage,
|
||||
the declared source-stamp certificate and, for SHA-256/SHA-512 digests, whether
|
||||
the declared content digest matches the current bytes. That match is not a
|
||||
signature verification: anyone who changes the bytes can recompute the digest.
|
||||
`posture` summarises v1-only signing (which cannot tell a v1-only APK from one
|
||||
whose v2/v3 block was removed), observed signer agreement across schemes,
|
||||
whether the v1 signer equals the block-scheme signer (`false` is notable),
|
||||
whether every differing signer appears in a declared (unverified) rotation
|
||||
lineage, and the strongest scheme.
|
||||
It never claims authenticity: no signature or lineage is cryptographically
|
||||
verified.
|
||||
|
||||
## Native targets
|
||||
|
||||
After `open_binary`, use focused search, procedure, or function tools directly.
|
||||
@@ -173,6 +262,9 @@ unknown rather than false.
|
||||
For a completed `analyze_function` dossier, use `inspect_analysis_view` with
|
||||
`view: {kind: "native", facet: "value_flow_operations", offset: 0, limit: 32}`
|
||||
and the exact retained Evidence ID to read selected facts without reanalysis.
|
||||
The default MCP reply is already a summary of that dossier: its
|
||||
`normalized_result.parent_evidence_id` is the retained ID to page, and its
|
||||
null counts mark facets the provider did not record or declared unavailable.
|
||||
Native facets also include procedure, pseudocode, assembly, basic blocks,
|
||||
comments, callers/callees, references, unresolved calls, referenced strings and
|
||||
names, the native API record, and value-flow summaries. Pseudocode offsets and limits count UTF-16
|
||||
@@ -192,6 +284,10 @@ run, use `observe_native_calls` with explicit breakpoints and a bounded
|
||||
`duration_ms`/`max_events`. It launches a new process under LLDB, so the target
|
||||
runs and may change files or use the network. Hardened-runtime targets need the
|
||||
`get-task-allow` entitlement.
|
||||
Function breakpoints use exact LLDB runtime names: a Darwin C `_main` static symbol
|
||||
may be `main` at runtime. REA does not translate names. Set `module` to the image
|
||||
file name when selecting that image only; otherwise loaded system images also
|
||||
match. Inspect retained locations and module paths before attributing a call.
|
||||
|
||||
Use `binary_session` with no arguments to check the open target, selected
|
||||
provider, and alignment. Its default `result.tool_availability` carries a
|
||||
@@ -238,6 +334,35 @@ Exported C and embedded resources remain useful independent evidence, but a
|
||||
timeout does not establish complete analysis or absence of code. See the
|
||||
[deadline and client guide](https://github.com/morluto/rea/blob/main/docs/mcp-contracts.md#ghidra-first-query-deadlines-and-recovery).
|
||||
|
||||
## Bridge request ceiling
|
||||
|
||||
Hopper and Cutter share `REA_BRIDGE_MAX_REQUEST_BYTES`. Unset, both sides use
|
||||
16777216 bytes. The payload budget excludes the terminating LF. A decimal
|
||||
integer from 7464 to 536870887 raises or lowers that ceiling. The floor fits
|
||||
the largest bounded symbol-name command. A tight Cutter status still returns
|
||||
its session id and up to 64 completion receipts. Its path is included when it
|
||||
fits, and is unknown when it does not, so an older descriptor path is not
|
||||
paired with the new generation. The maximum is the encoded payload, one byte under Node's string ceiling so
|
||||
the framing LF still fits. Cutter measures, signs, and writes a fitting
|
||||
response in bounded chunks. The bridge searches only the newly read bytes
|
||||
for the framing LF. The client unescapes each socket chunk into one response
|
||||
string before parsing it, so this ceiling does not keep the paged wire frame
|
||||
and another concatenated copy. Invalid values are configuration errors and do not fall back to 16777216. Set the variable in the environment that starts REA, restart REA,
|
||||
and start Hopper or Cutter from that same environment. An out-of-band bridge
|
||||
without the setting stays at 16777216 bytes. The client reports that mismatch
|
||||
during discovery only after the hello proves a live authenticated bridge, and
|
||||
does not send a status request or the command. A stale descriptor is ignored.
|
||||
Discovery fails if any such live ceiling differs, even when another live
|
||||
session matches. A command response discarded for that ceiling stays an
|
||||
unknown execution with `output_truncated`. A hello
|
||||
that never sends its authenticated frame does not hold the accept loop.
|
||||
At most 8 connection threads run; a further accept is closed, and a failure
|
||||
to start a thread does not stop the listener. `completed_connection` is the
|
||||
longest prefix of connections that have terminated. Dense scalar replies are
|
||||
encoded in bounded batches.
|
||||
The 25000 ms Cutter request deadline and the 256-candidate discovery budget
|
||||
still have no runtime override.
|
||||
|
||||
## Static memory and original file bytes
|
||||
|
||||
For Ghidra, `read_bytes` reads initialized analysis memory, including loader
|
||||
@@ -409,6 +534,16 @@ Other payloads are opaque, and this operation does not decode images or simulate
|
||||
Windows resource loading. Consult the budgets and supported tree profile in
|
||||
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/binary-diagnostics.md).
|
||||
|
||||
## Offline PE and ELF export tables
|
||||
|
||||
Use `inspect_native_exports` with an absolute `path`, or
|
||||
`rea inspect-native-exports ./libexample.so --json`, to read a PE export
|
||||
directory or ELF dynamic symbol table on any host without opening a native
|
||||
target. Ordinal-only, forwarded and hidden-version exports stay distinct;
|
||||
partial or unsupported coverage means the inventory is incomplete, not that
|
||||
exports are absent. Use `inspect_macho` for Mach-O. See
|
||||
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/binary-diagnostics.md).
|
||||
|
||||
For exact core-module import/export differences, use `compare_wasm_interfaces`
|
||||
with two complete inspection Evidence records (or current-session retained
|
||||
references) and explicit scope. Preserve unknown types/duplicate names; static
|
||||
|
||||
@@ -1,4 +1,52 @@
|
||||
# Browser and Electron runtime observation
|
||||
# Runtime observation
|
||||
|
||||
## Native runtime questions
|
||||
|
||||
Start from the requested observation. State the selected target, input, expected
|
||||
result, failure signal and stopping condition before a run. Reuse static Evidence
|
||||
to choose a function or hypothesis; `trace_native_values` follows recovered
|
||||
static value dependencies and does not measure runtime values. Prefer static
|
||||
inspection or existing capture comparison when it already answers the question.
|
||||
|
||||
| Question | Existing workflow | Host and effects |
|
||||
| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Which selected native entry is reached, with which integer argument registers? | `observe_native_calls` on the active Mach-O, with exact function/Objective-C breakpoints and a duration/event bound | macOS with LLDB and required debugger access. Launches a new owned process, stops at entries, records and continues; target execution may write files, show UI or use the network. It never attaches to an existing process. Inspect termination and partial coverage after the run. |
|
||||
| What does caller-authored instrumentation observe? | `list_frida_devices` / `list_frida_processes` when discovery is needed; `start_frida_session`, `load_frida_script`, then status/unload/close | Requires compatible installed Frida bindings and selected device access; the real REA verifier covers Linux. Attach selects an existing PID; spawn selects a program and starts it paused until `resume_frida_session`. Scripts execute in the target and can change memory, files, network activity and behavior. This is not passive inspection. |
|
||||
| Which debugger state or controlled execution explains the behavior? | `start_gdb_session` and `gdb_console`, or `start_rizin_debug_session` and `rizin_debug_command` | Persistent debugger workflows are Linux-only. GDB starts a debugger; console commands choose/load/attach/run targets. Rizin debug start launches the selected target and requires a durable `inferior_tty`. Both consoles are unrestricted and can change target memory/registers, run shell commands, write files and use the network. |
|
||||
| How do terminal output, process lifetime and selected files differ between runs? | `capture_process_scenario`, then `compare_process_captures` with compatible recorded inputs | New captures require Linux or macOS and a working native PTY backend; macOS also needs `xcrun`/Swift for ownership inspection. Capture launches an owned command with ordinary user permissions. Observation paths select snapshots, not a filesystem sandbox. Comparing retained captures does not execute targets and is also available on Windows. |
|
||||
| What does an existing desktop window show, or what changes after a declared UI action? | `observe_native_ui`; use `capture_native_ui_scenario` only for requested actions | macOS 14+ with required Screen Recording/Accessibility access. Select an existing PID/window. Observation does not launch or own the app; scenario actions can change its data or trigger network activity, and leave its state as-is. |
|
||||
|
||||
Close each persistent session with its matching tool. `close_frida_session`
|
||||
unloads scripts and detaches; it does not kill the target. A still-paused spawn
|
||||
is resumed only after instrumentation cleanup is confirmed. GDB and Rizin close
|
||||
can detach a live inferior and let it continue only after verifying its durable
|
||||
output terminal. When disposition or cleanup is unresolved, preserve the owned
|
||||
session and reported recovery; do not assume that closing the debugger terminated
|
||||
the target. Process capture and LLDB call observation instead own their launched
|
||||
processes and report their termination/cleanup outcome. Cancellation is not proof
|
||||
that cleanup completed.
|
||||
|
||||
Unrestricted GDB/Rizin commands and relevant start/close operations retire an
|
||||
active binary-analysis session in the same MCP server. Close an active Ghidra
|
||||
session explicitly first; after target effects settle, reopen the artifact before
|
||||
new static analysis. A Cutter bridge command is likewise unrestricted and may
|
||||
affect its GUI/target; it is not a passive native observation substitute.
|
||||
|
||||
To test a static hypothesis across two builds, retain compatible static
|
||||
comparison Evidence and `compare_process_captures` Evidence. Supply explicit
|
||||
finding/dimension mappings to `correlate_static_and_runtime`. It does not ingest
|
||||
arbitrary Frida, LLDB or debugger traces, auto-match names, or prove causality
|
||||
from cochange. Preserve those traces as their own Evidence and leave unobserved
|
||||
behavior unknown. Record truncation, unmatched breakpoints, changed target
|
||||
identity and cleanup uncertainty as limitations, rather than treating no retained
|
||||
event as proof that the behavior never occurs.
|
||||
|
||||
Detailed contracts and prerequisites: [Frida](https://github.com/morluto/rea/blob/main/docs/frida-instrumentation.md),
|
||||
[GDB/Rizin/Cutter](https://github.com/morluto/rea/blob/main/docs/reverse-engineering-tools.md),
|
||||
[process capture](https://github.com/morluto/rea/blob/main/docs/process-capture.md),
|
||||
and [native calls/UI](https://github.com/morluto/rea/blob/main/docs/native-investigation.md#native-desktop-observation).
|
||||
|
||||
## Browser and Electron
|
||||
|
||||
Supply the existing browser or Electron instance's literal-port loopback CDP
|
||||
endpoint in the request. No separate permission configuration or per-call
|
||||
|
||||
@@ -17,7 +17,10 @@ describe the use case and observable outcome. -->
|
||||
## Change and scope
|
||||
|
||||
<!-- Explain the chosen approach, why it fits REA's architecture, and what is
|
||||
intentionally not included. -->
|
||||
intentionally not included. If adding complexity for a speculative or vanishingly
|
||||
unlikely race or security edge case, record the concrete failure mode, likelihood,
|
||||
complexity cost, and the user's buy-in obtained before implementation. Do not
|
||||
silently expand scope for theoretical robustness. -->
|
||||
|
||||
Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope).
|
||||
Unresolved scope, authority, side effects, or required verification block
|
||||
|
||||
@@ -72,6 +72,7 @@ jobs:
|
||||
browser: ${{ steps.plan.outputs.browser }}
|
||||
binary_layout: ${{ steps.plan.outputs.binary_layout }}
|
||||
evm: ${{ steps.plan.outputs.evm }}
|
||||
apk_signature: ${{ steps.plan.outputs.apk_signature }}
|
||||
javascript_recovery: ${{ steps.plan.outputs.javascript_recovery }}
|
||||
recorded_crash: ${{ steps.plan.outputs.recorded_crash }}
|
||||
web_network: ${{ steps.plan.outputs.web_network }}
|
||||
@@ -384,6 +385,7 @@ jobs:
|
||||
needs.changes.outputs.browser == 'true' ||
|
||||
needs.changes.outputs.binary_layout == 'true' ||
|
||||
needs.changes.outputs.evm == 'true' ||
|
||||
needs.changes.outputs.apk_signature == 'true' ||
|
||||
needs.changes.outputs.javascript_recovery == 'true' ||
|
||||
needs.changes.outputs.recorded_crash == 'true' ||
|
||||
needs.changes.outputs.web_network == 'true' ||
|
||||
@@ -762,6 +764,7 @@ jobs:
|
||||
tests/boundary/mcp/nativeDemangleMcp.test.ts
|
||||
tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts
|
||||
tests/boundary/cli/nativeSignatureCli.test.ts
|
||||
tests/boundary/cli/nativeMachoArm64_32Cli.test.ts
|
||||
tests/boundary/mcp/nativeSignatureTargetBindingMcp.test.ts
|
||||
src/process/capture/FilesystemSnapshot.test.ts
|
||||
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
|
||||
@@ -786,7 +789,7 @@ jobs:
|
||||
id: npm-cache
|
||||
uses: ./.github/actions/npm-cache
|
||||
- run: npm ci
|
||||
- name: Run the public CLI/MCP reconstruction journey
|
||||
- name: Verify static CLI/MCP journeys and synthetic report conformance
|
||||
env:
|
||||
REA_READINESS_REPORT_PATH: readiness-report.json
|
||||
run: |
|
||||
@@ -930,7 +933,7 @@ jobs:
|
||||
tests/boundary/filesystem/javascriptArtifactContentIdentity.test.ts
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: 22.19.0
|
||||
node-version: 22.23.4
|
||||
- name: Verify native path boundaries on Node 22
|
||||
run: >-
|
||||
npx vitest run --coverage.enabled=false
|
||||
@@ -946,7 +949,7 @@ jobs:
|
||||
retention-days: 30
|
||||
|
||||
docs:
|
||||
name: Docs Generation
|
||||
name: Documentation
|
||||
needs: changes
|
||||
if: needs.changes.outputs.docs == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
@@ -961,6 +964,8 @@ jobs:
|
||||
uses: ./.github/actions/npm-cache
|
||||
- run: npm ci
|
||||
- run: npm run docs:check
|
||||
- name: Render documentation and validate page links
|
||||
run: npm run docs:build
|
||||
- name: Generation leaves authored files unchanged
|
||||
run: git diff --exit-code
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
@@ -1003,6 +1008,16 @@ jobs:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
apk_signature:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.apk_signature == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-apk-signature.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
javascript_recovery:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.javascript_recovery == 'true'
|
||||
@@ -1103,6 +1118,7 @@ jobs:
|
||||
browser,
|
||||
binary_layout,
|
||||
evm,
|
||||
apk_signature,
|
||||
javascript_recovery,
|
||||
recorded_crash,
|
||||
web_network,
|
||||
|
||||
@@ -25,16 +25,16 @@ jobs:
|
||||
node-version-file: .nvmrc
|
||||
- name: Validate the SDK pin against the provider's verified release
|
||||
run: node scripts/verify-ghidra-bridge-sources.mjs --check-sdk-pin >> "$GITHUB_ENV"
|
||||
# Only the jars and application.properties are extracted: the check
|
||||
# compiles against them and never launches Ghidra or imports a target.
|
||||
- name: Acquire checksum-verified Ghidra jars in isolated runner storage
|
||||
# The bridge probe imports only source-owned ARM/Thumb bytes and never
|
||||
# executes target code. All SDK/project state stays in runner storage.
|
||||
- name: Acquire checksum-verified Ghidra SDK in isolated runner storage
|
||||
shell: bash
|
||||
run: |
|
||||
ghidra_root="${RUNNER_TEMP}/rea-ghidra"
|
||||
mkdir -p "${ghidra_root}"
|
||||
curl -fsSL --retry 3 --write-out 'ghidra_download_bytes=%{size_download} ghidra_download_seconds=%{time_total}\n' -o "${ghidra_root}/ghidra.zip" "${GHIDRA_RELEASE_URL}"
|
||||
echo "${GHIDRA_RELEASE_SHA256} ${ghidra_root}/ghidra.zip" | sha256sum -c -
|
||||
unzip -q "${ghidra_root}/ghidra.zip" '*/Ghidra/*.jar' '*/Ghidra/application.properties' -d "${ghidra_root}"
|
||||
unzip -q "${ghidra_root}/ghidra.zip" -d "${ghidra_root}"
|
||||
rm "${ghidra_root}/ghidra.zip"
|
||||
properties="$(find "${ghidra_root}" -path '*/Ghidra/application.properties' -print -quit)"
|
||||
test -n "${properties}"
|
||||
@@ -46,3 +46,19 @@ jobs:
|
||||
java_home_variable="JAVA_HOME_${GHIDRA_JAVA_MAJOR}_X64"
|
||||
test -n "${!java_home_variable}"
|
||||
JAVA_HOME="${!java_home_variable}" npm run verify:ghidra:bridge-sources -- --verified-sdk
|
||||
- name: Inspect real ARM and Thumb instruction context through the bridge
|
||||
shell: bash
|
||||
run: |
|
||||
java_home_variable="JAVA_HOME_${GHIDRA_JAVA_MAJOR}_X64"
|
||||
export JAVA_HOME="${!java_home_variable}"
|
||||
probe_root="${RUNNER_TEMP}/rea-arm-context"
|
||||
mkdir -p "${probe_root}/project"
|
||||
printf '\036\377\057\341\160\107\000\000' > "${probe_root}/arm-thumb.bin"
|
||||
timeout 180s "${GHIDRA_INSTALL_DIR}/support/analyzeHeadless" "${probe_root}/project" ArmContext \
|
||||
-import "${probe_root}/arm-thumb.bin" -loader BinaryLoader -processor ARM:LE:32:v7 \
|
||||
-noanalysis -readOnly -deleteProject \
|
||||
-scriptPath "${GITHUB_WORKSPACE}/tests/conformance/ghidra;${GITHUB_WORKSPACE}/bridge/ghidra" \
|
||||
-postScript ReaArmContextProbe.java "${GITHUB_WORKSPACE}/bridge/ghidra" \
|
||||
> "${probe_root}/output.log" 2>&1 || { cat "${probe_root}/output.log"; exit 1; }
|
||||
cat "${probe_root}/output.log"
|
||||
grep -F 'REA_ARM_CONTEXT_OK arm=0 thumb=1 unavailable=null (injected provider value)' "${probe_root}/output.log"
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Real Android metadata bridge
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["**"]
|
||||
paths:
|
||||
- .github/workflows/real-android-metadata.yml
|
||||
- bridge/android/**
|
||||
- src/android/JadxRelease.ts
|
||||
- scripts/fixtures/android/**
|
||||
- tests/conformance/android/**
|
||||
- tests/fixtures/android/static-initializers/**
|
||||
- tests/fixtures/android/direct-invokes/**
|
||||
- .nvmrc
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: android-metadata-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
android-metadata:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Fetch checksum-pinned engine and public APK fixture
|
||||
run: node scripts/fixtures/android/fetch.mjs "$RUNNER_TEMP/rea-android"
|
||||
- name: Compile and exercise the real metadata bridge
|
||||
shell: bash
|
||||
run: |
|
||||
export JAVA_HOME="$JAVA_HOME_21_X64"
|
||||
android_root="$RUNNER_TEMP/rea-android"
|
||||
classes="$android_root/classes"
|
||||
jar="$android_root/jadx-headless-mcp-0.7.1-all.jar"
|
||||
mkdir -p "$classes"
|
||||
"$JAVA_HOME/bin/javac" -J-Xmx512m -J-XX:ActiveProcessorCount=2 \
|
||||
-classpath "$jar" -d "$classes" \
|
||||
bridge/android/ReaJadxBridge.java \
|
||||
tests/conformance/android/ReaJadxMetadataRegression.java
|
||||
"$JAVA_HOME/bin/java" -Xmx512m -XX:ActiveProcessorCount=2 \
|
||||
-classpath "$classes:$jar" ReaJadxMetadataRegression \
|
||||
"$android_root/ApiDemos-debug.apk" \
|
||||
"$GITHUB_WORKSPACE/tests/fixtures/android/static-initializers" \
|
||||
"$classes" "$GITHUB_WORKSPACE/tests/fixtures/android/direct-invokes" \
|
||||
| tee "$android_root/metadata.log"
|
||||
grep -F 'RAW_CLASS_SELECTOR_REGRESSION_PASS nested=true metadata=true references=true' \
|
||||
"$android_root/metadata.log"
|
||||
@@ -0,0 +1,97 @@
|
||||
name: Real APK signature inspection
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_cache_artifact:
|
||||
description: Portable build cache artifact from the calling CI run.
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
NODE_OPTIONS: "--max-old-space-size=768"
|
||||
RAYON_NUM_THREADS: "1"
|
||||
GOMAXPROCS: "2"
|
||||
UV_THREADPOOL_SIZE: "2"
|
||||
JAVA_TOOL_OPTIONS: "-Xmx512m -XX:ActiveProcessorCount=2"
|
||||
|
||||
jobs:
|
||||
real-apk-signature:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 12
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Cache npm package downloads
|
||||
id: npm-cache
|
||||
uses: ./.github/actions/npm-cache
|
||||
- run: npm ci
|
||||
- uses: ./.github/actions/portable-build-cache
|
||||
with:
|
||||
build_cache_artifact: ${{ inputs.build_cache_artifact }}
|
||||
- name: Prepare pinned SDK oracle without installing host software
|
||||
shell: bash
|
||||
run: |
|
||||
python3 - <<'PYTHON'
|
||||
import hashlib, os, urllib.request, zipfile
|
||||
from pathlib import Path
|
||||
root = Path(os.environ["RUNNER_TEMP"]) / "rea-apk-signature-sdk"
|
||||
packages = [
|
||||
("build-tools_r36.1_linux.zip", "a7b5889e4a79fcf3b0976bef40d401f4240fb1eed891d9d91169da1111e11d78", 64195204, "build-tools/36.1.0", {"apksigner", "aapt2", "lib/apksigner.jar", "source.properties"}),
|
||||
("platform-36_r02.zip", "37607369a28c5b640b3a7998868d45898ebcb777565a0e85f9acf36f29631d2e", 65878410, "platforms/android-36", {"android.jar", "source.properties"}),
|
||||
]
|
||||
for name, expected, expected_size, directory, selected in packages:
|
||||
archive = root / name
|
||||
archive.parent.mkdir(parents=True, exist_ok=True)
|
||||
digest = hashlib.sha256()
|
||||
total = 0
|
||||
with urllib.request.urlopen("https://dl.google.com/android/repository/" + name, timeout=60) as response, archive.open("wb") as output:
|
||||
while chunk := response.read(1024 * 1024):
|
||||
total += len(chunk)
|
||||
if total > expected_size:
|
||||
raise RuntimeError("SDK archive exceeds the pinned package size budget")
|
||||
digest.update(chunk)
|
||||
output.write(chunk)
|
||||
if total != expected_size or digest.hexdigest() != expected:
|
||||
raise RuntimeError("SDK oracle archive digest mismatch")
|
||||
extracted = set()
|
||||
with zipfile.ZipFile(archive) as source:
|
||||
for entry in source.infolist():
|
||||
relative = entry.filename.partition("/")[2]
|
||||
if relative not in selected:
|
||||
continue
|
||||
target = root / directory / relative
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(source.read(entry))
|
||||
target.chmod((entry.external_attr >> 16) & 0o777 or 0o644)
|
||||
extracted.add(relative)
|
||||
if extracted != selected:
|
||||
raise RuntimeError("Pinned SDK package is missing required oracle files")
|
||||
with Path(os.environ["GITHUB_ENV"]).open("a") as environment:
|
||||
environment.write("ANDROID_HOME=" + str(root) + "\n")
|
||||
PYTHON
|
||||
- run: npm run verify:apk-signature -- --require-tools
|
||||
@@ -48,6 +48,9 @@ jobs:
|
||||
run: npm install --prefix "$RUNNER_TEMP/rea-web-source-map-compiler" --no-audit --no-fund --package-lock=false esbuild@0.25.10
|
||||
- run: npm run verify:browser:source-maps
|
||||
env:
|
||||
# Retain browser launch argv/PID/stderr in the verifier's captured
|
||||
# CLI failure diagnostics without changing its deadline or retrying.
|
||||
DEBUG: pw:browser
|
||||
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
|
||||
REA_BROWSER_NO_SANDBOX: "true"
|
||||
REA_WEB_SOURCE_MAP_COMPILER: ${{ runner.temp }}/rea-web-source-map-compiler/node_modules/esbuild/lib/main.js
|
||||
+119
-41
@@ -47,19 +47,63 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: |
|
||||
finalized=$(gh api --paginate "repos/${GH_REPO}/pulls?state=open&base=main&per_page=100" \
|
||||
--jq '.[] | select(.head.repo.full_name == .base.repo.full_name) | select(.head.ref | startswith("release-please--branches--main--")) | select(any(.labels[]; .name == "rea:release-notes-finalized")) | .number')
|
||||
# Filter at the repository connection, without a search-index dependency.
|
||||
pages=$(gh api graphql --paginate --slurp \
|
||||
-f owner="${GH_REPO%/*}" -f name="${GH_REPO#*/}" \
|
||||
-f query='query($owner:String!,$name:String!,$endCursor:String) {
|
||||
repository(owner:$owner,name:$name) {
|
||||
nameWithOwner
|
||||
pullRequests(first:100,after:$endCursor,states:OPEN,baseRefName:"main",labels:["rea:release-notes-finalized"]) {
|
||||
nodes { number headRefName headRepository { nameWithOwner } }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
}')
|
||||
finalized=$(jq -er '
|
||||
if type != "array" or length == 0 then error("Missing GitHub pages") else . end |
|
||||
if .[-1].data.repository.pullRequests.pageInfo.hasNextPage != false
|
||||
then error("Incomplete GitHub pagination") else . end |
|
||||
map(
|
||||
if (.errors // [] | length) != 0 then error("GitHub query failed") else . end |
|
||||
.data.repository |
|
||||
if (.nameWithOwner | type) != "string" or (.pullRequests.nodes | type) != "array" or
|
||||
(.pullRequests.pageInfo.hasNextPage | type) != "boolean"
|
||||
then error("Invalid GitHub repository connection") else . end |
|
||||
.nameWithOwner as $repo | .pullRequests.nodes[] |
|
||||
if (.number | type) != "number" or (.headRefName | type) != "string" or (has("headRepository") | not) or
|
||||
(.headRepository != null and (.headRepository.nameWithOwner | type) != "string")
|
||||
then error("Invalid GitHub pull request") else . end |
|
||||
select(.headRepository.nameWithOwner == $repo) |
|
||||
select(.headRefName | startswith("release-please--branches--main--")) | .number
|
||||
) | map(tostring) | join(" ")' <<< "$pages")
|
||||
if [[ -n "${finalized}" ]]; then
|
||||
echo "Finalized release notes on PR ${finalized}; automatic proposal refresh is paused." >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "frozen=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
|
||||
- name: Check out proposal controller and complete Git history
|
||||
if: steps.editorial.outputs.frozen != 'true'
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
if: steps.editorial.outputs.frozen != 'true'
|
||||
with:
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
target-branch: main
|
||||
skip-github-release: true
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install proposal controller dependencies
|
||||
if: steps.editorial.outputs.frozen != 'true'
|
||||
run: npm ci
|
||||
|
||||
- name: Update release proposal
|
||||
if: steps.editorial.outputs.frozen != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
SOURCE_SHA: ${{ github.sha }}
|
||||
run: node scripts/release-proposal.mjs --target-branch main --source-sha "$SOURCE_SHA"
|
||||
|
||||
merged-release:
|
||||
name: Release reviewed main merge
|
||||
@@ -232,15 +276,25 @@ jobs:
|
||||
node scripts/release-notes.mjs publication --repository "$GH_REPO" \
|
||||
--source "$SOURCE_SHA"
|
||||
|
||||
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
|
||||
id: release
|
||||
with:
|
||||
- name: Prepare checkpoint release proposal
|
||||
if: inputs.phase == 'prepare'
|
||||
id: proposal
|
||||
env:
|
||||
# A dedicated credential starts PR CI automatically; the built-in
|
||||
# token creates approval-required PR runs for maintainers to approve.
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
|
||||
run: node scripts/release-proposal.mjs --target-branch "$RELEASE_BRANCH" --source-sha "$SOURCE_SHA"
|
||||
|
||||
- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
|
||||
if: inputs.phase == 'publish'
|
||||
id: release
|
||||
with:
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
target-branch: ${{ inputs.release_branch }}
|
||||
skip-github-release: ${{ inputs.phase == 'prepare' }}
|
||||
skip-github-pull-request: ${{ inputs.phase == 'publish' }}
|
||||
skip-github-release: false
|
||||
skip-github-pull-request: true
|
||||
|
||||
- name: Require a merged release PR for publication
|
||||
if: inputs.phase == 'publish' && steps.release.outputs.release_created != 'true'
|
||||
@@ -260,26 +314,26 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Check out release pull request
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
ref: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}
|
||||
ref: ${{ fromJSON(steps.proposal.outputs.pr).headBranchName }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js for generated documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
|
||||
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
|
||||
run: npm ci
|
||||
|
||||
- name: Validate prepared release version and migration notes
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
|
||||
env:
|
||||
RELEASE_BRANCH: ${{ inputs.release_branch }}
|
||||
SOURCE_SHA: ${{ steps.branch.outputs.sha }}
|
||||
@@ -289,11 +343,11 @@ jobs:
|
||||
--source-sha "$SOURCE_SHA"
|
||||
|
||||
- name: Regenerate release documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
|
||||
run: npm run docs:generate
|
||||
|
||||
- name: Validate generated release documentation
|
||||
if: inputs.phase == 'prepare' && steps.release.outputs.prs_created == 'true'
|
||||
if: inputs.phase == 'prepare' && steps.proposal.outputs.prs_created == 'true'
|
||||
run: npm run docs:check && git diff --exit-code
|
||||
|
||||
qualification:
|
||||
@@ -315,6 +369,9 @@ jobs:
|
||||
|
||||
rehearsal:
|
||||
name: Rehearse qualified release on a clean runner
|
||||
env:
|
||||
TURBO_TOKEN: ""
|
||||
TURBO_CACHE: local:rw
|
||||
needs: [merged-release, release-please, qualification]
|
||||
if: >-
|
||||
always() && !cancelled() && needs.qualification.result == 'success'
|
||||
@@ -348,6 +405,9 @@ jobs:
|
||||
always() && !cancelled() &&
|
||||
needs.qualification.result == 'success' && needs.rehearsal.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ""
|
||||
TURBO_CACHE: local:rw
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
@@ -356,41 +416,56 @@ jobs:
|
||||
with:
|
||||
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
registry-url: https://registry.npmjs.org
|
||||
- name: Download the original qualified package
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: verified-release-package
|
||||
path: .cache/verified-package/
|
||||
- name: Bind publication to qualified intent
|
||||
id: package
|
||||
run: node scripts/ci/prepare-npm-publication.mjs
|
||||
- name: Publish
|
||||
if: steps.package.outputs.published == 'false'
|
||||
env:
|
||||
NPM_RELEASE_TAG: ${{ steps.package.outputs.tag }}
|
||||
run: >-
|
||||
npm publish .cache/verified-package/package.tgz --ignore-scripts
|
||||
--registry=https://registry.npmjs.org --access=public --tag="$NPM_RELEASE_TAG"
|
||||
|
||||
verify-npm:
|
||||
name: Verify published npm package
|
||||
needs: [merged-release, release-please, publish]
|
||||
if: >-
|
||||
always() && !cancelled() && needs.publish.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ""
|
||||
TURBO_CACHE: local:rw
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
ref: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
package-manager-cache: false
|
||||
- run: npm ci
|
||||
|
||||
- uses: ./.github/actions/prepare-release-package
|
||||
id: package
|
||||
|
||||
- name: Inspect existing npm version
|
||||
id: registry
|
||||
env:
|
||||
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
|
||||
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
|
||||
run: node scripts/ci/npm-release.mjs inspect "$VERIFIED_PACKAGE_TARBALL" "$VERIFIED_PACKAGE_VERSION"
|
||||
|
||||
- name: Publish
|
||||
if: steps.registry.outputs.published == 'false'
|
||||
shell: bash
|
||||
env:
|
||||
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
|
||||
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
|
||||
run: |
|
||||
tag="$(node scripts/release-npm-tag.mjs "$VERIFIED_PACKAGE_VERSION")"
|
||||
npm publish "${VERIFIED_PACKAGE_TARBALL}" --access public --tag "${tag}"
|
||||
|
||||
- name: Verify published npm bytes and dist-tag
|
||||
env:
|
||||
VERIFIED_PACKAGE_TARBALL: ${{ steps.package.outputs.tarball }}
|
||||
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
|
||||
run: node scripts/ci/npm-release.mjs verify "$VERIFIED_PACKAGE_TARBALL" "$VERIFIED_PACKAGE_VERSION"
|
||||
|
||||
- name: Verify published CLI from npm
|
||||
env:
|
||||
VERIFIED_PACKAGE_VERSION: ${{ steps.package.outputs.version }}
|
||||
@@ -398,9 +473,12 @@ jobs:
|
||||
|
||||
publish-mcp:
|
||||
name: Publish to MCP Registry
|
||||
needs: [merged-release, release-please, publish]
|
||||
env:
|
||||
TURBO_TOKEN: ""
|
||||
TURBO_CACHE: local:rw
|
||||
needs: [merged-release, release-please, verify-npm]
|
||||
if: >-
|
||||
always() && !cancelled() && needs.publish.result == 'success' &&
|
||||
always() && !cancelled() && needs.verify-npm.result == 'success' &&
|
||||
((needs.merged-release.result == 'success' && needs.merged-release.outputs.release_created == 'true') ||
|
||||
(needs.release-please.result == 'success' && inputs.phase == 'publish' && needs.release-please.outputs.release_created == 'true'))
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -17,10 +17,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Exercise unavailable-runtime diagnostics, the first supported release,
|
||||
# and new patches on both supported lines without hand-editing versions.
|
||||
# Exercise unavailable-runtime diagnostics and rolling supported releases.
|
||||
# Security floors prevent fallback to an older hosted-toolcache patch.
|
||||
# The repository-pinned Node 24 version also runs the complete test suite.
|
||||
node: ["22.x", "24.15.0", "24.x", "26.x"]
|
||||
node: ["^22.23.4", "24.22.1", "^24.22.1", "^26.12.1"]
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"threshold": 3.5,
|
||||
"threshold": 4,
|
||||
"reporters": ["consoleFull"],
|
||||
"ignore": [
|
||||
"node_modules/**",
|
||||
|
||||
@@ -22,6 +22,8 @@ Keep proof of ownership separate from validity, editability, and readiness. Malf
|
||||
|
||||
For lifecycle changes, follow [ownership and completion invariants](docs/tool-design.md#own-the-lifecycle) and select the applicable [lifecycle regressions](docs/testing.md#lifecycle-regressions). Include late handshakes, cancellation during final cleanup, mixed child outcomes, and cleanup retry where those stages exist; active-execution cancellation alone does not cover finalization.
|
||||
|
||||
Before adding complexity for a speculative or vanishingly unlikely race or security edge case, explain the concrete failure mode, likelihood, and complexity cost to the user and get their buy-in. Do not silently expand scope for theoretical robustness.
|
||||
|
||||
Assess brittleness in touched code during implementation, debugging, review, and maintenance. Fix the producing representation or ownership rule before adding downstream repairs. Normalize once at the owning boundary using format-aware parsers. Remove superseded representations, aliases, wrappers, and redundant tests after verifying affected consumers. Introduce abstractions only when they remove observed complexity.
|
||||
|
||||
When fixing a bug, inspect the producing boundary and affected consumers for duplicated interpretation or unclear ownership. Complete any directly related refactor in the same change and remove superseded code. Passing regression tests alone does not establish that the brittleness is resolved. If related cleanup remains, explain what remains and why.
|
||||
@@ -59,7 +61,7 @@ do not establish fewer tokens or faster analysis.
|
||||
|
||||
## Development
|
||||
|
||||
Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Cursor Cloud may place older Node.js first on `PATH`; environment setup installs the pinned toolchain under `/usr/local/bin`.
|
||||
Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Cursor Cloud may place older Node.js first on `PATH`; environment setup installs the pinned toolchain under `/usr/local/bin`. Prefix `PATH="/usr/local/bin:$PATH"` before `node` and `npm`.
|
||||
|
||||
- `npm ci`: install locked dependencies.
|
||||
- `npm run build:cached`: build the runtime and packaged skill.
|
||||
@@ -70,6 +72,8 @@ Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain.
|
||||
|
||||
Use relevant checks for the change. The complete local gate, `npm run check:pr`, is optional for broad changes or CI diagnosis. See [CONTRIBUTING.md](CONTRIBUTING.md) for development, generated-file ownership, and release conventions.
|
||||
|
||||
Coordinate expensive checks across agents. If a test unexpectedly consumes heavy CPU, stop its owned process family and profile a bounded representative case before retrying. Inspect production work, fixture generation, subprocesses, and repeated setup; preserve regression coverage when optimizing. Follow the [test resource guidance](docs/testing.md#developer-commands).
|
||||
|
||||
`docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored outputs. Edit source contracts and `.agents/skills/reverse-engineer-anything/`, then regenerate as needed. `.cache/mcp-tool-catalog.json` is test metadata; runtime builds and source checking do not consume it. Never commit binaries, provider project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts.
|
||||
|
||||
## Tool and Test Changes
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ Full contribution record: [4 commits and 1 merged PR](https://github.com/morluto
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
- **Saved Evidence:** Evidence and analysis snapshots now require explicit nullable `relationship_coverage` in their identity commitments. Artifact inventories require scoped coverage and explicit `integrity_contradictions`; obsolete `coverage_status` and prose-based coverage inference are removed. Regenerate old Evidence/snapshots and inventory observations with this release: saved files are not migrated and omitted facts are not filled with guessed defaults. ([db8128d](https://github.com/morluto/rea/commit/db8128dc0d1c4f731f3d19877fa00d25cf17f657), [0405c9d](https://github.com/morluto/rea/commit/0405c9d361de6d904dfa16cc0b6d8350407f2c9b), [a9588be](https://github.com/morluto/rea/commit/a9588be4ebdd20eeda975cb63ffcdb1cd868b340)).
|
||||
- **Saved Evidence:** Evidence and analysis snapshots now require explicit nullable `relationship_coverage` in their identity commitments. Earlier records whose digest omitted null coverage are incompatible even when the serialized field is explicitly null. Artifact inventories require scoped coverage and explicit `integrity_contradictions`; obsolete `coverage_status` and prose-based coverage inference are removed. Regenerate old Evidence/snapshots and inventory observations with this release: saved files are not migrated and omitted facts are not filled with guessed defaults. ([db8128d](https://github.com/morluto/rea/commit/db8128dc0d1c4f731f3d19877fa00d25cf17f657), [0405c9d](https://github.com/morluto/rea/commit/0405c9d361de6d904dfa16cc0b6d8350407f2c9b), [a9588be](https://github.com/morluto/rea/commit/a9588be4ebdd20eeda975cb63ffcdb1cd868b340)).
|
||||
- **Browser capture results:** Consumers must retain resolved capture limits, event families and omission facts. Set desired budgets through the supported capture options; omitted caller inputs still select defaults, but stored observations must report the resolved values. Admitted content reads queue behind bounded concurrency. ([57a23f0](https://github.com/morluto/rea/commit/57a23f0a2da0fdc3835e203718ded8acea5a34e6)).
|
||||
- **Android version codes:** `inspect_adb_package.version_code` is a decimal string rather than a JSON number. Read it as a string, or use `BigInt` for numeric comparison. ([#3485](https://github.com/morluto/rea/pull/3485), [morluto/rea#3368](https://github.com/morluto/rea/issues/3368), [7666a48](https://github.com/morluto/rea/commit/7666a482b69dc7cabdbac3bbaf89a43e32300192)). Thanks @coygeek, @kaoru0822-kitauji.
|
||||
- **Native UI failures:** Failed or cancelled native UI scenarios now return typed errors with the collected report in `partial_observation`. Handle the error path and read that report instead of expecting a successful result containing failed steps. ([d909919](https://github.com/morluto/rea/commit/d9099190ab3a819bd3a9fd62a8714cef398016d5)).
|
||||
|
||||
+27
-4
@@ -10,10 +10,18 @@ and maintenance cost. Keep cosmetic polish with useful fixes; confusing or
|
||||
incorrect user instructions warrant standalone corrections. Collect other
|
||||
cosmetic-only suggestions in an issue rather than separate PRs.
|
||||
|
||||
For capability organization and provider composition, use the
|
||||
[architecture map](docs/architecture.mermaid). Production factories in
|
||||
`src/composition/` construct fresh providers without starting engines or acquiring
|
||||
targets; optional adapter failures must preserve successful peers. Run
|
||||
For selected architectural owners and dependency paths, use the
|
||||
[architecture map](docs/architecture.mermaid). It is not a complete directory or
|
||||
import inventory. For current provider wiring, start with
|
||||
[`src/composition/binary.ts`](src/composition/binary.ts) for deep candidates and
|
||||
the other `src/composition/` factories for tool services, including
|
||||
[`gpu.ts`](src/composition/gpu.ts) and [`model.ts`](src/composition/model.ts).
|
||||
Live Frida and Cutter adapters are constructed directly in
|
||||
[`src/cli/fridaCommands.ts`](src/cli/fridaCommands.ts),
|
||||
[`src/cli/cutterCommands.ts`](src/cli/cutterCommands.ts), and
|
||||
[`src/server/createServer.ts`](src/server/createServer.ts).
|
||||
Production factories construct fresh providers without starting engines or
|
||||
acquiring targets; optional adapter failures must preserve successful peers. Run
|
||||
`npm run verify:test-discovery` after adding or moving tests. Resolved import
|
||||
boundaries are checked by `npm run verify:module-boundaries`, included in lint.
|
||||
|
||||
@@ -26,6 +34,8 @@ to locate provider and shared-workflow bottlenecks, preserve Evidence, and
|
||||
compare equivalent real tasks before claiming an improvement.
|
||||
|
||||
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
|
||||
When adding or changing an admission ceiling, budget, or deadline, follow
|
||||
[admission limits and recovery](docs/design/limits-and-recovery.md).
|
||||
|
||||
## Reporting issues
|
||||
|
||||
@@ -239,6 +249,19 @@ MinGW-w64, and `tar`; the build script does not run on Windows. See that guide
|
||||
for the build and artifact-verification commands and the separate real Windows
|
||||
verification prerequisites. Keep the generated addon and manifest uncommitted.
|
||||
|
||||
The general source suite (`npm run test:fast`, `npm run test:changed`, and
|
||||
`npm run test:local`) runs on Linux in CI, and Windows CI runs a curated lane.
|
||||
On a Windows checkout, some source tests fail because fixtures use POSIX paths,
|
||||
`#!/bin/sh` tool stubs, or POSIX rename semantics. Compare failures against the
|
||||
base revision, or run the suite on Linux or WSL, before attributing them to a
|
||||
change. See [testing](docs/testing.md#developer-commands) for command scopes
|
||||
and lane selection.
|
||||
|
||||
`git submodule update --init --recursive` fails at the pinned Unblob submodule
|
||||
on Windows and can leave later submodules unchecked out. Apply the
|
||||
[Unblob checkout workaround](docs/firmware-analysis.md#windows-submodule-checkout),
|
||||
then rerun the command.
|
||||
|
||||
## Documentation website
|
||||
|
||||
The VitePress site uses the Markdown files in `docs/`. Run `npm run docs:dev`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div align="center">
|
||||
|
||||
**English** · [简体中文](docs/readme/README_zh.md) · [繁體中文](docs/readme/README_zh-TW.md) · [日本語](docs/readme/README_ja.md) · [한국어](docs/readme/README_ko.md) · [Türkçe](docs/readme/README_tr.md) · [Русский](docs/readme/README_ru.md) · [Tiếng Việt](docs/readme/README_vi.md) · [ไทย](docs/readme/README_th.md) · [Deutsch](docs/readme/README_de.md) · [Español](docs/readme/README_es.md) · [Français](docs/readme/README_fr.md) · [Українська](docs/readme/README_uk.md) · [Polski](docs/readme/README_pl.md) · [Português (Brasil)](docs/readme/README_pt-BR.md) · [العربية](docs/readme/README_ar.md) · [فارسی](docs/readme/README_fa.md) · [Bahasa Indonesia](docs/readme/README_id.md) · [Italiano](docs/readme/README_it.md)
|
||||
· [Ελληνικά](docs/readme/README_el.md) · [മലയാളം](docs/readme/README_ml.md)
|
||||
· [Ελληνικά](docs/readme/README_el.md) · [മലയാളം](docs/readme/README_ml.md) · [Roman Urdu](docs/readme/README_ur-Latn.md)
|
||||
|
||||
# REA: Reverse Engineer Anything
|
||||
|
||||
@@ -177,7 +177,7 @@ CLI commands use the same workflows.
|
||||
|
||||
## What you can analyze
|
||||
|
||||
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+, plus npm.
|
||||
REA requires Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), or 26.12.1+, plus npm.
|
||||
Additional tools and host support depend on the target:
|
||||
|
||||
| Target | What REA returns | Requirements and guide |
|
||||
@@ -191,7 +191,11 @@ Additional tools and host support depend on the target:
|
||||
| IL2CPP metadata | Unity global-metadata.dat assembly images, type and method names and index ranges | Built-in static decoding of versions 24, 27, 29, 31 and 39; no Unity required |
|
||||
| Godot PCK | Archive inventory and extraction, original header/directory facts and selected content identities | Built-in static formats 2, 3 and 4; no Godot required |
|
||||
| Rust binaries | Embedded cargo-auditable crate names, versions, sources and dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](docs/rust-binaries.md) |
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](docs/pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](docs/pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](docs/elf-notes.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and documented crash classes | Built-in static JSON inspection; [Apple IPS](docs/apple-ips.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, imports, lambdas and the Code header | Built-in static BEAM inspection; [Erlang BEAM](docs/erlang-beam.md) |
|
||||
| WASM artifact | Validation, sections, exact import/export WAT forms and decoded WAT | Caller-supplied WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/evm-bytecode.md) |
|
||||
| SQLite databases | Tables, columns, indexes, views, triggers and selected ordinary-table rows | Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits |
|
||||
@@ -201,6 +205,7 @@ Additional tools and host support depend on the target:
|
||||
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/web-network-captures.md) |
|
||||
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | None (static only); [managed-code guide](docs/managed-code-analysis.md) |
|
||||
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
|
||||
| APK signatures | v1/v2/v3/v3.1 signers, certificate fingerprints, key rotation and digest self-consistency | None (static only); `inspect_apk_signature` |
|
||||
| Android devices | Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers | Caller-supplied adb (emulator or hardware); [ADB guide](docs/adb-device-analysis.md) |
|
||||
| JEB projects | Engine-backed project units, artifact digests and type/method pseudo-code | A caller-started JEB client serving MCP, selected with REA_JEB_MCP_URL; [JEB guide](docs/jeb-analysis.md) |
|
||||
| Android resources | Decoded manifest, string tables, locales and version facts | Caller-supplied Apktool launcher; [resource guide](docs/apktool-resource-analysis.md) |
|
||||
@@ -238,7 +243,12 @@ for features added since the latest npm release.
|
||||
|
||||
## Showcases
|
||||
|
||||
[](https://rea.tools/showcase/)
|
||||
<a href="https://rea.tools/showcase/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/rea-showcases-dark.svg" />
|
||||
<img alt="Illustrations of the DX-Ball sound-pan, Notion clipboard-bridge, and TH04 bullet-ring showcases" src="docs/assets/rea-showcases.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
### DX-Ball: reconstruct a sound-pan calculation
|
||||
|
||||
@@ -396,7 +406,7 @@ to improve the code or docs.
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and checks,
|
||||
[testing](docs/testing.md) for verification lanes, and the
|
||||
[architecture map](docs/architecture.mermaid) for the project structure.
|
||||
[architecture map](docs/architecture.mermaid) for selected layers and dependency paths.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+22
-21
@@ -16,6 +16,7 @@ notices supplied with the software you actually obtain or redistribute.
|
||||
| Material | Use and licensing records |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| npm dependencies | [Complete inventory](licenses/npm-dependencies.txt) of every entry in `package-lock.json`, including transitive, optional platform, development, and website dependencies. [Runtime notices](licenses/npm-runtime-notices.txt) preserve the copyright, license, and NOTICE documents from integrity-verified release archives, plus the MIT notice for Rolldown's generated runtime helpers. Development dependencies are inventoried but are not all distributed in REA's npm package. |
|
||||
| pefile ordinal tables | Import-hash ordinal names are derived from [pefile 2024.8.26](https://github.com/erocarrera/pefile/tree/4b3b1e2e568a88d4f1897d694d684f23d9e270c4), copyright 2004–2024 Ero Carrera. Preserve the [MIT license](licenses/pefile-2024.8.26-LICENSE.txt). The Python package is not a runtime dependency. |
|
||||
| NativeAOT parser | REA's derived parser uses the MIT-licensed [ghidra-nativeaot](https://github.com/Washi1337/ghidra-nativeaot/tree/effeb734fc570c32650f88b159608979dc7b423e) work of Washi and the NativeAOT layout definitions in [dotnet/runtime](https://github.com/dotnet/runtime/tree/a3b2d40328be0be3f8dd43f0e5cc925b8827b044). Preserve [Washi's MIT notice](licenses/nativeaot-parser-MIT.txt) and the [.NET Foundation MIT notice](licenses/dotnet-runtime-MIT.txt). |
|
||||
| EVM analysis | `evmole@0.9.3`, MIT, copyright 2023 Maxim Andreev. The published npm archive omits its license document; REA retains the [original upstream license](third_party/evmole/LICENSE) and [provenance](third_party/evmole/README.md). It is also included in the runtime notices. |
|
||||
| Windows native addon | The release addon is compiled with MinGW-w64 and statically links GCC runtime libraries. Preserve the [MinGW-w64 runtime notices](licenses/MinGW-w64-runtime.txt), [GPLv3 text](licenses/GPL-3.0.txt), and [GCC Runtime Library Exception 3.1](licenses/GCC-Runtime-Library-Exception-3.1.txt). The exception governs eligible compiled combinations; it does not relicense the runtime itself under MIT. The Node-API build headers are from Node 22.19.0; its [original license and component notices](licenses/Node-22.19.0-LICENSE.txt) are retained. Node itself is supplied by the user. Snapshot provenance and release-toolchain review requirements are in [licenses/README.md](licenses/README.md). |
|
||||
@@ -52,27 +53,27 @@ REA's adapters do not grant, replace, or waive these licenses. The upstream
|
||||
executables and their dependency graphs are supplied separately. Git submodules
|
||||
pin inspection sources; they are not included in the npm package.
|
||||
|
||||
| Project | License and source | REA relationship |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Ghidra | [Apache-2.0](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/LICENSE), with the government-work and component qualifications in its [NOTICE](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/NOTICE) | Bring your own Ghidra 12.1.4. Copies of its [LICENSE](licenses/ghidra-12.1.4-LICENSE.txt), [NOTICE](licenses/ghidra-12.1.4-NOTICE.txt), and [DISCLAIMER](licenses/ghidra-12.1.4-DISCLAIMER.txt) are retained for reference. |
|
||||
| ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. |
|
||||
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
|
||||
| jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. |
|
||||
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.833/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.833/NOTICE) | Bring your own `kuna`; REA verified 1.833 and never downloads, installs or bundles it. REA invokes it as a separate process. |
|
||||
| Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. |
|
||||
| Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. |
|
||||
| Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. |
|
||||
| Wakaru | [Apache-2.0](https://github.com/pionxzh/wakaru/blob/8219bf5016a063d7fb215101a8c45d5959ec733f/LICENSE) | v1.14.0; pinned source and separately obtained JavaScript reconstruction tooling. |
|
||||
| WABT | [Apache-2.0](https://github.com/WebAssembly/wabt/blob/ff0ef7e0009402740c805a9744c09b05be063e48/LICENSE) | v1.0.42; external WebAssembly tools; [integration provenance](third_party/wabt/README.md). |
|
||||
| pwntools | [MIT, with GPL/BSD component qualifications](https://github.com/Gallopsled/pwntools/blob/3ad86ec5adab1abf9781ca7552a6f5d7cff7a33c/LICENSE-pwntools.txt) | 4.15.0; caller-supplied offline ELF analysis package. |
|
||||
| pyelftools | [Public-domain dedication and component notices](https://github.com/eliben/pyelftools/blob/v0.33/LICENSE) | 0.33; includes separately licensed bundled material, including Construct. |
|
||||
| Unicorn | [GPL-2.0 and component notices](https://github.com/unicorn-engine/unicorn/blob/2.1.2/COPYING) | 2.1.2; used by the caller-supplied ELF analysis profile for instruction emulation. |
|
||||
| pwndbg | [MIT](third_party/pwndbg/LICENSE.md) | 2026.09.15, commit `55596aa676a2756efb8d67034f31675170eabe48`; separately obtained debugger extension. |
|
||||
| GNU GDB | [GPL-3.0-or-later](https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/COPYING) | Caller-supplied debugger for the optional pwndbg core-file workflow. |
|
||||
| LLDB / LLVM | [Apache-2.0 with LLVM exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) | Caller-supplied debugger; the installed distribution's component and historical notices also apply. |
|
||||
| mitmproxy | [MIT](https://github.com/mitmproxy/mitmproxy/blob/6c09d56e4c29a92f5ad01b03199977584b8ea14f/LICENSE) | 12.2.3; separately supplied network capture proxy. |
|
||||
| Android Debug Bridge (adb) | [Apache-2.0](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/NOTICE) | User-supplied Android device tool. A complete Android SDK download has its own [SDK license terms](https://developer.android.com/studio/terms); adb's source license does not replace them. |
|
||||
| ILSpy / ICSharpCode.Decompiler | [MIT](https://github.com/icsharpcode/ILSpy/blob/master/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs) | Optional separately installed .NET decompiler; not a redistributed REA dependency. |
|
||||
| Project | License and source | REA relationship |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Ghidra | [Apache-2.0](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/LICENSE), with the government-work and component qualifications in its [NOTICE](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/NOTICE) | Bring your own Ghidra; see [supported versions](docs/installation.md#ghidra). Copies of the 12.1.4 [LICENSE](licenses/ghidra-12.1.4-LICENSE.txt), [NOTICE](licenses/ghidra-12.1.4-NOTICE.txt), and [DISCLAIMER](licenses/ghidra-12.1.4-DISCLAIMER.txt) are retained for reference. |
|
||||
| ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. |
|
||||
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
|
||||
| jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. |
|
||||
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.833/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.833/NOTICE) | Bring your own `kuna`; REA verified 1.833 and never downloads, installs or bundles it. REA invokes it as a separate process. |
|
||||
| Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. |
|
||||
| Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. |
|
||||
| Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. |
|
||||
| Wakaru | [Apache-2.0](https://github.com/pionxzh/wakaru/blob/8219bf5016a063d7fb215101a8c45d5959ec733f/LICENSE) | v1.14.0; pinned source and separately obtained JavaScript reconstruction tooling. |
|
||||
| WABT | [Apache-2.0](https://github.com/WebAssembly/wabt/blob/ff0ef7e0009402740c805a9744c09b05be063e48/LICENSE) | v1.0.42; external WebAssembly tools; [integration provenance](third_party/wabt/README.md). |
|
||||
| pwntools | [MIT, with GPL/BSD component qualifications](https://github.com/Gallopsled/pwntools/blob/3ad86ec5adab1abf9781ca7552a6f5d7cff7a33c/LICENSE-pwntools.txt) | 4.15.0; caller-supplied offline ELF analysis package. |
|
||||
| pyelftools | [Public-domain dedication and component notices](https://github.com/eliben/pyelftools/blob/v0.33/LICENSE) | 0.33; includes separately licensed bundled material, including Construct. |
|
||||
| Unicorn | [GPL-2.0 and component notices](https://github.com/unicorn-engine/unicorn/blob/2.1.2/COPYING) | 2.1.2; used by the caller-supplied ELF analysis profile for instruction emulation. |
|
||||
| pwndbg | [MIT](third_party/pwndbg/LICENSE.md) | 2026.09.15, commit `55596aa676a2756efb8d67034f31675170eabe48`; separately obtained debugger extension. |
|
||||
| GNU GDB | [GPL-3.0-or-later](https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/COPYING) | Caller-supplied debugger for the optional pwndbg core-file workflow. |
|
||||
| LLDB / LLVM | [Apache-2.0 with LLVM exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) | Caller-supplied debugger; the installed distribution's component and historical notices also apply. |
|
||||
| mitmproxy | [MIT](https://github.com/mitmproxy/mitmproxy/blob/6c09d56e4c29a92f5ad01b03199977584b8ea14f/LICENSE) | 12.2.3; separately supplied network capture proxy. |
|
||||
| Android Debug Bridge (adb) | [Apache-2.0](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/NOTICE) | User-supplied Android device tool. A complete Android SDK download has its own [SDK license terms](https://developer.android.com/studio/terms); adb's source license does not replace them. |
|
||||
| ILSpy / ICSharpCode.Decompiler | [MIT](https://github.com/icsharpcode/ILSpy/blob/master/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs) | Optional separately installed .NET decompiler; not a redistributed REA dependency. |
|
||||
|
||||
Ghidra's original notice credits software developed at the National Security
|
||||
Agency. It explains that US-government-created portions are not subject to US
|
||||
|
||||
@@ -10,6 +10,7 @@ import jadx.api.plugins.input.insns.Opcode;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.*;
|
||||
import java.io.PrintStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -23,6 +24,10 @@ import kotlin.coroutines.intrinsics.IntrinsicsKt;
|
||||
import kotlinx.coroutines.BuildersKt;
|
||||
import kotlinx.io.CoreKt;
|
||||
import kotlinx.io.JvmCoreKt;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
/** REA-owned metadata boundary for the unmodified jadx-headless-mcp 0.7.1 JAR. */
|
||||
class ReaJadxBridge {
|
||||
@@ -30,10 +35,13 @@ class ReaJadxBridge {
|
||||
private final SessionHolder holder;
|
||||
private JadxSession indexedSession;
|
||||
private Map<String, List<ClassSnapshot>> classes = Map.of();
|
||||
private JadxSession manifestSession;
|
||||
private ManifestSnapshot manifestSnapshot;
|
||||
|
||||
private record MethodSnapshot(String name, String rawName, String shortId, List<String> bodyAbsenceMarkers,
|
||||
Map<String, Object> summary) {}
|
||||
private record ClassSnapshot(JavaClass node, List<MethodSnapshot> methods, Map<String, Object> summary) {}
|
||||
private record ManifestSnapshot(String text, Map<String, Object> summary) {}
|
||||
|
||||
private static final class InputFailure extends IllegalArgumentException {
|
||||
final String field;
|
||||
@@ -93,6 +101,7 @@ class ReaJadxBridge {
|
||||
args.has("threads") ? integer(args, "threads", 1) : null,
|
||||
args.has("code_cache_size") ? integer(args, "code_cache_size", 64) : null,
|
||||
optionalString(args, "resources"));
|
||||
clearManifest();
|
||||
return loadCall(next -> holder.load(path, options, next), continuation);
|
||||
}
|
||||
|
||||
@@ -153,6 +162,77 @@ class ReaJadxBridge {
|
||||
return current;
|
||||
}
|
||||
|
||||
private void clearManifest() {
|
||||
manifestSession = null;
|
||||
manifestSnapshot = null;
|
||||
}
|
||||
|
||||
/** Original archive identity, never a deobfuscated basename or name suffix. */
|
||||
private ManifestSnapshot manifest(JadxSession current) {
|
||||
if (manifestSession != current || manifestSnapshot == null) {
|
||||
ResourceFile selected = null;
|
||||
for (ResourceFile resource : current.getResources()) {
|
||||
if (resource.getType() != ResourceType.MANIFEST
|
||||
|| !resource.getOriginalName().equals("AndroidManifest.xml")) continue;
|
||||
if (selected != null)
|
||||
throw new IllegalArgumentException("Root AndroidManifest.xml resource identity is ambiguous");
|
||||
selected = resource;
|
||||
}
|
||||
if (selected == null) throw new IllegalArgumentException("Root AndroidManifest.xml not found");
|
||||
var content = selected.loadContent();
|
||||
if (content == null || content.getDataType() != jadx.core.xmlgen.ResContainer.DataType.TEXT)
|
||||
throw new IllegalArgumentException("Root AndroidManifest.xml did not decode as text");
|
||||
String xml = content.getText().getCodeStr();
|
||||
ManifestSnapshot snapshot = new ManifestSnapshot(xml, manifestSummary(xml));
|
||||
manifestSnapshot = snapshot;
|
||||
manifestSession = current;
|
||||
}
|
||||
return manifestSnapshot;
|
||||
}
|
||||
|
||||
/** One parsed XML owner supplies namespace-aware values alongside the exact decoded text. */
|
||||
static Map<String, Object> manifestSummary(String xml) {
|
||||
Element root;
|
||||
try {
|
||||
// Match REA's existing JDK XML setup: document types are unsupported.
|
||||
// Parse an in-memory string; XInclude and external DTD/schema I/O stay disabled.
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultNSInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
factory.setXIncludeAware(false);
|
||||
factory.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
factory.setAttribute(javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
|
||||
root = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)))
|
||||
.getDocumentElement();
|
||||
} catch (javax.xml.parsers.ParserConfigurationException | org.xml.sax.SAXException
|
||||
| java.io.IOException failure) {
|
||||
throw new IllegalArgumentException("Root AndroidManifest.xml is not supported well-formed XML", failure);
|
||||
}
|
||||
if (!root.getTagName().equals("manifest") || root.getNamespaceURI() != null)
|
||||
throw new IllegalArgumentException("AndroidManifest.xml root must be an unnamespaced <manifest>");
|
||||
String android = "http://schemas.android.com/apk/res/android";
|
||||
String minSdk = "";
|
||||
String targetSdk = "";
|
||||
List<String> permissions = new ArrayList<>();
|
||||
for (Node child = root.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||
if (!(child instanceof Element element) || element.getNamespaceURI() != null) continue;
|
||||
if (element.getTagName().equals("uses-sdk")) {
|
||||
minSdk = element.getAttributeNS(android, "minSdkVersion");
|
||||
targetSdk = element.getAttributeNS(android, "targetSdkVersion");
|
||||
} else if (element.getTagName().equals("uses-permission")) {
|
||||
String permission = element.getAttributeNS(android, "name");
|
||||
if (!permission.isEmpty()) permissions.add(permission);
|
||||
}
|
||||
}
|
||||
Map<String, Object> summary = new LinkedHashMap<>();
|
||||
summary.put("package", root.getAttribute("package"));
|
||||
summary.put("version_name", root.getAttributeNS(android, "versionName"));
|
||||
summary.put("version_code", root.getAttributeNS(android, "versionCode"));
|
||||
summary.put("min_sdk", minSdk);
|
||||
summary.put("target_sdk", targetSdk);
|
||||
summary.put("permissions", List.copyOf(permissions));
|
||||
return Collections.unmodifiableMap(summary);
|
||||
}
|
||||
|
||||
private ClassSnapshot selected(JsonObject args) {
|
||||
JadxSession current = session();
|
||||
if (indexedSession != current) {
|
||||
@@ -189,12 +269,16 @@ class ReaJadxBridge {
|
||||
"type", field.getType().toString(), "def_pos", 0)).toList();
|
||||
List<String> inner = node.getInnerClasses().stream().map(ClassNode::getFullName).toList();
|
||||
Map<String, Object> summary = Map.of(
|
||||
"full_name", cls.getFullName(), "name", cls.getName(),
|
||||
"full_name", cls.getFullName(), "raw_name", cls.getRawName(), "name", cls.getName(),
|
||||
"method_count", methods.size(), "field_count", fields.size(),
|
||||
"inner_class_count", inner.size(), "inner_classes", inner,
|
||||
"methods", methods.stream().map(MethodSnapshot::summary).toList(), "fields", fields);
|
||||
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>())
|
||||
.add(new ClassSnapshot(cls, methods, summary));
|
||||
ClassSnapshot snapshot = new ClassSnapshot(cls, methods, summary);
|
||||
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>()).add(snapshot);
|
||||
// Keep the binary identity supplied by JADX; '$' can also be a literal
|
||||
// name character, so replacing separators would invent lookup aliases.
|
||||
if (!cls.getRawName().equals(cls.getFullName()))
|
||||
index.computeIfAbsent(cls.getRawName(), ignored -> new ArrayList<>()).add(snapshot);
|
||||
}
|
||||
}
|
||||
classes = Collections.unmodifiableMap(index);
|
||||
@@ -333,6 +417,9 @@ class ReaJadxBridge {
|
||||
"java_version", System.getProperty("java.version"),
|
||||
"metadata_scope", "parsed_members"));
|
||||
JadxSession current = session();
|
||||
if (name.equals("get_app_info")) return json(manifest(current).summary());
|
||||
if (name.equals("get_android_manifest"))
|
||||
return text(TextUtilKt.truncateToBytes(manifest(current).text(), current.getMaxSourceBytes()), false);
|
||||
if (name.equals("list_classes")) {
|
||||
List<String> names = current.getClassFqns();
|
||||
String query = args.has("query") ? string(args, "query") : "";
|
||||
@@ -371,7 +458,7 @@ class ReaJadxBridge {
|
||||
"markers", code.getMarkers(), "body", code.getText()));
|
||||
}
|
||||
List<JavaNode> uses;
|
||||
String target = string(args, "class_name");
|
||||
String target = (String) cls.summary().get("full_name");
|
||||
MethodSnapshot selectedMethod = null;
|
||||
int overloadIndex = 0;
|
||||
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
|
||||
@@ -420,6 +507,17 @@ class ReaJadxBridge {
|
||||
return json(result);
|
||||
}
|
||||
|
||||
private static void registerBridgeTool(Server server, ReaJadxBridge bridge, String name,
|
||||
ToolSchema schema, String description) {
|
||||
Tool tool = new Tool(name, schema, description, null, null, null, null, null, null);
|
||||
server.addTool(tool, (connection, request, continuation) -> {
|
||||
try { return bridge.handle(name, request.getArguments()); }
|
||||
catch (InputFailure error) { return text(JSON.toJson(Map.of(
|
||||
"kind", "invalid-input", "field", error.field, "message", error.getMessage())), true); }
|
||||
catch (IllegalArgumentException error) { return text(error.getMessage(), true); }
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
PrintStream protocolOut = System.out;
|
||||
System.setOut(System.err);
|
||||
@@ -428,10 +526,9 @@ class ReaJadxBridge {
|
||||
SessionHolder holder = new SessionHolder(defaults);
|
||||
ServerCapabilities capabilities = new ServerCapabilities(
|
||||
new ServerCapabilities.Tools(null), null, null, null, null, null, null, Map.of());
|
||||
Server server = new Server(new Implementation("rea-jadx-bridge", "4", null, null, null),
|
||||
Server server = new Server(new Implementation("rea-jadx-bridge", "6", null, null, null),
|
||||
new ServerOptions(capabilities), "", ignored -> Unit.INSTANCE);
|
||||
SessionToolsKt.registerSessionTools(server, holder);
|
||||
ManifestToolsKt.registerManifestTools(server, holder);
|
||||
ClassToolsKt.registerClassTools(server, holder);
|
||||
MethodToolsKt.registerMethodTools(server, holder);
|
||||
XrefToolsKt.registerXrefTools(server, holder);
|
||||
@@ -440,7 +537,7 @@ class ReaJadxBridge {
|
||||
server.removeTool("load_apk");
|
||||
server.addTool(loadTool, (connection, request, continuation) ->
|
||||
bridge.load(request.getArguments(), defaults, continuation));
|
||||
Set<String> consumed = Set.of("load_apk", "get_app_info", "get_android_manifest",
|
||||
Set<String> consumed = Set.of("load_apk",
|
||||
"list_classes", "get_class_summary", "get_method_body",
|
||||
"get_xrefs_to_class", "get_xrefs_to_method");
|
||||
for (String name : new ArrayList<>(server.getTools().keySet()))
|
||||
@@ -449,6 +546,13 @@ class ReaJadxBridge {
|
||||
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(Map.of()), List.of(), null),
|
||||
"Observed worker JVM and metadata scope", null, null, null, null, null, null);
|
||||
server.addTool(diagnostics, (connection, request, continuation) -> bridge.handle("rea_jvm_status", request.getArguments()));
|
||||
for (String name : List.of("get_app_info", "get_android_manifest")) {
|
||||
String description = name.equals("get_app_info")
|
||||
? "Namespace-aware summary of the exact root AndroidManifest.xml resource."
|
||||
: "Exact decoded root AndroidManifest.xml text, bounded by the session source byte limit.";
|
||||
registerBridgeTool(server, bridge, name,
|
||||
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(Map.of()), List.of(), null), description);
|
||||
}
|
||||
for (String name : List.of("list_classes", "get_class_summary",
|
||||
"get_method_body", "get_xrefs_to_class", "get_xrefs_to_method")) {
|
||||
Tool tool = server.getTools().get(name).getTool();
|
||||
@@ -470,14 +574,9 @@ class ReaJadxBridge {
|
||||
case "get_method_body" -> "Source for an exact class and stable same-name overload index; static initializers use smali to preserve folded field initialization.";
|
||||
default -> "Complete static using-node inventory for an exact class or selected method overload; one node may contain multiple invoke instructions, and source lines are unresolved.";
|
||||
};
|
||||
Tool narrowed = new Tool(name, new ToolSchema(null, new kotlinx.serialization.json.JsonObject(properties),
|
||||
tool.getInputSchema().getRequired(), null), description, null, null, null, null, null, null);
|
||||
server.addTool(narrowed, (connection, request, continuation) -> {
|
||||
try { return bridge.handle(name, request.getArguments()); }
|
||||
catch (InputFailure error) { return text(JSON.toJson(Map.of(
|
||||
"kind", "invalid-input", "field", error.field, "message", error.getMessage())), true); }
|
||||
catch (IllegalArgumentException error) { return text(error.getMessage(), true); }
|
||||
});
|
||||
registerBridgeTool(server, bridge, name,
|
||||
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(properties),
|
||||
tool.getInputSchema().getRequired(), null), description);
|
||||
}
|
||||
CountDownLatch done = new CountDownLatch(1);
|
||||
server.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
|
||||
@@ -503,6 +602,7 @@ class ReaJadxBridge {
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> server.close(continuation));
|
||||
} finally {
|
||||
bridge.clearManifest();
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> holder.unload(continuation));
|
||||
}
|
||||
|
||||
+604
-99
@@ -11,6 +11,7 @@ import json
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
import re
|
||||
import secrets
|
||||
import socket
|
||||
import stat
|
||||
@@ -33,9 +34,151 @@ except ImportError:
|
||||
from PySide2.QtCore import QCoreApplication, QObject, Signal
|
||||
|
||||
|
||||
MAX_FRAME_BYTES = 16 * 1024 * 1024
|
||||
# Request and response frame budget in bytes, excluding the trailing LF.
|
||||
# REA_BRIDGE_MAX_REQUEST_BYTES selects it. Unset stays 16777216.
|
||||
# 7464 is the authenticated frame of the largest bounded symbol name.
|
||||
# The 64-receipt recovery status is smaller with current_file null. A path
|
||||
# that fits is kept. A path that does not fit is null, not the descriptor path.
|
||||
# 536870887 is Node's 536870888-character string ceiling minus the framing LF.
|
||||
# The ceiling is the encoded payload; JSON expansion is measured before it.
|
||||
# A fitting response is signed from bounded JSON pieces and written in 64 KiB
|
||||
# chunks, so this maximum does not retain several copies of the frame.
|
||||
_BRIDGE_MAX_REQUEST_BYTES_SETTING = "REA_BRIDGE_MAX_REQUEST_BYTES"
|
||||
DEFAULT_BRIDGE_MAX_REQUEST_BYTES = 16 * 1024 * 1024
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES = 7464
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES = 536870887
|
||||
# The outer authenticated frame around the escaped JSON payload, excluding LF.
|
||||
_AUTHENTICATED_PREFIX = b'{"response":"'
|
||||
_AUTHENTICATED_PROOF_MARK = b'","proof":"'
|
||||
_PROOF_HEX_LENGTH = 64
|
||||
_AUTHENTICATED_FRAME_OVERHEAD = (
|
||||
len(_AUTHENTICATED_PREFIX)
|
||||
+ len(_AUTHENTICATED_PROOF_MARK)
|
||||
+ _PROOF_HEX_LENGTH
|
||||
+ len(b'"}')
|
||||
)
|
||||
_AUTHENTICATED_WRITE_BUFFER_BYTES = 64 * 1024
|
||||
# Raw JSON is produced in pieces no larger than this many characters.
|
||||
# One character expands to at most six JSON bytes (\u00XX) before the outer
|
||||
# string escape, so a piece stays far below the frame ceiling.
|
||||
_JSON_CHUNK_CHARS = 64 * 1024
|
||||
# Fixed admission cap. It does not follow REA_BRIDGE_MAX_REQUEST_BYTES.
|
||||
# A further accept is closed. Starting a worker must not stop the listener.
|
||||
MAX_CONNECTION_WORKERS = 8
|
||||
|
||||
|
||||
def _close_socket(connection: socket.socket) -> None:
|
||||
try:
|
||||
connection.close()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def _bridge_max_request_bytes_constraint():
|
||||
return (
|
||||
"REA_BRIDGE_MAX_REQUEST_BYTES must be a positive decimal integer "
|
||||
"from %d to %d bytes; the default is %d bytes (16 MiB). "
|
||||
"A value below %d bytes cannot carry the largest admitted "
|
||||
"symbol-name command, and a larger value is refused so the encoded "
|
||||
"payload plus its framing LF stays one string under Node's "
|
||||
"single-string ceiling."
|
||||
% (
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _parse_bridge_max_request_bytes(raw):
|
||||
"""Refuse a non-decimal or out-of-range ceiling. Never fall back."""
|
||||
constraint = _bridge_max_request_bytes_constraint()
|
||||
if isinstance(raw, bool):
|
||||
raise RuntimeError(constraint)
|
||||
if isinstance(raw, int):
|
||||
value = raw
|
||||
elif isinstance(raw, str):
|
||||
# ASCII digits only. str.isdigit() accepts numerals such as U+0668
|
||||
# that the TypeScript setting rejects.
|
||||
if re.fullmatch(r"[1-9][0-9]*", raw) is None:
|
||||
raise RuntimeError(constraint)
|
||||
value = int(raw)
|
||||
else:
|
||||
raise RuntimeError(constraint)
|
||||
if (
|
||||
value < MINIMUM_BRIDGE_MAX_REQUEST_BYTES
|
||||
or value > MAXIMUM_BRIDGE_MAX_REQUEST_BYTES
|
||||
):
|
||||
raise RuntimeError(constraint)
|
||||
return value
|
||||
|
||||
|
||||
def _resolve_bridge_max_request_bytes():
|
||||
"""Prefer an injected global, then the process environment, then 16 MiB."""
|
||||
injected = globals().get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
|
||||
if injected is not None:
|
||||
return _parse_bridge_max_request_bytes(injected), "setting"
|
||||
raw = os.environ.get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
|
||||
if raw is None:
|
||||
return DEFAULT_BRIDGE_MAX_REQUEST_BYTES, "default"
|
||||
return _parse_bridge_max_request_bytes(raw), "setting"
|
||||
|
||||
|
||||
MAX_FRAME_BYTES, BRIDGE_MAX_REQUEST_BYTES_SOURCE = (
|
||||
_resolve_bridge_max_request_bytes()
|
||||
)
|
||||
|
||||
|
||||
def _bridge_max_request_bytes_recovery():
|
||||
return (
|
||||
"The effective REA_BRIDGE_MAX_REQUEST_BYTES value is %d bytes "
|
||||
"(source %s; default %d, floor %d, maximum %d). "
|
||||
"Set REA_BRIDGE_MAX_REQUEST_BYTES in the environment that starts REA "
|
||||
"to a decimal integer from %d to %d, then restart REA. "
|
||||
"Restart Hopper or Cutter from that same environment so the bridge "
|
||||
"uses this ceiling."
|
||||
% (
|
||||
MAX_FRAME_BYTES,
|
||||
BRIDGE_MAX_REQUEST_BYTES_SOURCE,
|
||||
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _frame_limit_exceeded():
|
||||
return ValueError(
|
||||
"frame-limit-exceeded: Bridge request exceeds the %d-byte payload limit. %s"
|
||||
% (MAX_FRAME_BYTES, _bridge_max_request_bytes_recovery())
|
||||
)
|
||||
|
||||
|
||||
SOCKET_TIMEOUT_SECONDS = 20
|
||||
AUTH_FRAME_TIMEOUT_SECONDS = 1
|
||||
# The client request deadline. The authenticated frame read may use up to
|
||||
# this long; it does not grow past it when the byte ceiling is raised.
|
||||
REQUEST_DEADLINE_SECONDS = 25
|
||||
|
||||
|
||||
def _authenticated_frame_timeout_seconds():
|
||||
"""Time to hash and transfer one maximum-sized authenticated frame.
|
||||
|
||||
The hello frame stays at one second. The following command frame is
|
||||
built only after that challenge, so its read budget follows the
|
||||
configured payload size, at one second per mebibyte, and never exceeds
|
||||
the 25 second client deadline.
|
||||
"""
|
||||
mebibytes = (MAX_FRAME_BYTES + (1024 * 1024) - 1) // (1024 * 1024)
|
||||
return min(
|
||||
REQUEST_DEADLINE_SECONDS,
|
||||
max(AUTH_FRAME_TIMEOUT_SECONDS, mebibytes),
|
||||
)
|
||||
|
||||
|
||||
MAX_DESCRIPTOR_BYTES = 64 * 1024
|
||||
MAX_COMPLETION_RECEIPTS = 64
|
||||
|
||||
@@ -480,6 +623,17 @@ class _BridgeServer(threading.Thread):
|
||||
self.token = secrets.token_urlsafe(32)
|
||||
self.poisoned = False
|
||||
self.connection_ordinal = 0
|
||||
# The client proof is inside the authenticated frame, so that read
|
||||
# cannot start after proof. It runs off the accept loop. One
|
||||
# authenticated command still runs at a time. completed_connection
|
||||
# is the longest prefix of these ordinals whose handlers have
|
||||
# terminated, not "this status ordinal minus one".
|
||||
self._command_lock = threading.Lock()
|
||||
self._connections_lock = threading.Lock()
|
||||
self._connections: set[socket.socket] = set()
|
||||
self._workers: list[threading.Thread] = []
|
||||
self._finished_through = 0
|
||||
self._finished_ordinals: set[int] = set()
|
||||
self.directory = _bridge_directory()
|
||||
self.descriptor = self.directory / f"cutter-{os.getpid()}-{self.session_id}.json"
|
||||
|
||||
@@ -500,6 +654,7 @@ class _BridgeServer(threading.Thread):
|
||||
"current_file": self.dispatcher.current_file,
|
||||
"cutter_version": self.dispatcher.cutter_version,
|
||||
"identity_status": "partial",
|
||||
"max_request_bytes": MAX_FRAME_BYTES,
|
||||
}
|
||||
if os.name == "nt":
|
||||
_write_windows_descriptor(self.directory, self.descriptor, json.dumps(payload).encode("utf-8"))
|
||||
@@ -541,8 +696,46 @@ class _BridgeServer(threading.Thread):
|
||||
continue
|
||||
except OSError:
|
||||
break
|
||||
with self._connections_lock:
|
||||
self._workers = [
|
||||
worker for worker in self._workers if worker.is_alive()
|
||||
]
|
||||
at_capacity = len(self._workers) >= MAX_CONNECTION_WORKERS
|
||||
if at_capacity:
|
||||
_close_socket(connection)
|
||||
continue
|
||||
self.connection_ordinal += 1
|
||||
connection_ordinal = self.connection_ordinal
|
||||
worker = threading.Thread(
|
||||
target=self._serve_connection,
|
||||
args=(connection, connection_ordinal),
|
||||
name="rea-cutter-bridge-connection",
|
||||
daemon=True,
|
||||
)
|
||||
with self._connections_lock:
|
||||
self._connections.add(connection)
|
||||
self._workers.append(worker)
|
||||
try:
|
||||
worker.start()
|
||||
except RuntimeError:
|
||||
with self._connections_lock:
|
||||
self._connections.discard(connection)
|
||||
if worker in self._workers:
|
||||
self._workers.remove(worker)
|
||||
self._note_connection_finished(connection_ordinal)
|
||||
_close_socket(connection)
|
||||
with self._connections_lock:
|
||||
workers = list(self._workers)
|
||||
for worker in workers:
|
||||
worker.join(timeout=1)
|
||||
self.server.close()
|
||||
try:
|
||||
self.descriptor.unlink(missing_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def _serve_connection(self, connection: socket.socket, connection_ordinal: int) -> None:
|
||||
try:
|
||||
with connection:
|
||||
connection.settimeout(AUTH_FRAME_TIMEOUT_SECONDS)
|
||||
authenticated = False
|
||||
@@ -550,60 +743,73 @@ class _BridgeServer(threading.Thread):
|
||||
hello = self._receive_frame(connection)
|
||||
if not isinstance(hello, dict) or hello.get("kind") != "rea-auth-v2" or not isinstance(hello.get("nonce"), str) or len(hello["nonce"]) != 64:
|
||||
self._send(connection, {"ok": False, "error": "authentication-failed"})
|
||||
continue
|
||||
return
|
||||
client_nonce = hello["nonce"]
|
||||
server_nonce = f"{connection_ordinal:016x}{secrets.token_hex(24)}"
|
||||
server_proof = hmac.new(self.token.encode(), f"rea-cutter-server-v2:{self.session_id}:{client_nonce}:{server_nonce}".encode(), hashlib.sha256).hexdigest()
|
||||
self._send(connection, {"session_id": self.session_id, "nonce": server_nonce, "proof": server_proof})
|
||||
authenticated_frame = self._receive_frame(connection)
|
||||
authenticated_frame = self._receive_frame(
|
||||
connection, _authenticated_frame_timeout_seconds()
|
||||
)
|
||||
request_bytes = authenticated_frame.get("request") if isinstance(authenticated_frame, dict) else None
|
||||
expected_proof = hmac.new(self.token.encode(), f"rea-cutter-client-v2:{self.session_id}:{client_nonce}:{server_nonce}:{request_bytes}".encode(), hashlib.sha256).hexdigest()
|
||||
if not isinstance(request_bytes, str) or not isinstance(authenticated_frame.get("proof"), str) or not hmac.compare_digest(authenticated_frame["proof"], expected_proof):
|
||||
presented_proof = authenticated_frame.get("proof") if isinstance(authenticated_frame, dict) else None
|
||||
if not isinstance(request_bytes, str) or not isinstance(presented_proof, str):
|
||||
self._send(connection, {"ok": False, "error": "authentication-failed"})
|
||||
continue
|
||||
return
|
||||
# Update the prefix and the request separately. One joined
|
||||
# preimage would retain another copy of a maximum-sized command.
|
||||
expected_proof = hmac.new(self.token.encode(), digestmod=hashlib.sha256)
|
||||
expected_proof.update(
|
||||
f"rea-cutter-client-v2:{self.session_id}:{client_nonce}:{server_nonce}:".encode()
|
||||
)
|
||||
expected_proof.update(request_bytes.encode())
|
||||
if not hmac.compare_digest(presented_proof, expected_proof.hexdigest()):
|
||||
self._send(connection, {"ok": False, "error": "authentication-failed"})
|
||||
return
|
||||
request = json.loads(request_bytes)
|
||||
if not isinstance(request, dict) or request.get("session_id") != self.session_id:
|
||||
self._send(connection, {"ok": False, "error": "authentication-failed"})
|
||||
continue
|
||||
return
|
||||
authenticated = True
|
||||
connection.settimeout(SOCKET_TIMEOUT_SECONDS)
|
||||
if self.poisoned and request.get("kind") not in ("status", "ack"):
|
||||
self._send_authenticated(connection, {
|
||||
"ok": False,
|
||||
"error": "session-poisoned-after-ui-timeout",
|
||||
"execution_state": "not_started",
|
||||
}, client_nonce, server_nonce)
|
||||
continue
|
||||
if (
|
||||
request.get("kind") == "command"
|
||||
and not self.dispatcher.can_accept_command()
|
||||
):
|
||||
self._send_authenticated(connection, {
|
||||
"ok": False,
|
||||
"error": "completion-receipt-capacity-exceeded",
|
||||
"execution_state": "not_started",
|
||||
}, client_nonce, server_nonce)
|
||||
continue
|
||||
request["session_id"] = self.session_id
|
||||
event = threading.Event()
|
||||
cancelled = threading.Event()
|
||||
reply: dict[str, Any] = {}
|
||||
request["_event"] = event
|
||||
request["_cancelled"] = cancelled
|
||||
self.dispatcher.dispatch.emit(request, reply)
|
||||
if not event.wait(SOCKET_TIMEOUT_SECONDS):
|
||||
cancelled.set()
|
||||
self.poisoned = True
|
||||
self._send_authenticated(connection, {
|
||||
"ok": False,
|
||||
"error": "ui-thread-request-timeout",
|
||||
"execution_state": "unknown",
|
||||
"message": "The UI operation may still complete after this timeout; restart Cutter before issuing further commands.",
|
||||
}, client_nonce, server_nonce)
|
||||
else:
|
||||
if request.get("kind") == "status" and reply.get("ok") is True:
|
||||
reply["completed_connection"] = f"{connection_ordinal - 1:016x}"
|
||||
self._send_authenticated(connection, reply, client_nonce, server_nonce)
|
||||
with self._command_lock:
|
||||
connection.settimeout(SOCKET_TIMEOUT_SECONDS)
|
||||
if self.poisoned and request.get("kind") not in ("status", "ack"):
|
||||
self._send_authenticated(connection, {
|
||||
"ok": False,
|
||||
"error": "session-poisoned-after-ui-timeout",
|
||||
"execution_state": "not_started",
|
||||
}, client_nonce, server_nonce)
|
||||
return
|
||||
if (
|
||||
request.get("kind") == "command"
|
||||
and not self.dispatcher.can_accept_command()
|
||||
):
|
||||
self._send_authenticated(connection, {
|
||||
"ok": False,
|
||||
"error": "completion-receipt-capacity-exceeded",
|
||||
"execution_state": "not_started",
|
||||
}, client_nonce, server_nonce)
|
||||
return
|
||||
request["session_id"] = self.session_id
|
||||
event = threading.Event()
|
||||
cancelled = threading.Event()
|
||||
reply: dict[str, Any] = {}
|
||||
request["_event"] = event
|
||||
request["_cancelled"] = cancelled
|
||||
self.dispatcher.dispatch.emit(request, reply)
|
||||
if not event.wait(SOCKET_TIMEOUT_SECONDS):
|
||||
cancelled.set()
|
||||
self.poisoned = True
|
||||
self._send_authenticated(connection, {
|
||||
"ok": False,
|
||||
"error": "ui-thread-request-timeout",
|
||||
"execution_state": "unknown",
|
||||
"message": "The UI operation may still complete after this timeout; restart Cutter before issuing further commands.",
|
||||
}, client_nonce, server_nonce)
|
||||
else:
|
||||
if request.get("kind") == "status" and reply.get("ok") is True:
|
||||
reply["completed_connection"] = self._completion_watermark()
|
||||
self._send_authenticated(connection, reply, client_nonce, server_nonce)
|
||||
except socket.timeout:
|
||||
if authenticated:
|
||||
self._send_authenticated(connection, {
|
||||
@@ -614,77 +820,217 @@ class _BridgeServer(threading.Thread):
|
||||
}, client_nonce, server_nonce)
|
||||
except Exception as error:
|
||||
self._send(connection, {"ok": False, "error": str(error)})
|
||||
self.server.close()
|
||||
try:
|
||||
self.descriptor.unlink(missing_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
finally:
|
||||
self._note_connection_finished(connection_ordinal)
|
||||
with self._connections_lock:
|
||||
self._connections.discard(connection)
|
||||
|
||||
def _note_connection_finished(self, ordinal: int) -> None:
|
||||
"""Record one accepted connection as terminated.
|
||||
|
||||
The watermark is the longest prefix 1..W that has terminated. A later
|
||||
status cannot report an earlier connection that is still reading or
|
||||
still waiting to run.
|
||||
"""
|
||||
with self._connections_lock:
|
||||
if ordinal <= self._finished_through or ordinal in self._finished_ordinals:
|
||||
return
|
||||
self._finished_ordinals.add(ordinal)
|
||||
while self._finished_through + 1 in self._finished_ordinals:
|
||||
self._finished_through += 1
|
||||
self._finished_ordinals.discard(self._finished_through)
|
||||
|
||||
def _completion_watermark(self) -> str:
|
||||
with self._connections_lock:
|
||||
return f"{self._finished_through:016x}"
|
||||
|
||||
@staticmethod
|
||||
def _receive_frame(connection: socket.socket) -> dict[str, Any]:
|
||||
def _receive_frame(
|
||||
connection: socket.socket,
|
||||
timeout_seconds: float = AUTH_FRAME_TIMEOUT_SECONDS,
|
||||
) -> dict[str, Any]:
|
||||
data = bytearray()
|
||||
deadline = time.monotonic() + AUTH_FRAME_TIMEOUT_SECONDS
|
||||
while b"\n" not in data:
|
||||
if len(data) >= MAX_FRAME_BYTES:
|
||||
raise ValueError("frame-limit-exceeded")
|
||||
deadline = time.monotonic() + timeout_seconds
|
||||
newline = -1
|
||||
while newline < 0:
|
||||
if len(data) > MAX_FRAME_BYTES:
|
||||
raise _frame_limit_exceeded()
|
||||
remaining = deadline - time.monotonic()
|
||||
if remaining <= 0:
|
||||
raise socket.timeout("authentication frame timed out")
|
||||
connection.settimeout(remaining)
|
||||
# MAX_FRAME_BYTES limits the JSON payload; the terminating LF is
|
||||
# framing and may be the one byte beyond that payload budget.
|
||||
part = connection.recv(min(65536, MAX_FRAME_BYTES + 1 - len(data)))
|
||||
if not part:
|
||||
raise ValueError("incomplete authentication frame")
|
||||
scanned = len(data)
|
||||
data.extend(part)
|
||||
line, remainder = bytes(data).split(b"\n", 1)
|
||||
if len(line) > MAX_FRAME_BYTES or remainder:
|
||||
# The delimiter is one byte, so it cannot straddle a chunk.
|
||||
# Search only the bytes just received.
|
||||
newline = data.find(b"\n", scanned)
|
||||
if newline > MAX_FRAME_BYTES:
|
||||
raise _frame_limit_exceeded()
|
||||
if newline != len(data) - 1:
|
||||
raise ValueError("invalid framed request")
|
||||
value = json.loads(line)
|
||||
value = json.loads(bytes(data[:newline]))
|
||||
return value if isinstance(value, dict) else {}
|
||||
|
||||
def _send_authenticated(self, connection: socket.socket, value: dict[str, Any], client_nonce: str, server_nonce: str) -> None:
|
||||
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
|
||||
if encoded is None:
|
||||
value = self._truncated_response(value)
|
||||
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
|
||||
if encoded is None:
|
||||
# This fixed-size fallback is checked through the same signed framing path.
|
||||
value = {
|
||||
"ok": False,
|
||||
"error": "response-limit-exceeded",
|
||||
"execution_state": value.get("execution_state", "unknown"),
|
||||
"output": None,
|
||||
"output_truncated": True,
|
||||
"message": "The result exceeded the bridge frame limit; command completion state is preserved.",
|
||||
}
|
||||
encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
|
||||
if encoded is None:
|
||||
if self._deliver_authenticated(connection, value, client_nonce, server_nonce):
|
||||
return
|
||||
try:
|
||||
connection.sendall(encoded)
|
||||
except OSError:
|
||||
pass
|
||||
# Keep the recovery payload. Include the observed path when it
|
||||
# fits. An explicit null path means unknown, so the client does
|
||||
# not pair this generation with the descriptor's older path.
|
||||
bounded = self._bounded_status_response(value)
|
||||
if bounded is not None:
|
||||
observed_path = value.get("current_file")
|
||||
if isinstance(observed_path, str) and observed_path:
|
||||
with_path = dict(bounded)
|
||||
with_path["current_file"] = observed_path
|
||||
with_path.pop("message", None)
|
||||
if self._deliver_authenticated(
|
||||
connection, with_path, client_nonce, server_nonce
|
||||
):
|
||||
return
|
||||
if self._deliver_authenticated(
|
||||
connection, bounded, client_nonce, server_nonce
|
||||
):
|
||||
return
|
||||
command = self._bounded_command_response(value)
|
||||
if command is not None:
|
||||
observed_path = value.get("current_file")
|
||||
if isinstance(observed_path, str) and observed_path:
|
||||
with_path = dict(command)
|
||||
with_path["current_file"] = observed_path
|
||||
with_path.pop("message", None)
|
||||
if self._deliver_authenticated(
|
||||
connection, with_path, client_nonce, server_nonce
|
||||
):
|
||||
return
|
||||
if self._deliver_authenticated(
|
||||
connection, command, client_nonce, server_nonce
|
||||
):
|
||||
return
|
||||
value = self._truncated_response(value)
|
||||
if self._deliver_authenticated(connection, value, client_nonce, server_nonce):
|
||||
return
|
||||
# This fixed-size fallback is checked through the same signed framing path.
|
||||
self._deliver_authenticated(connection, {
|
||||
"ok": False,
|
||||
"error": "response-limit-exceeded",
|
||||
"execution_state": value.get("execution_state", "unknown"),
|
||||
"output": None,
|
||||
"output_truncated": True,
|
||||
"message": "The result exceeded the bridge frame limit; command completion state is preserved.",
|
||||
}, client_nonce, server_nonce)
|
||||
|
||||
def _encode_authenticated_response(self, value: dict[str, Any], client_nonce: str, server_nonce: str) -> bytes | None:
|
||||
def _deliver_authenticated(
|
||||
self, connection: socket.socket, value: dict[str, Any], client_nonce: str, server_nonce: str
|
||||
) -> bool:
|
||||
proof = self._authenticated_proof(value, client_nonce, server_nonce)
|
||||
if proof is None:
|
||||
return False
|
||||
try:
|
||||
self._write_authenticated_frame(connection, value, proof)
|
||||
except OSError:
|
||||
# A disconnected caller must not fall through to another payload.
|
||||
pass
|
||||
return True
|
||||
|
||||
def _authenticated_proof(
|
||||
self, value: dict[str, Any], client_nonce: str, server_nonce: str
|
||||
) -> str | None:
|
||||
if not _within_response_limit(value):
|
||||
return None
|
||||
prefix = b'{"response":"'
|
||||
suffix = b'","proof":"' + (b"0" * 64) + b'"}\n'
|
||||
frame_size = len(prefix) + len(suffix)
|
||||
response_parts: list[bytes] = []
|
||||
escaped_parts: list[bytes] = []
|
||||
for fragment in json.JSONEncoder(ensure_ascii=False, separators=(",", ":")).iterencode(value):
|
||||
raw = fragment.encode("utf-8")
|
||||
escaped = json.dumps(fragment, ensure_ascii=False)[1:-1].encode("utf-8")
|
||||
frame_size += len(escaped)
|
||||
# The payload budget excludes the terminating LF. Fragments are
|
||||
# measured and signed here, then discarded. The write pass encodes
|
||||
# them again so a maximum-sized reply is not held in several copies.
|
||||
frame_size = _AUTHENTICATED_FRAME_OVERHEAD
|
||||
proof = hmac.new(self.token.encode(), digestmod=hashlib.sha256)
|
||||
proof.update(
|
||||
f"rea-cutter-response-v2:{self.session_id}:{client_nonce}:{server_nonce}:".encode()
|
||||
)
|
||||
for fragment in _response_fragments(value):
|
||||
frame_size += len(_escape_json_fragment(fragment))
|
||||
if frame_size > MAX_FRAME_BYTES:
|
||||
return None
|
||||
response_parts.append(raw)
|
||||
escaped_parts.append(escaped)
|
||||
proof = hmac.new(self.token.encode(), digestmod=hashlib.sha256)
|
||||
proof.update(f"rea-cutter-response-v2:{self.session_id}:{client_nonce}:{server_nonce}:".encode())
|
||||
for part in response_parts:
|
||||
proof.update(part)
|
||||
return prefix + b"".join(escaped_parts) + b'","proof":"' + proof.hexdigest().encode() + b'"}\n'
|
||||
proof.update(fragment.encode("utf-8"))
|
||||
return proof.hexdigest()
|
||||
|
||||
def _encode_authenticated_response(
|
||||
self, value: dict[str, Any], client_nonce: str, server_nonce: str
|
||||
) -> bytes | None:
|
||||
"""Return one exact frame. The socket path writes the same pieces."""
|
||||
proof = self._authenticated_proof(value, client_nonce, server_nonce)
|
||||
if proof is None:
|
||||
return None
|
||||
return b"".join(self._iter_authenticated_frame(value, proof))
|
||||
|
||||
def _iter_authenticated_frame(self, value: dict[str, Any], proof: str):
|
||||
yield _AUTHENTICATED_PREFIX
|
||||
for fragment in _response_fragments(value):
|
||||
yield _escape_json_fragment(fragment)
|
||||
yield _AUTHENTICATED_PROOF_MARK + proof.encode("ascii") + b'"}\n'
|
||||
|
||||
def _write_authenticated_frame(
|
||||
self, connection: socket.socket, value: dict[str, Any], proof: str
|
||||
) -> None:
|
||||
pending = bytearray()
|
||||
for chunk in self._iter_authenticated_frame(value, proof):
|
||||
pending.extend(chunk)
|
||||
if len(pending) >= _AUTHENTICATED_WRITE_BUFFER_BYTES:
|
||||
connection.sendall(pending)
|
||||
pending.clear()
|
||||
if pending:
|
||||
connection.sendall(pending)
|
||||
|
||||
@staticmethod
|
||||
def _bounded_status_response(value: dict[str, Any]) -> dict[str, Any] | None:
|
||||
if "completed_commands" not in value and "completed_connection" not in value:
|
||||
return None
|
||||
bounded: dict[str, Any] = {
|
||||
"ok": value.get("ok") is True,
|
||||
"session_id": value.get("session_id"),
|
||||
"document_generation": value.get("document_generation"),
|
||||
"identity_status": value.get("identity_status", "partial"),
|
||||
"completed_commands": value.get("completed_commands"),
|
||||
"current_file": None,
|
||||
"message": (
|
||||
"current_file is unknown because the status frame exceeded "
|
||||
"REA_BRIDGE_MAX_REQUEST_BYTES. Do not reuse an older descriptor "
|
||||
"path with this document_generation."
|
||||
),
|
||||
}
|
||||
if "completed_connection" in value:
|
||||
bounded["completed_connection"] = value["completed_connection"]
|
||||
return bounded
|
||||
|
||||
@staticmethod
|
||||
def _bounded_command_response(value: dict[str, Any]) -> dict[str, Any] | None:
|
||||
if "execution_state" not in value and "document_generation" not in value:
|
||||
return None
|
||||
bounded: dict[str, Any] = {
|
||||
"ok": value.get("ok") is True,
|
||||
"output": None,
|
||||
"execution_state": value.get("execution_state", "unknown"),
|
||||
"output_truncated": True,
|
||||
"current_file": None,
|
||||
"message": (
|
||||
"current_file is unknown because the result frame exceeded "
|
||||
"REA_BRIDGE_MAX_REQUEST_BYTES. The command completion state and "
|
||||
"document_generation are preserved. Do not reuse an older "
|
||||
"descriptor path with this document_generation."
|
||||
),
|
||||
}
|
||||
generation = value.get("document_generation")
|
||||
if isinstance(generation, int) and not isinstance(generation, bool):
|
||||
bounded["document_generation"] = generation
|
||||
if value.get("identity_status") == "partial":
|
||||
bounded["identity_status"] = "partial"
|
||||
if value.get("ok") is not True and isinstance(value.get("error"), str):
|
||||
bounded["error"] = value["error"]
|
||||
return bounded
|
||||
|
||||
@staticmethod
|
||||
def _truncated_response(value: dict[str, Any]) -> dict[str, Any]:
|
||||
@@ -742,6 +1088,17 @@ class _BridgeServer(threading.Thread):
|
||||
self.server.close()
|
||||
except OSError:
|
||||
pass
|
||||
with self._connections_lock:
|
||||
connections = list(self._connections)
|
||||
for connection in connections:
|
||||
try:
|
||||
connection.shutdown(socket.SHUT_RDWR)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
connection.close()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
class ReaCutterPlugin(cutter.CutterPlugin):
|
||||
@@ -775,6 +1132,128 @@ def create_cutter_plugin() -> ReaCutterPlugin:
|
||||
return ReaCutterPlugin()
|
||||
|
||||
|
||||
def _json_object_key(key: Any) -> str:
|
||||
"""Match CPython's JSON object-key text before quoting."""
|
||||
if isinstance(key, str):
|
||||
return key
|
||||
if key is True:
|
||||
return "true"
|
||||
if key is False:
|
||||
return "false"
|
||||
if key is None:
|
||||
return "null"
|
||||
if isinstance(key, int):
|
||||
return str(key)
|
||||
if isinstance(key, float):
|
||||
return json.dumps(key, separators=(",", ":"))
|
||||
raise TypeError("keys must be str, int, float, bool or None")
|
||||
|
||||
|
||||
def _response_fragments(value: Any):
|
||||
"""Yield compact JSON pieces whose concatenation is the encoded value.
|
||||
|
||||
String contents are sliced before encoding so a large output never becomes
|
||||
one frame-sized fragment. Each piece is at most ``_JSON_CHUNK_CHARS``
|
||||
characters, or the six-byte ``\\u00XX`` expansion of one such slice.
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
yield '"'
|
||||
for start in range(0, len(value), _JSON_CHUNK_CHARS):
|
||||
yield json.dumps(
|
||||
value[start:start + _JSON_CHUNK_CHARS], ensure_ascii=False
|
||||
)[1:-1]
|
||||
yield '"'
|
||||
return
|
||||
if isinstance(value, dict):
|
||||
yield "{"
|
||||
first = True
|
||||
for key, item in value.items():
|
||||
if not first:
|
||||
yield ","
|
||||
first = False
|
||||
yield from _response_fragments(_json_object_key(key))
|
||||
yield ":"
|
||||
yield from _response_fragments(item)
|
||||
yield "}"
|
||||
return
|
||||
if isinstance(value, (list, tuple)):
|
||||
yield from _list_fragments(value)
|
||||
return
|
||||
token = _scalar_token(value)
|
||||
if token is None:
|
||||
raise TypeError("response value is not JSON")
|
||||
yield token
|
||||
|
||||
|
||||
def _scalar_token(value: Any) -> str | None:
|
||||
"""Compact JSON for one scalar. None means the value needs structure."""
|
||||
if type(value) is int:
|
||||
return str(value)
|
||||
if value is None:
|
||||
return "null"
|
||||
if value is True:
|
||||
return "true"
|
||||
if value is False:
|
||||
return "false"
|
||||
if isinstance(value, float):
|
||||
return json.dumps(value, separators=(",", ":"))
|
||||
return None
|
||||
|
||||
|
||||
def _list_fragments(value: list[Any] | tuple[Any, ...]):
|
||||
"""Encode a list, batching scalars into bounded pieces.
|
||||
|
||||
One ``json.dumps`` per number makes a dense array miss the client
|
||||
deadline. A batch is at most ``_JSON_CHUNK_CHARS`` of compact JSON.
|
||||
"""
|
||||
yield "["
|
||||
first = True
|
||||
batch: list[str] = []
|
||||
batch_len = 0
|
||||
|
||||
def flush_batch() -> str | None:
|
||||
nonlocal first, batch, batch_len
|
||||
if not batch:
|
||||
return None
|
||||
piece = ",".join(batch)
|
||||
batch = []
|
||||
batch_len = 0
|
||||
if first:
|
||||
first = False
|
||||
return piece
|
||||
return "," + piece
|
||||
|
||||
for item in value:
|
||||
token = _scalar_token(item)
|
||||
if token is None:
|
||||
emitted = flush_batch()
|
||||
if emitted is not None:
|
||||
yield emitted
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
yield ","
|
||||
yield from _response_fragments(item)
|
||||
continue
|
||||
extra = len(token) if not batch else len(token) + 1
|
||||
if batch and batch_len + extra > _JSON_CHUNK_CHARS:
|
||||
emitted = flush_batch()
|
||||
if emitted is not None:
|
||||
yield emitted
|
||||
extra = len(token)
|
||||
batch.append(token)
|
||||
batch_len += extra
|
||||
emitted = flush_batch()
|
||||
if emitted is not None:
|
||||
yield emitted
|
||||
yield "]"
|
||||
|
||||
|
||||
def _escape_json_fragment(fragment: str) -> bytes:
|
||||
"""Escape one raw JSON piece for the outer response string."""
|
||||
return json.dumps(fragment, ensure_ascii=False)[1:-1].encode("utf-8")
|
||||
|
||||
|
||||
def _within_response_limit(value: Any) -> bool:
|
||||
pending = [value]
|
||||
seen: set[int] = set()
|
||||
@@ -798,13 +1277,22 @@ def _within_response_limit(value: Any) -> bool:
|
||||
# Unencodable output also needs the signed omission fallback.
|
||||
return False
|
||||
size += current.count('"') + current.count("\\")
|
||||
size += sum(5 for character in current if ord(character) < 0x20)
|
||||
# A short escape is two JSON bytes and was already counted as one.
|
||||
# Other controls expand to a six-byte \\u00XX sequence.
|
||||
size += sum(
|
||||
1 if ord(character) in (0x08, 0x09, 0x0A, 0x0C, 0x0D) else 5
|
||||
for character in current
|
||||
if ord(character) < 0x20
|
||||
)
|
||||
elif isinstance(current, dict):
|
||||
identity = id(current)
|
||||
if identity in seen:
|
||||
return False
|
||||
seen.add(identity)
|
||||
size += 2 + 4 * len(current)
|
||||
count = len(current)
|
||||
# Braces, one colon per entry, and commas between entries.
|
||||
# Keys and values are counted when they are visited.
|
||||
size += 2 + count + max(0, count - 1)
|
||||
if size > MAX_FRAME_BYTES:
|
||||
return False
|
||||
pending.extend(current.keys())
|
||||
@@ -814,12 +1302,29 @@ def _within_response_limit(value: Any) -> bool:
|
||||
if identity in seen:
|
||||
return False
|
||||
seen.add(identity)
|
||||
size += 2 + 2 * len(current)
|
||||
count = len(current)
|
||||
# Brackets and commas between items. Each item is counted below.
|
||||
size += 2 + max(0, count - 1)
|
||||
if size > MAX_FRAME_BYTES:
|
||||
return False
|
||||
pending.extend(current)
|
||||
elif current is None:
|
||||
size += 4
|
||||
elif current is True:
|
||||
size += 4
|
||||
elif current is False:
|
||||
size += 5
|
||||
elif isinstance(current, int):
|
||||
size += len(str(current))
|
||||
elif isinstance(current, float):
|
||||
size += len(json.dumps(current, separators=(",", ":")))
|
||||
else:
|
||||
size += 16
|
||||
try:
|
||||
size += len(
|
||||
json.dumps(current, ensure_ascii=False, separators=(",", ":"))
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
if size > MAX_FRAME_BYTES:
|
||||
return False
|
||||
return True
|
||||
@@ -1,38 +1,91 @@
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.services.Analyzer;
|
||||
import ghidra.app.util.headless.HeadlessScript;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/** Disable the named auto-analyzers before default auto-analysis. */
|
||||
/**
|
||||
* Disable the named auto-analyzers before default auto-analysis.
|
||||
*
|
||||
* REA_GHIDRA_DISABLED_ANALYZERS is process-wide, but Ghidra registers only the
|
||||
* installed analyzers whose canAnalyze(program) accepts this program. A name an
|
||||
* installed analyzer provides without being registered here is not applicable
|
||||
* and is recorded instead of rejected; a name no installed analyzer provides
|
||||
* aborts the import. The outcome is stored as a program property that the
|
||||
* bridge reports during its handshake.
|
||||
*/
|
||||
public final class ReaGhidraAnalysisOptions extends HeadlessScript {
|
||||
static final String REPORT_OPTION = "REA disabled analyzers";
|
||||
private static final String SETTING = "REA_GHIDRA_DISABLED_ANALYZERS";
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] arguments = getScriptArgs();
|
||||
if (arguments.length != 1 || arguments[0].isBlank()) {
|
||||
throw new IllegalArgumentException(
|
||||
"ReaGhidraAnalysisOptions requires one comma-separated analyzer-name argument");
|
||||
"ReaGhidraAnalysisOptions requires one comma-separated " + SETTING + " argument");
|
||||
}
|
||||
String[] names = arguments[0].split(",");
|
||||
boolean[] registered = new boolean[names.length];
|
||||
AutoAnalysisManager analysis = AutoAnalysisManager.getAnalysisManager(currentProgram);
|
||||
Map<String, String> options = getCurrentAnalysisOptionsAndValues(currentProgram);
|
||||
for (String requestedName : names) {
|
||||
Set<String> installed = null;
|
||||
for (int index = 0; index < names.length; index++) {
|
||||
monitor.checkCancelled();
|
||||
String name = requestedName.trim();
|
||||
if (analysis.getAnalyzer(name) == null || !options.containsKey(name)) {
|
||||
throw new IllegalArgumentException("Unknown Ghidra analyzer for this program: " + name);
|
||||
String name = names[index].trim();
|
||||
names[index] = name;
|
||||
if (analysis.getAnalyzer(name) != null) {
|
||||
if (!options.containsKey(name)) {
|
||||
throw new IllegalStateException(SETTING + " names " + name +
|
||||
", which Ghidra registered for this program without an enablement option");
|
||||
}
|
||||
registered[index] = true;
|
||||
continue;
|
||||
}
|
||||
if (installed == null) installed = installedAnalyzerNames();
|
||||
if (!installed.contains(name)) {
|
||||
throw new IllegalArgumentException(SETTING + " names " + name +
|
||||
", which is not an analyzer in this Ghidra installation");
|
||||
}
|
||||
}
|
||||
for (String requestedName : names) {
|
||||
for (int index = 0; index < names.length; index++) {
|
||||
monitor.checkCancelled();
|
||||
setAnalysisOption(currentProgram, requestedName.trim(), "false");
|
||||
if (registered[index]) setAnalysisOption(currentProgram, names[index], "false");
|
||||
}
|
||||
options = getCurrentAnalysisOptionsAndValues(currentProgram);
|
||||
for (String requestedName : names) {
|
||||
JsonArray analyzers = new JsonArray();
|
||||
for (int index = 0; index < names.length; index++) {
|
||||
monitor.checkCancelled();
|
||||
String name = requestedName.trim();
|
||||
if (!"false".equals(options.get(name))) {
|
||||
throw new IllegalStateException("Ghidra analyzer remained enabled: " + name);
|
||||
String name = names[index];
|
||||
if (registered[index] && !"false".equals(options.get(name))) {
|
||||
throw new IllegalStateException(SETTING + " names " + name +
|
||||
", which remained enabled for this program");
|
||||
}
|
||||
JsonObject analyzer = new JsonObject();
|
||||
analyzer.addProperty("name", name);
|
||||
analyzer.addProperty("status", registered[index] ? "disabled" : "not_applicable");
|
||||
analyzers.add(analyzer);
|
||||
}
|
||||
JsonObject report = new JsonObject();
|
||||
report.addProperty("format", "rea-ghidra-disabled-analyzers-v1");
|
||||
report.add("analyzers", analyzers);
|
||||
currentProgram.getOptions(Program.PROGRAM_INFO).setString(REPORT_OPTION, report.toString());
|
||||
println("REA disabled analyzers: " + report);
|
||||
}
|
||||
|
||||
/** The same analyzer universe AutoAnalysisManager filters by canAnalyze(program). */
|
||||
private static Set<String> installedAnalyzerNames() {
|
||||
Set<String> names = new HashSet<>();
|
||||
for (Analyzer analyzer : ClassSearcher.getInstances(Analyzer.class)) {
|
||||
names.add(analyzer.getName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
}
|
||||
@@ -178,6 +178,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
private List<FunctionEntry> procedureInventory;
|
||||
private List<InventoryItem> stringInventory;
|
||||
private DecompInterface decompiler;
|
||||
/** Per-function native decompiler limit from the session; 0 leaves it unbounded. */
|
||||
private int decompileTimeoutSeconds;
|
||||
// DecompileProcess converts seconds to an int millisecond timer delay.
|
||||
private static final int MAX_DECOMPILE_TIMEOUT_SECONDS = Integer.MAX_VALUE / 1000;
|
||||
private final ReaGhidraExtensions analysisExtensions = new ReaGhidraExtensions();
|
||||
private static AddressSpace sessionDefaultAddressSpace;
|
||||
|
||||
@@ -189,6 +193,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
Path descriptorPath = Path.of(arguments[0]);
|
||||
SessionDescriptor descriptor = readDescriptor(descriptorPath);
|
||||
decompileTimeoutSeconds = descriptor.decompileTimeoutSeconds();
|
||||
// Windows retains a native immutable bearer descriptor lease until
|
||||
// runtime_close; POSIX removes it after this read. Normal cleanup removes
|
||||
// the Windows file, while abrupt owner death can leave private residue.
|
||||
@@ -523,6 +528,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
String seeds = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
|
||||
.getString("REA analysis seeds", null);
|
||||
if (seeds != null) result.add("analysis_seeds", JsonParser.parseString(seeds));
|
||||
// Written by ReaGhidraAnalysisOptions before auto-analysis; REA checks it
|
||||
// against the committed profile.
|
||||
String disabledAnalyzers = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
|
||||
.getString("REA disabled analyzers", null);
|
||||
if (disabledAnalyzers != null) result.add("disabled_analyzers", JsonParser.parseString(disabledAnalyzers));
|
||||
// Written by ReaGhidraMipsSoftFloat after analysis; REA requires it
|
||||
// exactly when the committed profile declares soft-float storage.
|
||||
String softFloat = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
|
||||
@@ -1092,6 +1102,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
Function function = resolveProcedure(requireString(params, "procedure"));
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("name", procedureName(function));
|
||||
Symbol symbol = function.getSymbol();
|
||||
result.add("symbol", symbol == null ? JsonNull.INSTANCE : symbolMetadata(symbol));
|
||||
result.addProperty("entrypoint", canonicalAddress(function.getEntryPoint()));
|
||||
result.addProperty("basicblock_count", basicBlocks(function).size());
|
||||
JsonObject body = functionBody(function);
|
||||
@@ -1263,9 +1275,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
result.add("procedure", function == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(function.getEntryPoint())));
|
||||
result.addProperty("architecture", currentProgram.getLanguageID().getIdAsString());
|
||||
result.addProperty("mode", currentProgram.getLanguage().getLanguageDescription().getVariant());
|
||||
if (currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:")) {
|
||||
if (currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:") ||
|
||||
currentProgram.getLanguageID().getIdAsString().startsWith("ARM:")) {
|
||||
JsonArray decoderContext = new JsonArray();
|
||||
var modeRegister = currentProgram.getRegister("ISA_MODE");
|
||||
String regName = currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:") ? "ISA_MODE" : "TMode";
|
||||
var modeRegister = currentProgram.getRegister(regName);
|
||||
if (modeRegister != null) {
|
||||
var modeValue = currentProgram.getProgramContext().getValue(modeRegister, address, false);
|
||||
JsonObject context = new JsonObject();
|
||||
@@ -1548,7 +1562,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
} catch (RequestFailure failure) {
|
||||
// The listing, reference and CFG facts below do not depend on the
|
||||
// native decompiler. Cancellation still ends the request.
|
||||
if (!failure.code.equals("decompile_failed")) throw failure;
|
||||
if (!failure.code.equals("decompile_failed") &&
|
||||
!failure.code.equals("decompile_timed_out")) throw failure;
|
||||
decompilation = null;
|
||||
decompilerFailure = failure.getMessage().strip();
|
||||
}
|
||||
@@ -2131,13 +2146,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (!address.isMemoryAddress() && !address.isExternalAddress()) {
|
||||
continue;
|
||||
}
|
||||
JsonObject facts = new JsonObject();
|
||||
facts.addProperty("primary", symbol.isPrimary());
|
||||
facts.addProperty("dynamic", symbol.isDynamic());
|
||||
facts.addProperty("external", symbol.isExternal());
|
||||
facts.addProperty("type", normalizedSymbolType(symbol));
|
||||
facts.addProperty("source", normalizedSource(symbol.getSource()));
|
||||
result.add(new InventoryItem(address, symbol.getName(true), facts));
|
||||
JsonObject facts = symbolMetadata(symbol);
|
||||
result.add(new InventoryItem(address, symbol.getName(true), facts, null));
|
||||
}
|
||||
result.sort(INVENTORY_ORDER);
|
||||
nameInventory = List.copyOf(result);
|
||||
@@ -2181,7 +2191,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
InventoryItem item = new InventoryItem(
|
||||
function.getEntryPoint(),
|
||||
symbol == null ? function.getName() : symbol.getName(true),
|
||||
facts
|
||||
facts,
|
||||
symbol == null ? null : symbolMetadata(symbol)
|
||||
);
|
||||
destination.add(new FunctionEntry(function, item));
|
||||
}
|
||||
@@ -2212,7 +2223,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
instance.isMissingNullTerminator() ? "missing" : "present_or_not_required"
|
||||
);
|
||||
facts.addProperty("byte_length", Math.max(0, data.getLength()));
|
||||
result.add(new InventoryItem(data.getAddress(), value, facts));
|
||||
result.add(new InventoryItem(data.getAddress(), value, facts, null));
|
||||
}
|
||||
result.sort(INVENTORY_ORDER);
|
||||
stringInventory = List.copyOf(result);
|
||||
@@ -3354,12 +3365,22 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
DecompileResults results = decompiler.decompileFunction(
|
||||
function,
|
||||
0,
|
||||
decompileTimeoutSeconds,
|
||||
monitor
|
||||
);
|
||||
if (results.isCancelled()) {
|
||||
throw new RequestFailure("decompile_cancelled", "Ghidra decompilation was cancelled");
|
||||
}
|
||||
// DecompInterface disposes the native process at the limit and starts a
|
||||
// fresh one for the next request, so the session stays usable.
|
||||
if (results.isTimedOut()) {
|
||||
throw new RequestFailure(
|
||||
"decompile_timed_out",
|
||||
"Ghidra decompilation of " + canonicalAddress(function.getEntryPoint()) +
|
||||
" exceeded the " + decompileTimeoutSeconds +
|
||||
"-second limit set by REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS"
|
||||
);
|
||||
}
|
||||
if (!results.decompileCompleted()) {
|
||||
throw new RequestFailure(
|
||||
"decompile_failed",
|
||||
@@ -4399,6 +4420,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (factsName != null) {
|
||||
result.add(factsName, item.facts.deepCopy());
|
||||
}
|
||||
if (item.symbol != null) {
|
||||
result.add("symbol", item.symbol.deepCopy());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4426,6 +4450,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("address", canonicalAddress(function.getEntryPoint()));
|
||||
result.addProperty("name", procedureName(function));
|
||||
Symbol symbol = function.getSymbol();
|
||||
result.add("symbol", symbol == null ? JsonNull.INSTANCE : symbolMetadata(symbol));
|
||||
result.add("classification", functionClassification(function));
|
||||
result.add("body", functionBody(function));
|
||||
return result;
|
||||
@@ -4654,6 +4680,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
return value.isEmpty() ? "unknown" : value;
|
||||
}
|
||||
|
||||
private static JsonObject symbolMetadata(Symbol symbol) {
|
||||
JsonObject facts = new JsonObject();
|
||||
facts.addProperty("primary", symbol.isPrimary());
|
||||
facts.addProperty("dynamic", symbol.isDynamic());
|
||||
facts.addProperty("external", symbol.isExternal());
|
||||
facts.addProperty("type", normalizedSymbolType(symbol));
|
||||
facts.addProperty("source", normalizedSource(symbol.getSource()));
|
||||
return facts;
|
||||
}
|
||||
|
||||
private static String normalizedSource(SourceType source) {
|
||||
return source.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
@@ -4694,10 +4730,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
};
|
||||
}
|
||||
|
||||
// The host queue owns session lifetime, including concurrent cancellation.
|
||||
private static RequestFailure regexStackExhausted(String expression, String stage) {
|
||||
return new RequestFailure("regex_stack_exhausted",
|
||||
"Ghidra's Java regex engine exhausted its stack while " + stage +
|
||||
" pattern " + expression + ". Use literal mode or simplify the regex; the session remains usable.");
|
||||
" pattern " + expression + ".");
|
||||
}
|
||||
|
||||
private static SessionDescriptor readDescriptor(Path path) throws IOException {
|
||||
@@ -4708,7 +4745,15 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
Set<String> keys = new HashSet<>(DESCRIPTOR_KEYS);
|
||||
if (parsed.isJsonObject() && parsed.getAsJsonObject().has("analysis_extensions"))
|
||||
keys.add("analysis_extensions");
|
||||
if (parsed.isJsonObject() && parsed.getAsJsonObject().has("decompile_timeout_seconds"))
|
||||
keys.add("decompile_timeout_seconds");
|
||||
JsonObject object = requireObject(parsed, keys);
|
||||
int decompileTimeout = 0;
|
||||
if (object.has("decompile_timeout_seconds")) {
|
||||
decompileTimeout = requireInteger(object, "decompile_timeout_seconds");
|
||||
if (decompileTimeout < 1 || decompileTimeout > MAX_DECOMPILE_TIMEOUT_SECONDS)
|
||||
throw new IllegalArgumentException("REA session descriptor is invalid");
|
||||
}
|
||||
return new SessionDescriptor(
|
||||
requireString(object, "transport"),
|
||||
requireString(object, "endpoint_path"),
|
||||
@@ -4717,7 +4762,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
requireSha256(object, "target_sha256"),
|
||||
requireString(object, "provider_version"),
|
||||
requireString(object, "profile_digest"),
|
||||
object.has("analysis_extensions") ? object.getAsJsonArray("analysis_extensions") : new JsonArray()
|
||||
object.has("analysis_extensions") ? object.getAsJsonArray("analysis_extensions") : new JsonArray(),
|
||||
decompileTimeout
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4900,7 +4946,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
.thenComparingInt(Reference::getOperandIndex)
|
||||
.thenComparing(Reference::isPrimary);
|
||||
|
||||
private record InventoryItem(Address address, String value, JsonObject facts) {}
|
||||
private record InventoryItem(Address address, String value, JsonObject facts, JsonObject symbol) {}
|
||||
private record AddressNameRequest(String inputAddress, Address address, String name) {}
|
||||
private record FunctionEntry(Function function, InventoryItem item) {}
|
||||
private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations, JsonObject relationshipCoverage, boolean decompilerFailed) {}
|
||||
@@ -4922,7 +4968,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
String targetSha256,
|
||||
String providerVersion,
|
||||
String profileDigest,
|
||||
JsonArray analysisExtensions
|
||||
JsonArray analysisExtensions,
|
||||
int decompileTimeoutSeconds
|
||||
) {}
|
||||
private record Request(int id, String method, JsonObject params) {}
|
||||
|
||||
|
||||
+144
-34
@@ -8,12 +8,100 @@ API access on this thread: moving dispatch to a worker can deadlock Hopper.
|
||||
import json
|
||||
import hmac
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
from typing import Any, Optional, Protocol, Sequence
|
||||
|
||||
BAD_ADDRESSES = (-1, 0xFFFFFFFFFFFFFFFF, None)
|
||||
# The native label setter truncates NSString names at this UTF-16 extent.
|
||||
HOPPER_SYMBOL_NAME_UTF16_UNITS = 1024
|
||||
# Request JSON payload budget in bytes, excluding the trailing LF.
|
||||
# REA_BRIDGE_MAX_REQUEST_BYTES selects it. Unset stays 16777216.
|
||||
# 7464 is the authenticated Cutter frame of the largest bounded symbol name.
|
||||
# Cutter's 64-receipt recovery status is smaller with a null path. A path
|
||||
# that does not fit is null, not an older descriptor path.
|
||||
# 536870887 is Node's 536870888-character string ceiling minus the framing LF.
|
||||
# The ceiling is the encoded payload; JSON expansion is measured before it.
|
||||
_BRIDGE_MAX_REQUEST_BYTES_SETTING = "REA_BRIDGE_MAX_REQUEST_BYTES"
|
||||
DEFAULT_BRIDGE_MAX_REQUEST_BYTES = 16 * 1024 * 1024
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES = 7464
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES = 536870887
|
||||
|
||||
|
||||
def _bridge_max_request_bytes_constraint():
|
||||
return (
|
||||
"REA_BRIDGE_MAX_REQUEST_BYTES must be a positive decimal integer "
|
||||
"from %d to %d bytes; the default is %d bytes (16 MiB). "
|
||||
"A value below %d bytes cannot carry the largest admitted "
|
||||
"symbol-name command, and a larger value is refused so the encoded "
|
||||
"payload plus its framing LF stays one string under Node's "
|
||||
"single-string ceiling."
|
||||
% (
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _parse_bridge_max_request_bytes(raw):
|
||||
"""Refuse a non-decimal or out-of-range ceiling. Never fall back."""
|
||||
constraint = _bridge_max_request_bytes_constraint()
|
||||
if isinstance(raw, bool):
|
||||
raise RuntimeError(constraint)
|
||||
if isinstance(raw, int):
|
||||
value = raw
|
||||
elif isinstance(raw, str):
|
||||
# ASCII digits only. str.isdigit() accepts numerals such as U+0668
|
||||
# that the TypeScript setting rejects.
|
||||
if re.fullmatch(r"[1-9][0-9]*", raw) is None:
|
||||
raise RuntimeError(constraint)
|
||||
value = int(raw)
|
||||
else:
|
||||
raise RuntimeError(constraint)
|
||||
if (
|
||||
value < MINIMUM_BRIDGE_MAX_REQUEST_BYTES
|
||||
or value > MAXIMUM_BRIDGE_MAX_REQUEST_BYTES
|
||||
):
|
||||
raise RuntimeError(constraint)
|
||||
return value
|
||||
|
||||
|
||||
def _resolve_bridge_max_request_bytes():
|
||||
"""Prefer the bootstrap injection, then the process environment, then 16 MiB."""
|
||||
injected = globals().get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
|
||||
if injected is not None:
|
||||
return _parse_bridge_max_request_bytes(injected), "setting"
|
||||
raw = os.environ.get(_BRIDGE_MAX_REQUEST_BYTES_SETTING)
|
||||
if raw is None:
|
||||
return DEFAULT_BRIDGE_MAX_REQUEST_BYTES, "default"
|
||||
return _parse_bridge_max_request_bytes(raw), "setting"
|
||||
|
||||
|
||||
MAX_REQUEST_BYTES, BRIDGE_MAX_REQUEST_BYTES_SOURCE = (
|
||||
_resolve_bridge_max_request_bytes()
|
||||
)
|
||||
|
||||
|
||||
def _bridge_max_request_bytes_recovery():
|
||||
return (
|
||||
"The effective REA_BRIDGE_MAX_REQUEST_BYTES value is %d bytes "
|
||||
"(source %s; default %d, floor %d, maximum %d). "
|
||||
"Set REA_BRIDGE_MAX_REQUEST_BYTES in the environment that starts REA "
|
||||
"to a decimal integer from %d to %d, then restart REA. "
|
||||
"Restart Hopper or Cutter from that same environment so the bridge "
|
||||
"uses this ceiling."
|
||||
% (
|
||||
MAX_REQUEST_BYTES,
|
||||
BRIDGE_MAX_REQUEST_BYTES_SOURCE,
|
||||
DEFAULT_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MINIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
MAXIMUM_BRIDGE_MAX_REQUEST_BYTES,
|
||||
)
|
||||
)
|
||||
_rea_session_document = None
|
||||
_search_inventory_cache = {}
|
||||
_pseudocode_cache = {}
|
||||
@@ -236,20 +324,27 @@ def _name_matches(document, address, name):
|
||||
return observed == name or (not name and observed is None)
|
||||
|
||||
|
||||
def _bookmark_name(document, address):
|
||||
getter = getattr(document, "getBookmarkName", None)
|
||||
if not callable(getter):
|
||||
def _bookmark_api(document, name):
|
||||
method = getattr(document, name, None)
|
||||
if not callable(method):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose getBookmarkName"
|
||||
"Hopper's public Python API does not expose %s" % name
|
||||
)
|
||||
return getter(address)
|
||||
return method
|
||||
|
||||
|
||||
def _address(document, value=None):
|
||||
"""Keep explicit hexadecimal coordinates distinct from symbol names."""
|
||||
def _bookmark_name(document, address):
|
||||
return _bookmark_api(document, "getBookmarkName")(address)
|
||||
|
||||
|
||||
def _address_selection(document, value=None):
|
||||
"""Own the coordinate and whether it was selected by symbol or address."""
|
||||
if value is None:
|
||||
return document.getCurrentAddress()
|
||||
return document.getCurrentAddress(), "cursor"
|
||||
_api_text(value, "Address")
|
||||
if value.startswith("file-offset:"):
|
||||
raise InvalidRequestError("Hopper does not support file-offset: selectors; select Ghidra for source-file coordinates or use a Hopper analysis address")
|
||||
source = "address"
|
||||
if value.lower().startswith("0x"):
|
||||
try:
|
||||
address = int(value, 16)
|
||||
@@ -260,6 +355,8 @@ def _address(document, value=None):
|
||||
# Prefer an observed symbol, retaining bare-hex compatibility only
|
||||
# when Hopper has no symbol with the supplied name.
|
||||
address = document.getAddressForName(value)
|
||||
if address not in BAD_ADDRESSES:
|
||||
source = "symbol"
|
||||
if address in BAD_ADDRESSES:
|
||||
try:
|
||||
address = int(value, 16)
|
||||
@@ -267,7 +364,11 @@ def _address(document, value=None):
|
||||
raise InvalidRequestError("Unknown Hopper address or name: %s" % value) from error
|
||||
if not 0 <= address < 0xFFFFFFFFFFFFFFFF:
|
||||
raise InvalidRequestError("Address is outside Hopper's usable unsigned 64-bit range: %s" % value)
|
||||
return address
|
||||
return address, source
|
||||
|
||||
|
||||
def _address(document, value=None):
|
||||
return _address_selection(document, value)[0]
|
||||
|
||||
|
||||
def _segment(document, address):
|
||||
@@ -278,10 +379,15 @@ def _segment(document, address):
|
||||
|
||||
|
||||
def _procedure(document, value=None):
|
||||
address = document.getCurrentAddress() if value is None else _address(document, value)
|
||||
address, source = _address_selection(document, value)
|
||||
result = _segment(document, address).getProcedureAtAddress(address)
|
||||
if result is None:
|
||||
raise InvalidRequestError("No procedure exists at the requested address")
|
||||
if source == "symbol" and address != result.getEntryPoint():
|
||||
raise InvalidRequestError(
|
||||
"Symbol %s at %s is inside procedure %s at %s; select its entry name/address or use resolve_containing_procedure for explicit containment"
|
||||
% (value, _hex(address), _procedure_name(result), _hex(result.getEntryPoint()))
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@@ -294,6 +400,7 @@ def _procedure_identity(procedure):
|
||||
return {
|
||||
"address": _hex(procedure.getEntryPoint()),
|
||||
"name": _procedure_name(procedure),
|
||||
"symbol": None,
|
||||
"classification": None,
|
||||
"body": {
|
||||
"available": False,
|
||||
@@ -740,6 +847,7 @@ def _dispatch(method, params):
|
||||
"version": "1.0.0",
|
||||
"run_id": REA_RUN_ID,
|
||||
"process_id": os.getpid(),
|
||||
"max_request_bytes": MAX_REQUEST_BYTES,
|
||||
}
|
||||
if method in ("shutdown", "shutdown_document"):
|
||||
document = _session_document()
|
||||
@@ -963,6 +1071,7 @@ def _dispatch(method, params):
|
||||
length = sum(max(0, _basic_block_end(procedure, block) - block.getStartingAddress()) for block in blocks)
|
||||
return {
|
||||
"name": _procedure_name(procedure),
|
||||
"symbol": None,
|
||||
"entrypoint": _hex(procedure.getEntryPoint()),
|
||||
"basicblock_count": procedure.getBasicBlockCount(),
|
||||
"length": length,
|
||||
@@ -1003,39 +1112,24 @@ def _dispatch(method, params):
|
||||
observed = getter(address)
|
||||
return observed == comment or (comment == "" and observed is None)
|
||||
if method == "list_bookmarks":
|
||||
return [{"address": _hex(item), "name": _bookmark_name(document, item)} for item in document.getBookmarks()]
|
||||
return [{"address": _hex(item), "name": _bookmark_name(document, item)} for item in _bookmark_api(document, "getBookmarks")()]
|
||||
if method == "set_bookmark":
|
||||
address = _address(document, params.get("address"))
|
||||
_segment(document, address)
|
||||
name = params.get("name")
|
||||
if name is not None:
|
||||
_api_text(name, "Bookmark name")
|
||||
has_bookmark = getattr(document, "hasBookmarkAtAddress", None)
|
||||
if not callable(has_bookmark):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose hasBookmarkAtAddress"
|
||||
)
|
||||
has_bookmark = _bookmark_api(document, "hasBookmarkAtAddress")
|
||||
exists = has_bookmark(address)
|
||||
if name is None and exists:
|
||||
# Omission means preserve the existing bookmark and its name.
|
||||
return True
|
||||
if not callable(getattr(document, "getBookmarkName", None)):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose getBookmarkName"
|
||||
)
|
||||
_bookmark_api(document, "getBookmarkName")
|
||||
if name == "" and exists:
|
||||
rename = getattr(document, "renameBookmarkAtAddress", None)
|
||||
if not callable(rename):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose renameBookmarkAtAddress"
|
||||
)
|
||||
rename = _bookmark_api(document, "renameBookmarkAtAddress")
|
||||
rename(address, name)
|
||||
else:
|
||||
setter = getattr(document, "setBookmarkAtAddress", None)
|
||||
if not callable(setter):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose setBookmarkAtAddress"
|
||||
)
|
||||
setter = _bookmark_api(document, "setBookmarkAtAddress")
|
||||
setter(address, name)
|
||||
if not has_bookmark(address):
|
||||
return False
|
||||
@@ -1047,10 +1141,12 @@ def _dispatch(method, params):
|
||||
address = _address(document, params.get("address"))
|
||||
# Permit removing a legacy orphan bookmark, but do not report success
|
||||
# for an unmapped address with no bookmark to remove.
|
||||
if not document.hasBookmarkAtAddress(address):
|
||||
has_bookmark = _bookmark_api(document, "hasBookmarkAtAddress")
|
||||
remove_bookmark = _bookmark_api(document, "removeBookmarkAtAddress")
|
||||
if not has_bookmark(address):
|
||||
_segment(document, address)
|
||||
document.removeBookmarkAtAddress(address)
|
||||
return not document.hasBookmarkAtAddress(address)
|
||||
remove_bookmark(address)
|
||||
return not has_bookmark(address)
|
||||
raise InvalidRequestError("Unknown bridge method")
|
||||
|
||||
|
||||
@@ -1060,9 +1156,23 @@ def _serve_connection(connection):
|
||||
should_stop = False
|
||||
try:
|
||||
while True:
|
||||
line = file.readline()
|
||||
# Bound the read itself. The payload budget excludes LF, so permit
|
||||
# one additional byte for the delimiter while still refusing an
|
||||
# oversized unterminated peer without waiting for EOF.
|
||||
line = file.readline(MAX_REQUEST_BYTES + 1)
|
||||
if not line:
|
||||
break
|
||||
if not line.endswith(b"\n"):
|
||||
message = (
|
||||
"Bridge request exceeds the %d-byte payload limit. %s"
|
||||
% (MAX_REQUEST_BYTES, _bridge_max_request_bytes_recovery())
|
||||
if len(line) > MAX_REQUEST_BYTES
|
||||
else "Incomplete bridge request frame"
|
||||
)
|
||||
_write_message(file, {"id": 0, "error": _safe_diagnostic(
|
||||
InvalidRequestError(message)
|
||||
)})
|
||||
break
|
||||
request_id = None
|
||||
authenticated = False
|
||||
should_stop = False
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import Foundation
|
||||
import CoreFoundation
|
||||
|
||||
struct AccessibilityValue {
|
||||
let value: Any
|
||||
let gap: String?
|
||||
}
|
||||
|
||||
/** Preserve JSON scalars; report successful reads that JSON cannot represent faithfully. */
|
||||
func captureAccessibilityValue(_ source: Any?) -> AccessibilityValue {
|
||||
guard let source else { return AccessibilityValue(value: NSNull(), gap: nil) }
|
||||
if let text = source as? String { return AccessibilityValue(value: text, gap: nil) }
|
||||
if let number = source as? NSNumber {
|
||||
if CFGetTypeID(number) == CFBooleanGetTypeID() {
|
||||
return AccessibilityValue(value: number.boolValue, gap: nil)
|
||||
}
|
||||
let value = number.doubleValue
|
||||
let encoding = String(cString: number.objCType)
|
||||
let floating = encoding == "f" || encoding == "d"
|
||||
if number is NSDecimalNumber || !value.isFinite || (value == 0 && value.sign == .minus) || (!floating && abs(value) > 9_007_199_254_740_991) {
|
||||
return AccessibilityValue(value: NSNull(), gap: "AXValue number \(number.stringValue) cannot be represented faithfully as a JSON number")
|
||||
}
|
||||
return AccessibilityValue(value: number, gap: nil)
|
||||
}
|
||||
return AccessibilityValue(value: NSNull(), gap: "AXValue type \(type(of: source)) is not a supported JSON scalar")
|
||||
}
|
||||
@@ -43,10 +43,13 @@ func windowBounds(_ element: AXUIElement) -> CGRect? {
|
||||
return CGRect(origin: point, size: size)
|
||||
}
|
||||
func observe(_ request: Request) async throws -> [String: Any] {
|
||||
guard let app = NSRunningApplication(processIdentifier: request.pid), let url = app.executableURL else {
|
||||
try fail("target-mismatch", "Selected PID does not expose an application executable URL")
|
||||
guard let app = NSRunningApplication(processIdentifier: request.pid) else {
|
||||
try fail("pid-not-application", "LaunchServices does not expose an application for the selected PID; this does not establish that the process is absent")
|
||||
}
|
||||
guard let canonicalPointer = realpath(url.path, nil) else { try fail("target-mismatch", "Running executable path cannot be resolved") }
|
||||
guard let url = app.executableURL else {
|
||||
try fail("application-executable-unavailable", "The selected application does not expose an executable URL; select a PID with an identifiable application executable")
|
||||
}
|
||||
guard let canonicalPointer = realpath(url.path, nil) else { try fail("executable-path-unavailable", "Running executable path cannot be resolved") }
|
||||
defer { free(canonicalPointer) }
|
||||
let canonicalExecutable = String(cString: canonicalPointer)
|
||||
guard canonicalExecutable == request.executable else {
|
||||
@@ -117,7 +120,9 @@ func observe(_ request: Request) async throws -> [String: Any] {
|
||||
let childCountResult = childCount(element)
|
||||
var actions: CFArray?
|
||||
AXUIElementCopyActionNames(element, &actions)
|
||||
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": text(element, kAXValueAttribute), "actions": actions as? [String] ?? [], "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
|
||||
let observedValue = captureAccessibilityValue(attribute(element, kAXValueAttribute))
|
||||
if let gap = observedValue.gap { gaps.append("\(gap) at path \(path)"); truncated = true }
|
||||
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": observedValue.value, "actions": actions as? [String] ?? [], "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
|
||||
guard let totalChildren = childCountResult.value else {
|
||||
truncated = true
|
||||
gaps.append("AX child count unavailable at path \(path): AXError \(childCountResult.error ?? -1)")
|
||||
|
||||
@@ -661,18 +661,15 @@ def trace(debugger, config):
|
||||
open(observation_path, "wb")
|
||||
)
|
||||
process = target.Launch(_launch_info(target, config, listener), error)
|
||||
if not error.Success() or not process.IsValid():
|
||||
process_created = process.IsValid()
|
||||
if not error.Success() or not process_created:
|
||||
exit_observed = _retire_failed_launch(
|
||||
process, config, listener, stdout, stderr
|
||||
)
|
||||
return {
|
||||
"status": "launch-error",
|
||||
"error": error.GetCString(),
|
||||
**(
|
||||
{"target_process_created": True}
|
||||
if process.IsValid()
|
||||
else {}
|
||||
),
|
||||
"target_process_created": process_created,
|
||||
**(
|
||||
{"target_process_exit_observed": True}
|
||||
if exit_observed
|
||||
|
||||
@@ -25,6 +25,73 @@ spec.loader.exec_module(tracer)
|
||||
|
||||
|
||||
class NativeTraceBudgetTests(unittest.TestCase):
|
||||
def test_invalid_launch_process_reports_explicit_no_creation(self):
|
||||
class InvalidProcess:
|
||||
def IsValid(self):
|
||||
return False
|
||||
|
||||
def Kill(self):
|
||||
raise AssertionError("an invalid process cannot own target cleanup")
|
||||
|
||||
class LaunchError:
|
||||
def Success(self):
|
||||
return False
|
||||
|
||||
def GetCString(self):
|
||||
return "No such file or directory: fixture/absent"
|
||||
|
||||
class Target:
|
||||
def IsValid(self):
|
||||
return True
|
||||
|
||||
def Launch(self, _launch_info, _error):
|
||||
return InvalidProcess()
|
||||
|
||||
class Debugger:
|
||||
def SetAsync(self, _enabled):
|
||||
pass
|
||||
|
||||
def GetInstanceName(self):
|
||||
return "fixture"
|
||||
|
||||
def CreateTarget(self, *_arguments):
|
||||
return Target()
|
||||
|
||||
class Pipe:
|
||||
def __init__(self, *_arguments):
|
||||
self.closed = False
|
||||
pipes.append(self)
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
def close(self, expected_exit=False):
|
||||
self.closed = True
|
||||
|
||||
pipes = []
|
||||
debugger_api = types.SimpleNamespace(SetInternalVariable=lambda *_arguments: None)
|
||||
with patch.object(tracer.lldb, "SBDebugger", debugger_api, create=True), patch.object(
|
||||
tracer.lldb, "SBError", LaunchError, create=True
|
||||
), patch.object(tracer.lldb, "SBListener", lambda _name: object(), create=True), patch.object(
|
||||
tracer, "_hash_file", return_value="expected"
|
||||
), patch.object(tracer, "_file_identity", return_value=(1, 2)), patch.object(
|
||||
tracer, "_module_identity", return_value=("fixture", (1, 2), "fixture-uuid")
|
||||
), patch.object(tracer, "_launch_info", return_value=object()), patch.object(
|
||||
tracer, "_OutputPipe", Pipe
|
||||
):
|
||||
result = tracer.trace(Debugger(), {
|
||||
"executable": "fixture", "architecture": "arm64", "expected_sha256": "expected",
|
||||
"breakpoints": [], "working_directory": "fixture/absent",
|
||||
"stdout_path": "stdout", "stdout_capture_path": "stdout-capture",
|
||||
"stderr_path": "stderr", "stderr_capture_path": "stderr-capture",
|
||||
})
|
||||
self.assertEqual(result["status"], "launch-error")
|
||||
self.assertEqual(result["error"], "No such file or directory: fixture/absent")
|
||||
self.assertIs(result["target_process_created"], False)
|
||||
self.assertNotIn("target_process_exit_observed", result)
|
||||
self.assertEqual(len(pipes), 2)
|
||||
self.assertTrue(all(pipe.closed for pipe in pipes))
|
||||
|
||||
def test_failed_launch_does_not_infer_exit_from_a_failed_kill(self):
|
||||
class Process:
|
||||
def IsValid(self):
|
||||
|
||||
@@ -132,6 +132,11 @@ def inspect_elf(path, cache):
|
||||
try:
|
||||
headers = ELFFile(io.BytesIO(content))
|
||||
section_count = headers.num_sections()
|
||||
if section_count:
|
||||
location(headers.header.e_shoff, section_count * headers.header.e_shentsize, len(content))
|
||||
segment_count = headers.num_segments()
|
||||
if segment_count:
|
||||
location(headers.header.e_phoff, segment_count * headers.header.e_phentsize, len(content))
|
||||
if headers.header.e_shstrndx == 0xffff and section_count == 0:
|
||||
raise LayoutFailure("format", "ELF extended section-name index requires a declared section-zero header.")
|
||||
raw_name_index = headers.header.e_shstrndx
|
||||
@@ -147,9 +152,21 @@ def inspect_elf(path, cache):
|
||||
if names_header.sh_type != "SHT_STRTAB":
|
||||
raise LayoutFailure("format", f"ELF section-name table {section_name_index} is not SHT_STRTAB.")
|
||||
location(names_header.sh_offset, names_header.sh_size, len(content))
|
||||
except ELFError as error:
|
||||
except (ELFError, ValueError, OverflowError, AssertionError, IndexError) as error:
|
||||
raise LayoutFailure("format", str(error)) from error
|
||||
class LayoutELF(ELF):
|
||||
def search(self, needle, writable=False, executable=False):
|
||||
# This adapter exposes structural facts, GOT/PLT and mitigation
|
||||
# indicators, not kernel config/version discovery. Upstream's
|
||||
# constructor searches virtual memory for those unrelated
|
||||
# heuristics, materializing NOBITS tails and interpreting
|
||||
# ordinary IKCFG_ST data as a compressed kernel config.
|
||||
return iter(())
|
||||
|
||||
def _populate_kernel_version(self):
|
||||
# Kernel banner decoding is outside this report too.
|
||||
return None
|
||||
|
||||
def _get_section_name(self, header):
|
||||
# SHN_UNDEF declares no names; upstream would otherwise read
|
||||
# strings at the inactive section-zero offset.
|
||||
@@ -294,6 +311,7 @@ def inspect_elf(path, cache):
|
||||
"Relocatable relocation targets with SHN_UNDEF or inactive SHT_NULL headers retain their reported section index/offset as unknown; they do not establish an associated target section.",
|
||||
"GOT/PLT are derived upstream convenience maps; PLT inference can emulate selected instructions in Unicorn. They do not establish target runtime behavior; aliases may collapse and warnings may indicate incomplete coverage. Their completeness is unknown.",
|
||||
"Mitigations are upstream static heuristics, not runtime protection. ET_DYN does not prove an executable; absent canary symbols do not prove every function unprotected.",
|
||||
"Kernel configuration/version discovery is not performed; these memory-content scans do not supply the reported structural, linkage or mitigation facts.",
|
||||
"This profile inspects ELF EXEC/DYN/REL layout only. It does not analyze recorded cores, resolve loaded libraries, launch the target as a host process or start a debugger.",
|
||||
"Symbol/relocation inventories reflect original section tables. A sectionless image can still report dynamic dependency names; missing tables do not prove absence of dynamic symbols or relocations.",
|
||||
*(["Absent section-name table: section-dependent RELRO/canary heuristics may be incomplete; protection coverage is unknown."] if all_sections and section_name_index == 0 else []),
|
||||
@@ -302,7 +320,7 @@ def inspect_elf(path, cache):
|
||||
}
|
||||
except LayoutFailure:
|
||||
raise
|
||||
except (ELFError, ValueError, AssertionError, IndexError) as error:
|
||||
except (ELFError, ValueError, OverflowError, AssertionError, IndexError) as error:
|
||||
raise LayoutFailure("format", "Selected ELF failed unchanged upstream structural parsing: " + str(error)) from error
|
||||
|
||||
|
||||
|
||||
@@ -88,16 +88,18 @@ def inspect_core(path, cache):
|
||||
segments, notes, padding, threads, signals = [], [], [], [], []
|
||||
for segment_index, segment in enumerate(image.iter_segments()):
|
||||
h = segment.header
|
||||
backing = h.p_type != "PT_NULL" and h.p_filesz > 0
|
||||
header_offset = header.e_phoff + segment_index * header.e_phentsize
|
||||
type_id = int.from_bytes(content[header_offset:header_offset + 4], "little")
|
||||
backing = type_id != 0 and h.p_filesz > 0
|
||||
if backing:
|
||||
location(h.p_offset, h.p_filesz, len(content))
|
||||
if h.p_type == "PT_LOAD" and h.p_filesz > h.p_memsz:
|
||||
if type_id == 1 and h.p_filesz > h.p_memsz:
|
||||
raise CoreFailure("format", "Recorded PT_LOAD file bytes exceed memory size.")
|
||||
segments.append({"index": segment_index, "type": h.p_type,
|
||||
"header_location": location(header.e_phoff + segment_index * header.e_phentsize, header.e_phentsize, len(content)),
|
||||
segments.append({"index": segment_index, "type": h.p_type, "type_id": type_id,
|
||||
"header_location": location(header_offset, header.e_phentsize, len(content)),
|
||||
"offset": hex(h.p_offset), "file_size": hex(h.p_filesz), "memory_size": hex(h.p_memsz),
|
||||
"virtual_address": hex(h.p_vaddr), "physical_address": hex(h.p_paddr), "alignment": hex(h.p_align), "flags": hex(h.p_flags), "file_backing": "file" if backing else "none"})
|
||||
if h.p_type != "PT_NOTE" or h.p_filesz == 0:
|
||||
if type_id != 4 or h.p_filesz == 0:
|
||||
continue
|
||||
position, end = h.p_offset, h.p_offset + h.p_filesz
|
||||
header_size = image.structs.Elf_Nhdr.sizeof()
|
||||
@@ -109,6 +111,7 @@ def inspect_core(path, cache):
|
||||
padding.append({"segment_index": segment_index, "location": location(position, len(raw), end), "bytes_base64": encoded(raw)})
|
||||
break
|
||||
note = struct_parse(image.structs.Elf_Nhdr, image.stream, stream_pos=position)
|
||||
note_type_id = int.from_bytes(content[position + 8:position + 12], "little")
|
||||
owner_start = position + header_size
|
||||
descriptor_start = owner_start + ((note.n_namesz + 3) // 4) * 4
|
||||
note_end = descriptor_start + ((note.n_descsz + 3) // 4) * 4
|
||||
@@ -119,7 +122,10 @@ def inspect_core(path, cache):
|
||||
display = owner.split(b"\0", 1)[0].decode("utf-8", "replace") if owner else None
|
||||
descriptor = content[descriptor_start:descriptor_start + note.n_descsz]
|
||||
note_index = len(notes)
|
||||
notes.append({"index": note_index, "segment_index": segment_index, "type": note.n_type,
|
||||
# pyelftools selects ET_CORE labels without owner context. Keep
|
||||
# those labels only for exact CORE owners; other kinds are opaque.
|
||||
note_type = note.n_type if owner == b"CORE\0" else note_type_id
|
||||
notes.append({"index": note_index, "segment_index": segment_index, "type": note_type, "type_id": note_type_id,
|
||||
"location": location(position, note_end - position, end),
|
||||
"owner_location": location(owner_start, len(owner), end), "owner_bytes_base64": encoded(owner), "owner_display": display,
|
||||
"descriptor_location": location(descriptor_start, len(descriptor), end), "descriptor_bytes_base64": encoded(descriptor)})
|
||||
@@ -127,7 +133,7 @@ def inspect_core(path, cache):
|
||||
# Only the initial Linux CORE ABI is interpreted. Other owners retain exact bytes.
|
||||
if owner != b"CORE\0":
|
||||
continue
|
||||
if note.n_type == "NT_PRSTATUS":
|
||||
if note_type_id == 1:
|
||||
if len(descriptor) < ctypes.sizeof(prstatus_type):
|
||||
raise CoreFailure("format", "Linux " + profile_label + " NT_PRSTATUS descriptor is truncated.")
|
||||
status = prstatus_type.from_buffer_copy(descriptor)
|
||||
@@ -136,7 +142,7 @@ def inspect_core(path, cache):
|
||||
relative = prstatus_type.pr_reg.offset + getattr(registers_type, name).offset
|
||||
registers.append({"name": name, "value": hex(getattr(status.pr_reg, name)), "location": location(descriptor_start + relative, ctypes.sizeof(kind), end)})
|
||||
threads.append({"note_index": note_index, "historical_pid": ctypes.c_int32(status.pr_pid).value, "recorded_current_signal": status.pr_cursig, "registers": registers})
|
||||
elif note.n_type == "NT_SIGINFO":
|
||||
elif note_type_id == 0x53494749:
|
||||
if len(descriptor) < ctypes.sizeof(elf_siginfo_64):
|
||||
raise CoreFailure("format", "Linux " + profile_label + " NT_SIGINFO descriptor is truncated.")
|
||||
signal = elf_siginfo_64.from_buffer_copy(descriptor)
|
||||
|
||||
@@ -32,7 +32,12 @@ export default defineConfig({
|
||||
{ text: "IL2CPP metadata", link: "/il2cpp-metadata" },
|
||||
{ text: "Model artifacts", link: "/model-artifacts" },
|
||||
{ text: "Rust dependencies", link: "/rust-binaries" },
|
||||
{ text: "PE certificates", link: "/pe-certificates" },
|
||||
{ text: "PE imports", link: "/pe-imports" },
|
||||
{ text: "ELF notes", link: "/elf-notes" },
|
||||
{ text: "Apple IPS crash reports", link: "/apple-ips" },
|
||||
{ text: "CPython pyc files", link: "/python-pyc" },
|
||||
{ text: "Erlang BEAM", link: "/erlang-beam" },
|
||||
{
|
||||
text: "JavaScript and Electron",
|
||||
link: "/javascript-artifact-reconstruction",
|
||||
@@ -84,6 +89,10 @@ export default defineConfig({
|
||||
items: [
|
||||
{ text: "MCP contracts", link: "/mcp-contracts" },
|
||||
{ text: "Tool design", link: "/tool-design" },
|
||||
{
|
||||
text: "Admission limits and recovery",
|
||||
link: "/design/limits-and-recovery",
|
||||
},
|
||||
{ text: "Testing", link: "/testing" },
|
||||
{ text: "Client verification", link: "/client-verification" },
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ existing analysis families directly, for example
|
||||
| `read_adb_logcat` | read | Bounded `logcat -d -t` dump for one buffer, optional pid filter |
|
||||
| `list_adb_packages` | read | `pm list packages -f` with scope; base APK device paths |
|
||||
| `resolve_adb_packages` | read | Case-insensitive package-name substring search with exact-match flag |
|
||||
| `inspect_adb_package` | read | `dumpsys package` projection: versions, install times, installer, flags, permission count |
|
||||
| `inspect_adb_package` | read | `dumpsys package` projection: versions, install times, installer, flags, and count of listed requested-permission rows (including Unicode names) |
|
||||
| `list_adb_directory` | read | NUL-framed immediate children, including hidden entries: kinds, permissions, owner, group, sizes, symlink targets |
|
||||
| `pull_adb_package` | read | Complete split APK set from `pm path`, each file digested |
|
||||
| `pull_adb_file` | read | One absolute device path pulled and digested |
|
||||
@@ -103,6 +103,11 @@ existing analysis families directly, for example
|
||||
| `start_adb_activity` | **mutates device** | Starts an intent (`am start -a` with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
|
||||
| `stop_adb_app` | **mutates device** | Force-stops every process of a package, ending a dynamic-analysis run |
|
||||
|
||||
When `push_adb_file` receives an existing directory, adb places the local file
|
||||
inside it under the local basename; Evidence reports that effective file path
|
||||
while retaining the caller's directory path in the input parameters. The
|
||||
private read-only staging copy preserves the source file's executable bits.
|
||||
|
||||
For `inspect_adb_window`, `focus_contexts` retains each display header, its exact
|
||||
display ID when recognized, and every focus declaration with its original line.
|
||||
Declarations without a display header are explicitly `unscoped`. A missing
|
||||
@@ -131,13 +136,38 @@ and during shutdown.
|
||||
|
||||
## Interpret the result
|
||||
|
||||
- **Package numeric declarations retain failures.** `inspect_adb_package`
|
||||
admits the first valid `userId` or `appId` as `user_id`: a nonnegative safe
|
||||
integer, including zero. A null value means no valid declaration was found.
|
||||
Malformed declarations before or after a valid value remain in
|
||||
`numeric_issues`: the first ten issues with command, field, reason and original
|
||||
`raw_line`, plus the exact `total`. Reasons are `invalid_format` or
|
||||
`unsafe_integer`. Any issue makes package coverage partial; existing section
|
||||
omissions can also make it partial. `version_code` remains exact source text.
|
||||
- **Display nulls preserve uncertainty.** `inspect_adb_display` admits positive
|
||||
safe integers. Absent or malformed optional values are null; `numeric_issues`
|
||||
distinguishes malformed declarations from absence. It keeps the first ten
|
||||
issues and exact total, with command, field, original line and reason
|
||||
(`invalid_format`, `unsafe_integer` or `nonpositive`). The last recognized
|
||||
labeled declaration wins, even when malformed. A physical-density label
|
||||
suppresses bare-number fallback even when malformed; without that label, the
|
||||
first unsigned bare number wins, including zero or an unsafe integer that
|
||||
leaves the value null. Display coverage is complete only with known physical
|
||||
density and no numeric issues. Missing or malformed required physical size
|
||||
produces a protocol failure with `details.partial_observation`: an
|
||||
`inspect_adb_display` observation from provider `adb`, reason
|
||||
`physical_size_absent` or `physical_size_malformed`, and a `result` retaining
|
||||
client, serial, optional display facts, numeric issues, null physical size
|
||||
and partial coverage.
|
||||
- **Directory names retain whitespace and newlines.** Symlink targets do too,
|
||||
including trailing newlines. Records containing invalid UTF-8 remain as
|
||||
labelled hex in `unparsed_lines` and make directory coverage partial.
|
||||
- **Inferences carry their basis.** Device `kind` reports
|
||||
`kind_basis` (serial prefix or USB transport); APK `role` reports
|
||||
`role_basis: file_name`; `emulator_observed` is the build's own
|
||||
`ro.kernel.qemu` value, `null` when absent.
|
||||
`role_basis: file_name`; `emulator_observed` is derived from the build's
|
||||
`ro.kernel.qemu` property: `true` only when its value is `"1"`, `false` for
|
||||
any other reported value, and `null` when the property is absent. The raw
|
||||
property value remains available in `properties`.
|
||||
- **Device paths are not host paths.** `base_apk_device_path` values live on
|
||||
the device filesystem. Only pulls and captures produce host-local paths,
|
||||
each paired with byte size and SHA-256 digest. A pulled file's host name is
|
||||
@@ -235,3 +265,13 @@ Record: verified against adb 34.0.5 on Linux against an Android 14
|
||||
287 services, 92 features, a real two-APK split set pulled with byte-exact
|
||||
digests, a verified push/pull roundtrip, a 1.3 MB screen capture, and the
|
||||
complete package lifecycle; see `docs/testing.md` for the lane summary.
|
||||
|
||||
Intent integer extras use signed decimal text. The ADB argument encoder removes
|
||||
redundant leading zeros for Android's radix-sensitive `--ei` parser while retained
|
||||
Evidence keeps the caller's original spelling. Package inspection accepts both
|
||||
`userId` and Android 16's `appId` spelling for the legacy `user_id` result field.
|
||||
|
||||
A completed push and device digest verification are separate observations. A
|
||||
verification timeout, cancellation, or output budget failure returns the typed
|
||||
failure with partial Evidence of the completed transfer. An unavailable checksum
|
||||
command or unrecognized checksum output leaves verification explicitly unknown.
|
||||
+159
-18
@@ -165,7 +165,10 @@ Each such MCP tool call selects one of the records its manifest tool cites and
|
||||
returns that record's recorded response. Selection compares the supplied path
|
||||
parameters against the record's path segments at the parameter indexes, then
|
||||
the supplied query values against the record's query string, and keeps the
|
||||
record with the most matches; ties keep manifest order. The result reports
|
||||
record with the most matches; ties keep manifest order. A query argument accepts
|
||||
one string or an array of strings for repeated values; the array is matched
|
||||
against every occurrence in captured order. The raw recorded query remains the
|
||||
replay evidence. The result reports
|
||||
where the answer came from:
|
||||
|
||||
```json
|
||||
@@ -187,9 +190,14 @@ where the answer came from:
|
||||
}
|
||||
```
|
||||
|
||||
`exact_match` is true when every supplied path parameter and query value
|
||||
equals the recorded one; it says nothing about parameters the caller omitted.
|
||||
Recorded query parsing runs only when supplied query arguments affect selection.
|
||||
`exact_match` is true when every supplied path parameter and query occurrence
|
||||
matches the recorded one; it says nothing about parameters the caller omitted.
|
||||
Comparison probes retain the raw query spelling and its decoded parameter pairs
|
||||
in request order, including the interleaving of repeated values across names.
|
||||
Standalone tool calls express repeated values as per-name arrays; when matching
|
||||
records differ only in cross-name order, `exact_match` is false because those arguments cannot
|
||||
identify that order. Recorded query parsing runs only when supplied query
|
||||
arguments affect selection.
|
||||
A path value matches a recorded segment as written or after percent-decoding;
|
||||
when both kinds of match are available, the record whose segment equals the
|
||||
value as written is chosen. `recorded_query` keeps the `?` of an explicit empty
|
||||
@@ -365,7 +373,8 @@ shape and carries no annotations.
|
||||
Path parameters are required strings whose
|
||||
description names the segment index, the templating rule and the observed
|
||||
values (sampled within the list budget above; the manifest keeps them all);
|
||||
query parameter names are optional strings; write methods accept an
|
||||
query parameter names accept an optional string or array of strings (for
|
||||
repeated values in captured order); write methods accept an
|
||||
optional `body` object described by the recorded top-level key types. Each
|
||||
input keeps its own argument: a query name equal to a path parameter's name is
|
||||
advertised as `query_<name>`, a query name longer than 64 characters as
|
||||
@@ -380,7 +389,7 @@ data-plane operations and no children. Served
|
||||
operations and one intake extension instead, and the catalog holds the read
|
||||
tools and only the write tools whose effect the capture confirmed: a read tool
|
||||
is described as answering from the state where the state backs it, and a
|
||||
served write tool by the effect it applies to the state and the body members it
|
||||
served write tool by the effect it applies to the state and the quoted body members it
|
||||
accepts.
|
||||
|
||||
## Serve from the state
|
||||
@@ -422,24 +431,47 @@ member reports which applied:
|
||||
|
||||
Each read tool answers according to its view in the state:
|
||||
|
||||
| View | Answer |
|
||||
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `item` | The entity the path arguments name, rendered into the recorded envelope of `envelope_ordinal`. Arguments match as replay matches them: each collection segment equals the held collection's segment as written or after percent-decoding, and the identity is the last argument percent-decoded, then as written; values are never re-split on `/`. Any entity the state holds answers, including one `data/add` loaded; query arguments only decide `replay.exact_match`. An entity the state does not hold answers the tool's latest recorded 400, 404 or 410 response with `state.view` `missing`, or is a tool error when none was recorded; one lacking a field every recording carried is a tool error; a body is never invented. |
|
||||
| `list` | When the arguments name one of the lines the view checked exactly (its latest recordings before the first related write; every path and query value equal to the recording's, and every recorded query parameter supplied), that recorded envelope with its array rendered from the state: every entity of the collection in state order for `membership: "all"`, or the recorded members still held. A call with no query argument to a `membership: "all"` view answers from a checked line of the collection its path arguments name that only pages its list, with `replay.exact_match` false. A member lacking a field every recording carried makes the call a tool error rather than a partial list. |
|
||||
| `document` | When the arguments name the document's checked line exactly, the state's value for it; a tool error when the state holds none. |
|
||||
| `recorded` | The recording, as replay answers it. |
|
||||
| View | Answer |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `item` | The entity the path arguments name, rendered into the recorded envelope of `envelope_ordinal`. Arguments match as replay matches them: each collection segment equals the held collection's segment as written or after percent-decoding, and the identity is the last argument percent-decoded, then as written; values are never re-split on `/`. Any entity the state holds answers, including one `data/add` loaded; query arguments only decide `replay.exact_match`, except that a call matching one of the view's `projected_query_keys`, or on a view with `projected_record_ordinals` asking for some fields only with a value other than `*` no checked line gave or one a replayed read gave, replays its recording. An entity the state does not hold answers the tool's latest recorded 400, 404 or 410 response with `state.view` `missing`, or is a tool error when none was recorded; one lacking a field every recording carried is a tool error, unless a read showed the application leaves that field out; a body is never invented. |
|
||||
| `list` | When the arguments name one of the lines the view checked exactly (its latest recordings before the first related write; every path and query value equal to the recording's, and every recorded query parameter supplied), that recorded envelope with its array rendered from the state: every entity of the collection in state order for `membership: "all"` or a line `all_record_ordinals` names, or the recorded members still held. A call with no query argument answers from such a line of the collection its path arguments name that only pages its list, with `replay.exact_match` false. On a line the view's `query` answers, and for a call whose every query argument has a role there (an argument-less call aside), the answer is the query evaluated over the state: filtered, searched, sorted and paged, with counters at the number listed and `query.totals` at the number matched, and `state.membership: "query"`; such a call answers on a query or whole line of its collection with `replay.exact_match` false. A value an `inert` parameter never recorded, a parameter without a role, a call matching one of the view's `projected_query_keys`, or a field selection the checked lines do not show (as for `item`), replays its recording. A member lacking a field every recording carried makes the call a tool error rather than a partial list. |
|
||||
| `document` | When the arguments name the document's checked line exactly, the state's value for it; a tool error when the state holds none. |
|
||||
| `recorded` | The recording, as replay answers it. |
|
||||
|
||||
A read with repeated query values uses replay, because the state query model
|
||||
does not infer repeated-parameter semantics. A repeated-query write is refused
|
||||
before it can change state for the same reason.
|
||||
|
||||
List counters retain their recorded number or digit-string representation.
|
||||
Changing a digit-string counter preserves its observed zero-padding width;
|
||||
query-total inference remains limited to canonical decimal counts. Path matching
|
||||
uses the same segment parser as extraction, preserving empty internal segments.
|
||||
|
||||
An item or list view that always spells a clock field otherwise than the
|
||||
entity's stored value names that spelling in `clock_formats`, and a value it
|
||||
never recorded, such as a created entity's clock, is rendered in it: a list that
|
||||
writes `+00:00` where item reads write `Z` lists a new entity the same way.
|
||||
|
||||
A list or document call that names no checked line, including a line recorded
|
||||
after a write or with other query values, answers from the recording. Every
|
||||
answer has the replay shape plus `state`: `{"source": "state", "view": …}`
|
||||
naming the entity, collection or document path, or `{"source": "recording",
|
||||
"reason": …}` with the view's reason, `no-view`, `unrecorded-request` or `page-exceeded` (a call naming a page of a whole list that the collection has outgrown). A
|
||||
"reason": …}` with the view's reason, `no-view`, `unrecorded-request`, `page-exceeded` (a call naming a page of a whole list that the collection has outgrown), `projection` (a call to an item or list tool with replayed reads asking for a selection of fields the checked lines do not show, or leaving out a projection parameter every checked line names) or `variant` (a call matching another `projected_query_keys` key: a parameter, or a value of one, only lines showing the entities in another representation named, or leaving out a parameter every other line named). A `projection` or `variant` answer is a recording, so it does not show served writes. A
|
||||
state answer carries the base record's status, content type and `replay`
|
||||
provenance with `truncated: false`; one whose JSON would exceed 256 KiB is
|
||||
refused as a `resource_constraint` tool error rather than cut. Extraction does
|
||||
not state-back a document whose rendered JSON exceeds 256 KiB; that read stays
|
||||
recorded (`rendered-oversize`) and replays the captured body. Query `POST`s
|
||||
stay served and answer from their recordings with the reason `query-body`.
|
||||
recorded (`rendered-oversize`) and replays the captured body. Query `POST`s,
|
||||
and `POST` tools the capture shows only reading (a lookup answering as a `GET`
|
||||
did, an RPC tunnel naming read operations, a session renewal), stay served and
|
||||
answer from their recordings with the reason `query-body`; they are not write
|
||||
tools.
|
||||
|
||||
A line establishes whole-collection membership only from the beginning:
|
||||
`offset`, `start`, `_start` and `page` must be zero when present. Positive or
|
||||
malformed positions do not by themselves establish whole membership. A separate
|
||||
query model may describe supported paging; an empty later page cannot start
|
||||
returning earlier entities merely because it was empty in the capture.
|
||||
|
||||
`POST`, `PUT`, `PATCH` and `DELETE` tools are served only when the state's
|
||||
[write plan](web-network-captures.md#environment-state) for them is served:
|
||||
@@ -451,7 +483,7 @@ every MCP session and `data/get` then see:
|
||||
| Effect | Call |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `create` | Adds an entity to the collection the path names, built from the observed entity with each ruled field taken from the call or generated, at the plan's `insert` position. A plan tied to its exemplar's collection writes only there. |
|
||||
| `update` | Sets the sent members on the entity the path names, constants for members accepted only with their recorded value, and the clock, increment and slug changes the plan models. |
|
||||
| `update` | Sets the sent members on the entity the path names, constants for members accepted only with their recorded value, and the clock, increment, slug, unique and renewed changes the plan models. |
|
||||
| `delete` | Removes the entity the path names. |
|
||||
| `document` | Merges the body into the plan's document object, or replaces it for `PUT`. |
|
||||
|
||||
@@ -562,7 +594,10 @@ id are unique; `view_fields` is optional and names item or list views of its
|
||||
type. Views render their values over the entity's fields, so a view's values
|
||||
must not set the type's identity field to a value whose text differs from the
|
||||
entity's `id`. Spell a collection as requests spell it, as `data/get` reports it: list
|
||||
calls find entities under the checked line's recorded path. Each document
|
||||
calls find entities under the checked line's recorded path. State collection
|
||||
identity preserves repeated separators inside captured paths: `/api/books`
|
||||
and `/api//books` are distinct. A single trailing slash continues to name the
|
||||
same collection. Each document
|
||||
names a `document` view once, at its initial document's `path`. The `version`,
|
||||
and the `state_evidence_id` and `types` that `data/get` reports, must match
|
||||
the served state, so a `data/get` result loads back unchanged. A number without
|
||||
@@ -635,7 +670,7 @@ agent-env run ./grafana-example
|
||||
```text
|
||||
grafana-example/
|
||||
README.md provenance, what it serves, run command, rights note
|
||||
envs/grafana-example/Dockerfile pinned image: node:24.18.0-bookworm-slim + rea-agents@<this version>
|
||||
envs/grafana-example/Dockerfile pinned image: node:24.22.1-bookworm-slim + rea-agents@<this version>
|
||||
envs/grafana-example/env.toml environment_name = "grafana-example"
|
||||
envs/grafana-example/manifest.json byte-exact copy of manifest.json
|
||||
envs/grafana-example/evidence.json byte-exact copy of capture.json
|
||||
@@ -715,6 +750,112 @@ state is unavailable, and that neither is emulation and served
|
||||
content is untrusted data. It states that the recorded content belongs to the
|
||||
captured application and must not be published without the rights to it.
|
||||
|
||||
## Measure fidelity against the application
|
||||
|
||||
An environment agrees with its own capture by construction. What matters to an agent is how it answers the calls the
|
||||
capture did not make, what it shows after a write, and how it fails. `rea environment-compare` measures that against the
|
||||
application itself:
|
||||
|
||||
```sh
|
||||
rea environment-compare manifest.json --evidence capture.json \
|
||||
--probe probe-crud.json --probe probe-reads.json --json
|
||||
```
|
||||
|
||||
Each `--probe` is capture Evidence (`inspect-web-network-capture` of a HAR, or `capture-browser-scenario`) of one call
|
||||
sequence made against the live application, seeded as it was for the capture and reset before the sequence. The command
|
||||
makes no network call: whoever records the probes drives the application with its own credentials. For each probe it
|
||||
resets the environment's state, takes the requests in the order they were made (record order when any lacks a start time), matches each to the manifest tool of the
|
||||
same method whose path template fits with the most literal segments (a format-rule parameter must fit its format once
|
||||
percent-decoded). A path argument is the raw segment, which the environment decodes once.
|
||||
It answers the call as the served environment would, and compares that answer with the application's.
|
||||
|
||||
A re-seeded application holds the same entities under other ids, so the comparison maps them. Objects in the two answers
|
||||
to one call pair by a natural key, a string field unique on each side that both share, such as a name or a slug; their
|
||||
identifier fields (`id`, `uuid`, `guid`, and names ending in `_id`, `Id`, `ID`, `_uuid` or `Uuid`), and those of two single
|
||||
answers such as a create's, map the live value to the environment's, including a create that returns only the new id.
|
||||
Ids are learned only from an answer the environment
|
||||
gave that call itself, from its state or a recording of that very call, request line and body, never from another call's
|
||||
recording replayed in its place and never from a recording whose body the probe omitted. The answer's own `id`, `uuid` or `guid` also maps within the collection the call names, since
|
||||
two collections can reuse a live id, and an equal pair stays in that collection. A live id in a later request's path, query or body is translated before the
|
||||
environment answers, a path parameter by the collection the live path names (a nested collection keeps its parent values, each decoded once, and a literal route segment stays), except a query value that pages a list, such as `limit`
|
||||
or `page`. Each call is checked against the served tool's input schema first, as the MCP server checks it. After mapping, two clock values compare by format only (ISO-8601 strings, and epoch numbers under
|
||||
`at`, `time`, `date`, `timestamp`, `created`, `updated` or `modified` field names, including snake-case and camel-case forms).
|
||||
Numeric arrays retain that containing field's clock context through nested
|
||||
arrays; an object's own fields establish their own context. Numeric values at
|
||||
the body root, in root arrays, or under measurement fields remain literal.
|
||||
|
||||
Generated strings normalize only where this confirmed, served write's entity
|
||||
response rules identify generation. A `unique` rule or UUID identity rule must
|
||||
still compare two UUIDs; a `renewed` rule applies only at its recorded pointers,
|
||||
with `*` matching actual array items. Token/sequence strings retain the existing
|
||||
format check: equal length of at least eight, the same character classes, and
|
||||
a digit. Numeric sequences continue to compare literally unless an ID mapping
|
||||
applies. String shape and names such as `token` or `sku` alone do not establish
|
||||
generation. Read and replay responses without these facts stay literal except
|
||||
for supported clocks and learned IDs. A missing write target replays an absence
|
||||
response and does not inherit the successful entity response's generation rules.
|
||||
Admitted generated values must also preserve aliases and distinct identities
|
||||
within that response. UUID identity uses the generator's case-insensitive
|
||||
matching; token and sequence strings retain their literal identity. This
|
||||
correspondence ends with the answer and is not inferred across calls.
|
||||
The report counts each kind of normalised
|
||||
value; the generation rules remain model-derived facts, rather than new
|
||||
observations of the application.
|
||||
ISO clock offsets are accepted only within RFC 3339's `00`–`23` hour and `00`–`59` minute ranges.
|
||||
Probe ordering retains every recorded fractional-second digit in ISO timestamps
|
||||
with a four-digit year, `T`, and a `Z` or `±HH:MM` suffix. Equal instants use ordinal order. Other native-Date
|
||||
spellings keep their existing millisecond interpretation; an absent or unreadable
|
||||
start time makes the whole probe use record order. Original capture timestamps
|
||||
remain unchanged. Clock-format comparison and respelling recognize four-digit
|
||||
ISO years `0000`–`0099` as well as later years, with normal calendar validation.
|
||||
Respelling preserves the original value when the target zone would require a year
|
||||
outside `0000`–`9999`; generated writes refuse a clock outside that format.
|
||||
Capture-state start ordering uses the same exact recorded timestamp facts,
|
||||
including minute-only times and fractions longer than nine digits. Recorded
|
||||
spellings may use `T` or a space, `Z` or an extended/compact offset, or no zone
|
||||
(interpreted as UTC). Equal starts use ordinal order; a missing or unreadable
|
||||
start makes the capture use record order. Request interval and overlap checks
|
||||
still use millisecond coordinates, and generated-clock progression compares
|
||||
milliseconds. Derived ISO clock formats still require seconds and at most nine
|
||||
fractional digits.
|
||||
|
||||
Comparison keeps response representations distinct: JSON values compare structurally, text compares as text, and a
|
||||
retained base64 body compares by bytes (including with UTF-8 text carrying those same bytes), never by its base64
|
||||
spelling. ID mappings apply at identifier fields, `ids`/`uuids`/`guids` lists, an entity's own identity within its
|
||||
collection, and a string field learning confirmed as a state-held ID. ID mappings leave other numeric and string values unchanged. A
|
||||
global conflict involving an equal pair is not used to normalize either pair; the report counts it as an ID conflict.
|
||||
|
||||
A step agrees when the statuses and the normalised bodies are equal. The report gives agreement overall, per probe and
|
||||
per tool, `agreement_ignoring_order` for steps that differ only in item order, and each step's differences, at most 20,
|
||||
with a JSON pointer and both values cut short:
|
||||
|
||||
| Category | Meaning |
|
||||
| --------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `status` | The statuses differ |
|
||||
| `missing-field` | The application's object has a member the environment's lacks |
|
||||
| `extra-field` | The environment's object has a member the application's lacks |
|
||||
| `value` | Values differ, or one is an object or array and the other is not |
|
||||
| `length` | Arrays differ in length, or not every item pairs |
|
||||
| `order` | Paired items come in a different order |
|
||||
| `clock-format` | Both are clocks, in different formats |
|
||||
| `refused` | The environment refused the call |
|
||||
| `not-served` | The request's tool is one the environment does not offer, such as a withheld write |
|
||||
| `no-tool` | No manifest tool matches the request |
|
||||
| `unmapped-id` | The application answered with success and the environment did not, for a path id the comparison never mapped |
|
||||
|
||||
What a probe did not observe is not counted as the application's behaviour. A record without a usable request line,
|
||||
such as a redacted URL or a transaction the capture cannot attribute, is not replayed, nor is a request to an origin different
|
||||
from the application's (`other-origin`: the probe's origin with the most JSON-like records, as the manifest
|
||||
chooses its own), and a request with no recorded
|
||||
answer is made, since it may have changed the application, but not compared: each probe lists them under `unobserved` with the reason, and the summary counts them. A live
|
||||
body the probe did not record compares by status only and is counted as `unobserved_bodies`, unless the answer never
|
||||
carries one (204, 205, 304, `HEAD`). A browser probe captured without `capture.network.response_body` is refused, as
|
||||
is one that makes POST, PUT or PATCH calls without `capture.network.request_body`.
|
||||
|
||||
Agreement covers only the calls the probes make, after the mapping the answers allow. Clocks compared by format
|
||||
and fields normalized under explicit generation rules can still differ in content, and an equal answer after a write shows the state matched for that
|
||||
read, not that the write had no other effect.
|
||||
|
||||
## Compatibility verification
|
||||
|
||||
`npm run verify:agentenv:environment` is the hosted `real-agentenv` lane
|
||||
|
||||
@@ -171,8 +171,14 @@ the same source byte limit and explicit truncation reporting.
|
||||
## Interpret the result
|
||||
|
||||
- Package inspection retains decoded manifest XML, summary declarations,
|
||||
permissions and class/resource counts. Summary fields are extracted by the
|
||||
upstream engine and should be checked against the retained XML when needed.
|
||||
permissions and class/resource counts. REA's bridge selects the exact root
|
||||
`AndroidManifest.xml` resource by original archive name and manifest type,
|
||||
independently of asset order. One namespace-aware JDK XML parse supplies
|
||||
summary attributes and direct permission/SDK declarations; namespace prefixes
|
||||
and XML entity spelling do not change their values. Raw decoded XML is retained
|
||||
exactly and only its displayed text is subject to the existing source byte cap.
|
||||
Summaries use the complete selected XML before that cap. Document types are
|
||||
unsupported; parsing does not load external DTDs, schemas, or XInclude files.
|
||||
Signature verification is explicitly `not_performed`.
|
||||
- Class search filters the engine's cached top-level names before transferring
|
||||
pages, then follows contiguous inventory positions without rescanning earlier
|
||||
@@ -187,6 +193,13 @@ the same source byte limit and explicit truncation reporting.
|
||||
full reported display types, fields and inner class names. Synthetic members
|
||||
that code generation hides can appear in the inventory; it stays consistent
|
||||
after method decompilation within the retained session.
|
||||
Class and method inspection and reference tracing accept either the exact
|
||||
reported class name or its original binary name, including `$` in nested and
|
||||
anonymous classes. The bridge keeps both identities from JADX; it does not
|
||||
replace separators or guess suffix matches. Ambiguous selectors are refused.
|
||||
Follow-up calls preserve the selected name, while results report the frozen
|
||||
JADX display name. Quote `$` names in the shell, for example
|
||||
`'com.example.Manager$WorkerThread'`.
|
||||
Exact DEX prototypes are reported from frozen JADX method metadata as
|
||||
`dex_descriptor`, for example `(I)V`. Display names can be JADX aliases;
|
||||
the descriptor alone is not a complete raw DEX method ID. Overload indices
|
||||
@@ -256,6 +269,10 @@ such as
|
||||
`libreactnativejni.so`, `libreactnative.so`, `libhermes.so`,
|
||||
`libhermes_executor.so`, `libflutter.so`, and `libunity.so`; unrelated names
|
||||
such as `libcommunity.so` and `libreactivity.so` do not identify those families.
|
||||
The signing list retains the `META-INF/MANIFEST.MF`, `.SF`, `.RSA`, `.DSA`, and
|
||||
`.EC` paths selected by Android's v1 verifier, including nested paths. These are
|
||||
inventory metadata candidates, not proof that a v1 signature verifies or that
|
||||
the APK is signed; use `inspect-apk-signature` for signature analysis.
|
||||
The bridge basis is inferred from the native path and repeated for each managed
|
||||
component, so it does not identify a specific JNI binding. Bridge hypotheses form a DEX/JVM-class by native-library
|
||||
Cartesian product. The projection computes the product and exact serialized
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
# APK signature schemes
|
||||
|
||||
REA reads the signing of a selected local Android APK (or ZIP/AAB) statically,
|
||||
so an analyst can answer **who signed this, under which schemes, and whether the
|
||||
declared content digests match these bytes** before trusting or triaging it. The reader
|
||||
is in-process and needs no external engine: it parses the ZIP tail and the APK
|
||||
Signing Block, reads certificates with the platform X.509 parser, and
|
||||
recomputes the content digest. No Android code executes, and no device or
|
||||
`adb` is involved.
|
||||
|
||||
CLI and MCP share one workflow and return artifact-bound Evidence inline.
|
||||
|
||||
```sh
|
||||
rea inspect-apk-signature /path/app.apk
|
||||
```
|
||||
|
||||
## Selected facts and retained Evidence
|
||||
|
||||
MCP defaults to a summary with scheme presence, signer certificate fingerprints,
|
||||
row counts, posture and explicit coverage. `detail: "complete"` returns the full
|
||||
report. A selected page can answer a narrower question in the acquisition call:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/path/app.apk",
|
||||
"view": {
|
||||
"collection": "signers",
|
||||
"schemes": ["v3"],
|
||||
"fields": ["certificate_sha256"],
|
||||
"limit": 32
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The returned `parent_evidence_id` identifies the retained complete report.
|
||||
Subsequent reads use `inspect_analysis_view`, including after the APK is deleted:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "<returned parent_evidence_id>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "apk-signature",
|
||||
"collection": "digests",
|
||||
"signer_ids": ["<returned signer id>"],
|
||||
"fields": ["signature_algorithm_id", "algorithm", "matches_apk"],
|
||||
"offset": 0,
|
||||
"limit": 32
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Collections are `signers`, `digests` and declared `lineage` nodes. `ids`,
|
||||
`signer_ids` and `schemes` filters intersect; omitted filters select all available
|
||||
records and `[]` selects none. Omitted `fields` returns every collection column;
|
||||
`fields: []` retains identity and relationship links. IDs include the complete
|
||||
APK digest, scheme and source ordinal; child records retain `signer_id` and
|
||||
`signer_index`, and declared certificates retain their raw-DER SHA-256 identity.
|
||||
Lineage pages expose `previous_id` when selected. Certificate fingerprints can
|
||||
be composed across versions without treating a declaration as verified identity.
|
||||
|
||||
Page coverage describes examined declared records. Exhausting a page does not
|
||||
establish certificate authenticity, platform acceptance, v1 entry coverage or a
|
||||
verified rotation. Partial decoding and its reason remain visible even when a
|
||||
lineage page contains no rows. A weak cache reuses validated immutable report
|
||||
facts; each read checks its parent subject, and new file inspections read again.
|
||||
|
||||
CLI defaults to complete delivery. A compact one-shot response requires a complete
|
||||
Evidence export:
|
||||
|
||||
```sh
|
||||
rea inspect-apk-signature /path/app.apk --detail summary \
|
||||
--view '{"collection":"signers","fields":["certificate_sha256"]}' \
|
||||
--evidence_output /path/app-signing-evidence.json --json
|
||||
```
|
||||
|
||||
The export contains the same complete parent used by MCP. Historical CLI pages
|
||||
use `inspect-analysis-view` with an `evidence-file` source record. The CLI and MCP
|
||||
share the application workflow, projection, retention and publication owners.
|
||||
|
||||
## What it reports
|
||||
|
||||
- **Schemes present:** v1 (JAR signing, from the central directory), and the
|
||||
APK Signing Block's v2, v3 and v3.1. v1 follows apksig's selection: entries
|
||||
under `META-INF/` at any depth ending in `.SF`, or in `.RSA`/`.DSA`/`.EC`
|
||||
(case-sensitive). v1 is `present` only when a signature block has its
|
||||
matching `.SF` file (`META-INF/x/CERT.RSA` pairs with `META-INF/x/CERT.SF`);
|
||||
`signature_files` and `block_files` list every candidate either way.
|
||||
- **Per v1 signer** (each block paired with its `.SF`): REA inflates the block
|
||||
and reads its PKCS#7 `SignedData`. `certificate` is the certificate the
|
||||
block's one `SignerInfo` names by `issuerAndSerialNumber` or
|
||||
`subjectKeyIdentifier` (not by position), with the same facts and raw-DER
|
||||
SHA-256 as a v2/v3 signer, so a v1-only APK can be fingerprinted and
|
||||
clustered with v2/v3-signed ones. apksig resolves only
|
||||
`issuerAndSerialNumber`, so its v1 verification fails for a block whose
|
||||
`SignerInfo` uses `subjectKeyIdentifier`: REA still reports the declared
|
||||
certificate (`identified_by: subject-key-identifier`), adds a limitation,
|
||||
and treats that signer as unknown in `v1_signers_match_block_signers`. `certificates` lists every certificate the
|
||||
block carries (SHA-256 and subject) and `signer_infos` gives each
|
||||
`SignerInfo`'s certificate and digest/signature algorithm OIDs. The PKCS#7
|
||||
`SignedData` envelope is read as BER, including constructed signature OCTET
|
||||
STRINGs; each embedded certificate is still interpreted as DER by the X.509
|
||||
owner. Issuer matching preserves the encoded Name and also admits equivalent
|
||||
ASCII PrintableString/UTF8String values for CN, C, L, ST, O and OU: case and
|
||||
repeated/edge spaces are normalized, RDN sequence order is preserved, and
|
||||
attribute order inside one multivalued RDN is ignored. Other name forms
|
||||
require exact encoded identity; semantic equivalence remains unestablished.
|
||||
`certificate` is `null` when the block has more than one `SignerInfo` (with
|
||||
nothing verified there is no single signer) or names a certificate it does
|
||||
not carry. A block that cannot be read (malformed ASN.1, an unsupported
|
||||
compression method, Zip64 sizes, a local header that disagrees with the
|
||||
central directory on the name, data-descriptor flag, CRC-32 or a size, as
|
||||
apksig refuses it, or over a bound) keeps its names and carries
|
||||
`decode_error`; it is never reported as an absent signer. apksig ignores
|
||||
the ZIP encryption flag (bit 0), so a block that sets it is read as stored:
|
||||
the PKCS#7 decoding decides (bytes that really are encrypted fail to
|
||||
decode), and a limitation names the block.
|
||||
apksig rejects the v1 signature of an archive whose central directory
|
||||
holds any name more than once ("Duplicate entry"). REA checks every entry:
|
||||
a limitation names the first few duplicates (JSON-quoted; a name over 256
|
||||
bytes is cut there and its stored length given) and counts the rest, and a
|
||||
signer whose block, `.SF` or `META-INF/MANIFEST.MF` name is duplicated
|
||||
(one signer per block name) carries `decode_error`
|
||||
`duplicate entry: <name>`, so a duplicated manifest marks every signer.
|
||||
A duplicate outside those names leaves the signers as declared.
|
||||
Names are compared as apksig compares them, as Java strings decoded from
|
||||
UTF-8: malformed bytes become U+FFFD, so `dup\xff` and `dup\xfe` are one
|
||||
name (apksigner: `Duplicate entry: dup�`). REA reproduces Java's
|
||||
replacement rather than WHATWG `TextDecoder`'s; the two differ for encoded
|
||||
surrogates (`ED A0..BF`), which Java replaces as one unit. This was checked
|
||||
against OpenJDK 25's `new String(bytes, UTF_8)` on about 300,000 malformed
|
||||
and well-formed sequences, including a leading byte-order mark, which Java
|
||||
keeps; other Java runtimes are not checked. The same
|
||||
decoding names v1 entries and compares a local header's name with the
|
||||
central directory's; as in apksig, a local name longer in bytes than the
|
||||
directory's is refused before decoding. apksig takes the record count from the classic
|
||||
end-of-central-directory record's 16-bit field and ignores a Zip64 count,
|
||||
so it reads only the first 65,535 records of a larger directory: the
|
||||
limitation then says so, counts as duplicates for apksig only names whose
|
||||
second copy lies within those records, and names separately those
|
||||
duplicated only past them. REA still reads v1 signing entries, and flags
|
||||
duplicated signing names, from every record, so past record 65,535 it
|
||||
reports signers apksig would not see.
|
||||
Bytes after the block's `ContentInfo` are ignored, as apksig's BER parser
|
||||
ignores them, and a limitation names the block and their count.
|
||||
- **Per signer:** the certificate's **SHA-256 fingerprint over the raw embedded
|
||||
DER** (the same value `apksigner --print-certs` prints), plus subject, issuer,
|
||||
serial, ISO 8601 validity and public-key algorithm when the X.509 parser can
|
||||
read the exact carried DER (each is `null` otherwise). The parser's returned
|
||||
DER must equal those bytes before readable facts are combined with their
|
||||
fingerprint; embedded PEM text and trailing data cannot select a different
|
||||
certificate. Raw signature algorithm IDs, declared
|
||||
digest hex and digest algorithms, signature lengths and public-key length; and for
|
||||
v3/v3.1 the signer's declared min/max SDK.
|
||||
- **Key rotation (v3/v3.1):** the declared proof-of-rotation lineage, oldest
|
||||
first. Each node carries its certificate and the capability flags **retained
|
||||
by that node's own certificate** after rotation (installed data, shared UID,
|
||||
permission, rollback, auth). A lineage attribute that cannot be decoded is
|
||||
reported with `decode_error`, never silently emptied.
|
||||
- **Source stamp:** whether a source-stamp block is present and the stamp
|
||||
certificate it declares (the V2 stamp block is preferred over V1, as apksig
|
||||
does). `certificate` is `null` when no stamp is present, or when the block
|
||||
could not be decoded and `decode_error` says why. Stamp signatures are not
|
||||
verified.
|
||||
- **Declared digest consistency:** for SHA-256/SHA-512 digests, REA recomputes
|
||||
the v2/v3 content digest and reports `matches_apk` per digest and
|
||||
`posture.self_consistent`. This only shows that the **declared** digest
|
||||
equals the digest of the current bytes. It is **not a signature
|
||||
verification**: anyone who changes the bytes can recompute the digest and
|
||||
write it into the signed data, and REA still reports `true` while
|
||||
`apksigner verify` rejects the APK. A changed byte with an unchanged
|
||||
declared digest reads `false`.
|
||||
- **Posture** (derived from the facts above):
|
||||
- `v1_only`: v1 is present and no v2/v3/v3.1 block is. The v1
|
||||
`X-Android-APK-Signed` marker is not read, so this cannot distinguish an
|
||||
APK signed only with v1 from one whose v2/v3 block was removed.
|
||||
- `schemes_agree_on_signer` (observed): every present block scheme presents
|
||||
the same _set_ of signer certificates. An extra signer in one scheme is
|
||||
disagreement; a signer without a certificate makes it `null` (unknown).
|
||||
Build-tools 33+ rotate through v3.1 by default, so a rotated app usually
|
||||
reads `false` here and `true` below.
|
||||
- `signer_change_declared_by_rotation` (declared): the signers differ, but
|
||||
each appears in a lineage the APK declares. Read the two together: a
|
||||
rotated APK shows `false` / `true`; signers that differ with no declared
|
||||
lineage containing them show `false` / `false`. It is `null` when a
|
||||
signer's identity is unknown, or when the signers differ and a declared
|
||||
lineage could not be decoded.
|
||||
- `v1_signers_match_block_signers` (observed): the v1 signer certificates
|
||||
equal what the lowest block scheme that has signers (v2, else v3, else
|
||||
v3.1) expects v1 to carry. That is its signer set, except that a v3/v3.1
|
||||
signer declaring a rotation lineage expects the lineage's first
|
||||
(original) certificate, as apksig requires. `false` is notable: the JAR
|
||||
signature names a different key than the block signature. A key rotation
|
||||
keeps v1 (and v2) on the original key, so it reads `true` while
|
||||
`schemes_agree_on_signer` reads `false`, with or without v2. `null` when
|
||||
v1 or a block scheme is absent, any signer's identity is unknown (a
|
||||
`decode_error`, a `null` certificate or a `subjectKeyIdentifier`
|
||||
`SignerInfo`), or the lineage's original certificate cannot be
|
||||
identified; a limitation then says why.
|
||||
- `strongest_scheme`, `rotation_present`, `source_stamp_present`,
|
||||
`self_consistent`.
|
||||
|
||||
## What it does not claim
|
||||
|
||||
This is **declared identity and digest consistency, not trust**. No signature
|
||||
is cryptographically verified (including the v1 PKCS#7 signature and the
|
||||
`.SF`/`MANIFEST.MF` digests, so a v1 signer is reported even where `keytool`
|
||||
or `apksigner` reject the archive), certificate chains and revocation are not
|
||||
evaluated, and nothing here says an app is authentic. `self_consistent: true`
|
||||
means the declared digest equals the digest of the current bytes; it is not a
|
||||
signature verification, and it does not prove who made the bytes. Rotation-lineage node signatures are not verified either, so a lineage is
|
||||
what the APK _declares_: an attacker who re-signs an APK can declare any
|
||||
lineage, which is why `signer_change_declared_by_rotation` is kept apart from
|
||||
the observed agreement. SHA-256 identifies exact bytes, not authenticity.
|
||||
|
||||
Not yet read: v4 `.apk.idsig` (a sibling file), verity (`0x0421`/`0x0423`/`0x0425`) digests (reported with
|
||||
`matches_apk: null`), and the v1 `X-Android-APK-Signed` marker.
|
||||
|
||||
## Bounds and errors
|
||||
|
||||
The archive is read once, bounded at 512 MiB, without following symlinks and
|
||||
with change detection. ZIP tail offsets, the central-directory size and signing
|
||||
block spans used by the reader are checked against their containing sections,
|
||||
including the complete central-directory extent before ZIP end records. A process-wide queue permits
|
||||
one active whole-file inspection and eight pending scans across provider
|
||||
instances; pending scans retain no APK bytes and can be cancelled with their
|
||||
original reason. Further requests receive a capacity error. The active slot
|
||||
is held through parsing and cleanup. Signers per scheme, v1 signers and
|
||||
lineage nodes are capped at 32 each as resource admission limits, not format
|
||||
maxima (apksig separately accepts at most 10 v1 signers). Each v1 block is
|
||||
inflated to at most 1 MiB, and its BER is read within 32 levels of nesting and
|
||||
65,536 elements, with at most 32 certificates and 32 `SignerInfo`s; BER
|
||||
indefinite lengths and constructed OCTET STRING fields are accepted. Embedded
|
||||
X.509 certificates retain their DER requirement. The report's size is
|
||||
bounded from above by those counts before anything is materialised, and the exact
|
||||
serialized size of the report together with its limitations (which repeat
|
||||
archive names) is then checked against an 8 MiB output budget (the exact check
|
||||
is authoritative); an over-budget report is refused, not truncated.
|
||||
|
||||
The whole-file reader retains read chunks and a concatenated buffer; total
|
||||
process memory exceeds the selected file's size. It also computes the complete
|
||||
file SHA-256, so inspection is not header-only. Content digests are recomputed
|
||||
at most once per SHA-256/SHA-512 algorithm during one inspection. Input,
|
||||
inflation, decoded-record and serialized-output bounds keep work finite;
|
||||
resource refusals are distinct from malformed format errors.
|
||||
|
||||
A file that is not a ZIP is refused as an unsupported target. A damaged ZIP
|
||||
(local headers but no end-of-central-directory), a central directory that
|
||||
cannot be walked or whose declared record count does not account for all of
|
||||
its bytes, or a malformed signing block or signer is an input error naming the
|
||||
offset or scheme and reason. Zip64 tail damage includes a missing required
|
||||
locator, an impossible locator disk/count pair, a fixed end record that does
|
||||
not fit before its locator, or an incorrect end-record signature. These are
|
||||
input errors, not evidence of an unsupported format. Coherent multi-disk
|
||||
locator metadata and 64-bit offsets/counts/sizes beyond the reader's safe
|
||||
integer range remain unsupported. The locator offset is disk-relative, so
|
||||
multi-disk metadata is checked before applying local file bounds. This follows
|
||||
the record fields in [PKWARE APPNOTE 6.3.10, sections 4.3.14–4.3.15](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT);
|
||||
it does not establish full validation of Zip64 versions or extensible data.
|
||||
Signer framing includes signature records, the public-key field and matching
|
||||
signed/outer v3 SDK ranges. Suffix bytes beyond known
|
||||
fields are counted as uninterpreted, as Android readers may ignore extensions;
|
||||
their presence does not establish malformed signing.
|
||||
When the central directory is not immediately followed by
|
||||
the end-of-central-directory record (a declared-size mismatch or a Zip64
|
||||
record), the content digest is not recomputed and a limitation says so. A
|
||||
rotation attribute that cannot be decoded still counts as `rotation_present`
|
||||
(the attribute exists) and carries its `decode_error`.
|
||||
|
||||
Verified by the `verify:apk-signature` real lane. It builds a real minimal APK
|
||||
with `aapt2`, signs it with the SDK's `apksigner` (default v2+v3, v1-only with
|
||||
RSA and EC keys, v1+v2+v3 on a legacy target, a rotated key lineage with and
|
||||
without v1, rotation carried by v3.1, a source stamp, and a copy
|
||||
with one changed byte), and cross-checks REA against
|
||||
`apksigner verify --print-certs`, `apksigner lineage --print-certs` and
|
||||
`keytool -printcert -jarfile` through the CLI and MCP.
|
||||
|
||||
The real-toolchain CI lane uses pinned, checksum-verified Build Tools 36.1.0
|
||||
and platform 36 oracle files in its job workspace. It requires the existing
|
||||
host JDK and runs `npm run verify:apk-signature -- --require-tools`; absent
|
||||
oracles fail rather than silently establishing provider coverage. REA itself
|
||||
requires none of these external tools.
|
||||
@@ -93,9 +93,21 @@ still suppresses all string projection, including in this mode.
|
||||
`target.bytes` bind the observation to the exact APK that was decoded.
|
||||
The Evidence subject carries the same snapshot digest and caller-selected
|
||||
path, including when the original file changes during decoding.
|
||||
A change during snapshot acquisition reports retryable `artifact_changed`;
|
||||
wait for a stable input before retrying. Host read permission failures report
|
||||
`access_denied`, while a missing or non-regular selected path reports
|
||||
`invalid_request` with the rejected `path` field.
|
||||
- **Metadata comes from apktool.yml as apktool printed it.** Version codes
|
||||
keep their own spelling (`'7'` versus `7`); `package_name` is read from
|
||||
the decoded manifest, not inferred.
|
||||
Apktool 3's emitted `version: 3.x.y` header selects its single-line string
|
||||
dialect: literal hashes, single quotes, empty strings, and the text `null`
|
||||
stay part of the value; producer backslashes and Unicode escapes decode
|
||||
once. Older/tagged documents retain YAML scalar semantics; double-quoted
|
||||
values also retain YAML string semantics.
|
||||
This distinction follows the released
|
||||
[3.0.3 metadata writer](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/VersionInfo.java#L49-L55)
|
||||
and [string escape owner](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlStringEscapeUtils.java#L55-L116).
|
||||
- **The manifest is the decoded text**, bounded at 512 KiB and reported as
|
||||
truncated when larger.
|
||||
- **Strings are projected verbatim.** Escaped entities stay escaped, and
|
||||
@@ -167,6 +179,11 @@ Multi-locale and table-only projection were also verified through real CLI and
|
||||
stdio MCP workflows on Linux with Apktool 3.0.3/JDK 21 against NewPipe 0.29.1,
|
||||
ApiDemos 6.0.18 and Appium Settings 8.2.1. Independent XML projection checks,
|
||||
exact facts/Evidence parity and bounded task costs are recorded in the
|
||||
[shared-decode and table-only checkpoint](../experiments/analysis-efficiency/apktool-string-tables.md).
|
||||
[shared-decode and table-only checkpoint](https://github.com/morluto/rea/blob/42e1a17d6e6c0487f17be0bae248553c4caba7f7/experiments/analysis-efficiency/apktool-string-tables.md).
|
||||
This qualifies selected resource facts, not application execution or independent
|
||||
binary resource-decoder completeness.
|
||||
|
||||
If the selected APK changes while its private snapshot is copied, acquisition
|
||||
returns `artifact_changed`; request-schema correction cannot recover a stable
|
||||
snapshot. Launcher spawn permission denial is distinct from a missing launcher,
|
||||
and configuration diagnostics retain the selected command and system error.
|
||||
@@ -156,6 +156,10 @@ anatomy components.
|
||||
The projection does not read plists. Use `inspect_plist` on a bundle's
|
||||
`info_plist_path` or a launchd plist to read `CFBundleExecutable`, identifiers,
|
||||
`SMPrivilegedExecutables`, `BundleProgram`, URL schemes, and usage descriptions.
|
||||
MCP calls require an existing `open_binary` or `open_bundle_resources` session,
|
||||
including when `inspect_plist.path` is explicit. For a bundle without a resolved
|
||||
executable, open its resource session first. The CLI `rea inspect-plist <path>`
|
||||
opens the selected artifact for its one invocation.
|
||||
Use `inspect_signature` on an executable candidate for code-signing claims.
|
||||
|
||||
## Dylib load resolution
|
||||
@@ -209,6 +213,9 @@ Resolution follows dyld:
|
||||
already loaded image reuses it.
|
||||
- Symlinks such as `Versions/Current` are followed inside the analyzed root.
|
||||
An escape through `..` or an absolute link is `escapes-target`.
|
||||
- Dyld path components are POSIX names. On Windows hosts, link text uses `\`
|
||||
as its separator, and a dyld component that contains `\` or `:` cannot name
|
||||
a file in the analyzed root, so its candidate is `absent`.
|
||||
|
||||
Each edge lists every candidate path tried, with its outcome: `resolved`,
|
||||
`absent`, `not-mach-o`, `malformed`, `unsupported` (recognized Mach-O outside
|
||||
@@ -220,9 +227,16 @@ satisfy a dylib dependency. `platform-mismatch` means a matching-architecture
|
||||
dylib was found but its observed platform is incompatible with the process.
|
||||
These outcomes reject that candidate; dyld search may continue and resolve a
|
||||
later candidate. Candidate outcomes describe individual search paths, while
|
||||
`resolution.status` describes the final edge. Slices follow dyld's graded
|
||||
architectures, so an `x86_64h` process also loads `x86_64`. The edge's
|
||||
`resolution` is one of:
|
||||
`resolution.status` describes the final edge. For each selected process-root
|
||||
slice, candidate slices are matched by CPU type and dyld's subtype preference
|
||||
order, independently of FAT table order. An ARMv7s root slice prefers ARMv7s,
|
||||
then ARMv7; other known ARM32 subtypes use their compatibility rows from
|
||||
[Apple's dyld source](https://github.com/apple-oss-distributions/dyld/blob/dyld-852.2/src/dyld2.cpp#L2811-L3105).
|
||||
An `x86_64h` process also loads generic `x86_64`. Unknown ARM32 subtype policy
|
||||
remains undetermined when ARM32 candidate slices exist. This is a static model
|
||||
keyed to the selected root slice; it does not observe the target device's host
|
||||
CPU or prove which slice the device will execute. The edge's `resolution` is
|
||||
one of:
|
||||
|
||||
| Status | Meaning |
|
||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -235,7 +249,7 @@ A `conditional` image is still traced, because it is an in-root candidate
|
||||
that may load. Every edge below it has `loader_conditional: true`, and a
|
||||
request that reuses it by install name is `conditional` too. An in-root dylib
|
||||
with no platform load command remains unknown unless the root is an executable
|
||||
with observed macOS platform and the candidate’s CPU type is x86_64, i386, or arm64. In that macOS context,
|
||||
with observed macOS platform and the candidate's CPU type is x86_64, i386, or arm64. In that macOS context,
|
||||
[Apple's dyld](https://github.com/apple-oss-distributions/dyld/blob/fd8d0c4d52320ebf64db34f3cb280310d905c5ae/mach_o/UnsafeHeader.cpp#L1137-L1145) supplies an implicit macOS platform to legacy dylibs on those CPU
|
||||
types. The candidate records `platform_basis: "macos-dyld-implicit"`; the
|
||||
reported slice fields `platform: null` and `platforms: []` remain the parsed
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# Apple IPS crash reports
|
||||
|
||||
`inspect_apple_ips` / `inspect-apple-ips` answers whether one selected local
|
||||
Apple `.ips` file is a crash report (`bug_type` string `"309"`), which process,
|
||||
exception, and termination facts that report records, which threads and
|
||||
`usedImages` rows it lists, and which documented crash classes its strings
|
||||
match. It reads the file without executing a target and does not require Xcode,
|
||||
atos, a disassembler, or pwntools.
|
||||
|
||||
```bash
|
||||
rea inspect-apple-ips /absolute/path/to/report.ips --json
|
||||
```
|
||||
|
||||
The CLI resolves relative paths against the caller's working directory. MCP
|
||||
requires an absolute filesystem path and preserves that exact selection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_apple_ips",
|
||||
"arguments": { "path": "/artifacts/report.ips" }
|
||||
}
|
||||
```
|
||||
|
||||
## Observations
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
`metadata` is the first-line JSON object. `bug_type` stays a string. The crash
|
||||
body is parsed only when that string is `"309"`. Other types, including `"288"`
|
||||
stackshots, return no crash result.
|
||||
|
||||
`process`, `exception`, `termination`, `faulting_thread`, `threads`, and
|
||||
`images` reproduce documented body fields when present. Integers keep their
|
||||
decimal literals and a hexadecimal form, including arm64e registers above
|
||||
2^53. Frame roles follow the recorded frame index: row zero of the resolved
|
||||
faulting thread is `faulting_instruction`, row zero of other threads is
|
||||
`current_instruction`, and later rows are `return_address`. Each runtime
|
||||
address uses that row's own image base and offset. The recorded `inline` flag
|
||||
does not supply a missing offset or authorize borrowing another row's address.
|
||||
When `faultingThread` is missing or does not resolve to a recorded thread
|
||||
object, row-zero roles are `unknown`; the recorded lookup remains available.
|
||||
`process.os_version` keeps the producer's dictionary (`train`, `build`,
|
||||
`release_type`, `is_embedded`) or the older string form. Image UUIDs are uppercase 8-4-4-4-12; an all-zero UUID
|
||||
or a missing `arch` is `uuid_unknown`. Image `source` letters keep their
|
||||
documented meaning (`P` process, `S` shared cache, `C` shared cache library,
|
||||
`K` kernel, `U` kernel cache, `T` kernel text exec, `A` absolute). Keys the
|
||||
documentation does not list remain in `unlisted_keys` or `unlisted_body_keys`.
|
||||
|
||||
Each frame's `image_index` refers to its entry in `images`. Image architecture,
|
||||
UUID and `uuid_unknown` are retained once on that image. Resolve the reference
|
||||
when using those facts; an absent or out-of-range index leaves the image
|
||||
unknown. This avoids repeating image text for every stack frame.
|
||||
|
||||
A present value whose JSON type differs from the documented one is never read
|
||||
as absent: it is listed in `coverage.type_mismatches` with its path, the
|
||||
expected type, and the observed type, and `coverage.status` becomes
|
||||
`partial`. A crash body without `threads` or `usedImages` is also `partial`.
|
||||
Malformed image UUID strings likewise make coverage partial; their exact spelling
|
||||
is retained in the mismatch's `recorded_value`, while the canonical image UUID
|
||||
remains unknown. Both 32 hexadecimal digits and canonical hyphenated UUIDs are accepted.
|
||||
Unresolved thread/image references also make coverage partial. A rejected
|
||||
integer lookup keeps its exact decimal literal in `recorded_integer`, including
|
||||
negative values and values above uint32. Valid uint32 lookup values remain in
|
||||
`faulting_thread` or `image_index` even when the referenced row is unavailable.
|
||||
|
||||
`classification` matches only documented SIGKILL codes (compared by their
|
||||
canonical hexadecimal form), `EXC_GUARD` subtypes,
|
||||
pointer-authentication suffixes, and dyld missing-library strings. A report
|
||||
that matches none of those rules is `unclassified`.
|
||||
|
||||
Opened Mach-O and dSYM UUID joins remain unobserved. Symbol names printed
|
||||
inside the report are recorded claims and are not verified. Linux ELF cores
|
||||
use the separate [recorded crash](recorded-crashes.md) workflow.
|
||||
|
||||
## Limits and failures
|
||||
|
||||
The selected file is limited to 1 MiB by default (16 MiB maximum). At most 256
|
||||
threads, 8192 frames, 1024 `usedImages` rows, and 16384 `threadState`
|
||||
register slots are examined by default (4096 / 65536 / 4096 / 262144
|
||||
maximum). Budgets are checked before each row is parsed, and body members the
|
||||
report does not interpret are syntax-checked without being kept. Fixed
|
||||
allowances bound JSON nesting (64 levels), unlisted key names per object
|
||||
(1024), and `termination.reasons` entries (256). Malformed metadata, a
|
||||
truncated crash body, invalid UTF-8, a NUL in the text, and exhausted budgets
|
||||
fail without partial success.
|
||||
|
||||
`max_integer_text_bytes` (`--max-integer-text-bytes` in the CLI) separately
|
||||
bounds retained decimal and hexadecimal integer text, including every derived
|
||||
runtime address and rejected lookup literal. Its default is 8 MiB of UTF-16 text storage and its maximum is
|
||||
64 MiB. Each appearance counts, even when an object could be shared in memory
|
||||
but would be serialized more than once. The parser reserves a conservative
|
||||
upper bound before numeric conversion or address expansion, then releases the
|
||||
unused reservation after each value. It reuses the parsed source integer when
|
||||
the same image base contributes to many frames.
|
||||
|
||||
Raise this per-call allowance on the next call when more room is needed; no
|
||||
restart is required. File-size and row budgets do not raise it. This allowance
|
||||
is separate from JSON wire size, MCP response limits and the client's receive
|
||||
buffer. An exhausted allowance reports a typed resource error without partial
|
||||
success or truncated integer facts.
|
||||
|
||||
```bash
|
||||
rea inspect-apple-ips /absolute/path/to/report.ips --max-integer-text-bytes 16777216 --json
|
||||
```
|
||||
|
||||
This workflow does not execute a target, launch atos, or search for dSYMs.
|
||||
@@ -1,4 +1,6 @@
|
||||
graph TD
|
||||
%% Selected layer owners and dependency/wiring paths, not a directory or import inventory.
|
||||
%% Current deep-provider candidates are wired in src/composition/binary.ts.
|
||||
subgraph "Entry Points"
|
||||
CLI["src/cli.ts + src/cli/<br/>One-shot CLI adapter<br/>setup, diagnostics, analysis, decompilation"]
|
||||
MAIN["src/main.ts + src/main/<br/>MCP server adapter: stdio default, streamable HTTP opt-in<br/>config, session, process lifetime"]
|
||||
@@ -25,16 +27,22 @@ graph TD
|
||||
JS_RECON["src/application/javascript/ + src/artifacts/javascript/<br/>JavaScript artifact reconstruction<br/>directory/ASAR reads<br/>AST-only bundle projection"]
|
||||
end
|
||||
|
||||
subgraph "Provider Layers"
|
||||
subgraph "Provider Layers (selected adapters)"
|
||||
PROVIDER_OWNERS["src/providers/<br/>shared deferred client and inspection owners<br/>exact cleanup retry and attributed failures"]
|
||||
PWNTOOLS["src/native/pwntools/ + src/native/pwndbg/<br/>BYO offline ELF/core adapters<br/>owned snapshots/Python and optional core-only GDB<br/>unchanged upstream parsers, ABI structures and mapping API"]
|
||||
WASM["src/wasm/<br/>caller-supplied WABT; immutable artifact inspection<br/>owned process supervision and retryable workspace cleanup"]
|
||||
APKSIG["src/apkSignature/<br/>in-process APK Signing Block + v1 reader<br/>bounded single read, no device or execution"]
|
||||
EVM["src/evm/<br/>unchanged EVMole/WASM in bounded owned worker<br/>explicit carrier parsing and inferred interface adapter"]
|
||||
HOPPER["src/hopper/<br/>Hopper launch & Unix-socket protocol<br/>BridgeLauncher, HopperClient, protocol"]
|
||||
GHIDRA["src/ghidra/<br/>Ghidra discovery, profile & function analysis<br/>snapshot, Unix/Windows transport, bounded lifecycle"]
|
||||
IDA["src/ida/<br/>Upstream MCP contract adaptation<br/>live GUI binding or owned headless database"]
|
||||
KUNA["src/kuna/<br/>BYO Kuna deep analysis candidate<br/>discovery reads metadata; version probe on target open"]
|
||||
IDA_MCP["External ida-pro-mcp<br/>GUI proxy or database supervisor<br/>upstream installation and engine lifecycle"]
|
||||
|
||||
KUNA["src/kuna/<br/>BYO Kuna decompiler, a deep analysis candidate<br/>discovery reads metadata only; version probe when a target opens"]
|
||||
AUX["src/artifacts/ + src/native/<br/>artifact inventory, bounded directory triage and extraction<br/>format readers and native capabilities"]
|
||||
|
||||
|
||||
BROWSER["src/browser/<br/>CDP / Playwright capture<br/>captured scripts and native module resolution<br/>execution/: coverage, listener and source attribution"]
|
||||
JS_CODEC["src/javascript/sourceMaps/<br/>pinned upstream map codec<br/>owned heap-limited Node process, complete point evidence"]
|
||||
INSPECTOR["src/inspector/<br/>passive V8 Inspector discovery and capture<br/>script and target locations"]
|
||||
@@ -46,12 +54,19 @@ graph TD
|
||||
FIRMWARE["src/firmware/<br/>Binwalk / Unblob adapters<br/>owned processes, report normalization, private extraction"]
|
||||
ANDROID["src/android/<br/>Static APK adapter<br/>owned SDK stdio, pinned headless JADX, normalization"]
|
||||
REFERENCE["src/reference/<br/>Filesystem reference-source reader<br/>bounded traversal, validation, errors"]
|
||||
|
||||
ANDROID_TOOLS["src/adb/ + src/apktool/ + src/jeb/<br/>BYO device, resource-decoding and JEB adapters<br/>owned adb and Apktool processes; JEB engine client over MCP"]
|
||||
CAPTURE_TOOLS["src/captures/ + src/pcap/ + src/signals/ + src/sqlite/<br/>BYO tshark, pcap, sigrok and SQLite child inspection<br/>selected captures, recordings and databases; owned processes"]
|
||||
NATIVE_READERS["src/go/ + src/rust/ + src/il2cpp/ + src/flutter/ + src/gpu/<br/>Bundled in-process binary metadata readers<br/>Go build info, Rust dependencies, IL2CPP, Dart build ID, GPU code objects<br/>src/compression/: shared codec-result helper for rust and gpu"]
|
||||
FORMAT_READERS["src/model/ + src/python/ + src/erlang/ + src/electron/<br/>Bundled in-process format readers<br/>GGUF and safetensors headers, Python .pyc, Erlang BEAM, Electron fuses"]
|
||||
RUNTIME["src/frida/ + src/cutter/<br/>Frida instrumentation sessions and live Cutter bridge client"]
|
||||
end
|
||||
|
||||
subgraph "Provider Process Foundation"
|
||||
PROCESS["src/process/<br/>owned process groups, private roots<br/>deadlines, bounded file reads and diagnostics, cleanup"]
|
||||
WINDOWS["src/windows/<br/>Windows host boundary adapters<br/>package loader, opaque handles, owned jobs and private runtimes"]
|
||||
WINDOWS_NATIVE["native/windows/src/<br/>Node-API implementation<br/>NTFS admission, protected DACLs and atomic Job Object assignment"]
|
||||
FILESYSTEM["src/filesystem/<br/>owned file and directory handles<br/>stable reads, staged output, caller paths"]
|
||||
end
|
||||
|
||||
subgraph "Contracts Layer"
|
||||
@@ -71,8 +86,8 @@ graph TD
|
||||
TESTS["tests/<br/>Vitest suite<br/>fixtures/ holds fake launcher, bridge, and CDP seams"]
|
||||
end
|
||||
|
||||
subgraph "Evaluation"
|
||||
EVALUATION["src/evaluation/<br/>Agent release evaluation<br/>session transcript scoring"]
|
||||
subgraph "Development Evaluation"
|
||||
EVALUATION["tests/evaluation/model-evals/<br/>Agent release evaluation<br/>session transcript scoring"]
|
||||
end
|
||||
|
||||
DISPATCH --> CLI
|
||||
@@ -101,6 +116,8 @@ graph TD
|
||||
SERVER -->|narrow record ports| RECORDS
|
||||
RECORDS --> DOMAIN
|
||||
COMPOSITION --> WASM
|
||||
COMPOSITION --> APKSIG
|
||||
APKSIG --> DOMAIN
|
||||
WASM --> PROCESS
|
||||
WASM --> DOMAIN
|
||||
COMPOSITION --> PWNTOOLS
|
||||
@@ -109,6 +126,7 @@ graph TD
|
||||
COMPOSITION --> HOPPER
|
||||
COMPOSITION --> GHIDRA
|
||||
COMPOSITION --> IDA
|
||||
COMPOSITION --> KUNA
|
||||
COMPOSITION --> AUX
|
||||
IDA --> IDA_MCP
|
||||
IDA --> CONTRACTS
|
||||
@@ -139,6 +157,7 @@ graph TD
|
||||
GHIDRA --> GHIDRA_BRIDGE
|
||||
HOPPER --> PROCESS
|
||||
GHIDRA --> PROCESS
|
||||
KUNA --> PROCESS
|
||||
GHIDRA --> PROVIDER_OWNERS
|
||||
APP --> PROVIDER_OWNERS
|
||||
PROVIDER_OWNERS --> PROCESS
|
||||
@@ -156,6 +175,7 @@ graph TD
|
||||
CONTRACTS --> DOMAIN
|
||||
HOPPER --> DOMAIN
|
||||
GHIDRA --> DOMAIN
|
||||
KUNA --> DOMAIN
|
||||
AUX --> DOMAIN
|
||||
BROWSER --> DOMAIN
|
||||
PROCESS --> DOMAIN
|
||||
@@ -172,3 +192,23 @@ graph TD
|
||||
APP -->|EVM interface port| EVM
|
||||
EVM --> PROCESS
|
||||
EVM --> DOMAIN
|
||||
COMPOSITION --> KUNA
|
||||
KUNA --> PROCESS
|
||||
KUNA --> FILESYSTEM
|
||||
KUNA --> DOMAIN
|
||||
COMPOSITION --> ANDROID_TOOLS
|
||||
ANDROID_TOOLS --> PROCESS
|
||||
ANDROID_TOOLS --> DOMAIN
|
||||
COMPOSITION --> CAPTURE_TOOLS
|
||||
CAPTURE_TOOLS --> PROCESS
|
||||
CAPTURE_TOOLS --> DOMAIN
|
||||
COMPOSITION --> NATIVE_READERS
|
||||
NATIVE_READERS --> DOMAIN
|
||||
COMPOSITION --> FORMAT_READERS
|
||||
FORMAT_READERS --> DOMAIN
|
||||
SERVER --> RUNTIME
|
||||
CLI --> RUNTIME
|
||||
RUNTIME --> DOMAIN
|
||||
PROCESS --> FILESYSTEM
|
||||
AUX --> FILESYSTEM
|
||||
APP --> FILESYSTEM
|
||||
@@ -0,0 +1,118 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="263" viewBox="0 0 960 263" role="img" aria-labelledby="title desc">
|
||||
<title id="title">REA showcases</title>
|
||||
<desc id="desc">Three illustrations. A brick-breaker screen with a speaker on each side stands for the DX-Ball sound-pan showcase. A browser window joined by a wire to a clipboard stands for the Notion clipboard-bridge showcase. A ring of 24 dots with one orange dot around a dark centre stands for the TH04 bullet-ring showcase.</desc>
|
||||
<rect width="960" height="263" fill="#11111b"/>
|
||||
<rect x="43" y="16.5" width="262" height="235.5" fill="#181825"/>
|
||||
<rect x="354" y="14.5" width="258" height="237.5" fill="#181825"/>
|
||||
<rect x="664" y="16.5" width="260" height="235.5" fill="#181825"/>
|
||||
<g id="dx-ball">
|
||||
<rect x="108.5" y="54" width="140" height="160" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="106.85" y="52.1" width="136" height="157" fill="#181825" stroke="#cdd6f4" stroke-width="3.7"/>
|
||||
<rect x="110.9" y="55.9" width="128.2" height="149.6" fill="none" stroke="#6c7086" stroke-width="2.2"/>
|
||||
<rect x="112.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="145.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="178.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="211.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="112.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="145.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="178.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="211.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="111.8" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<rect x="144.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<rect x="177.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<rect x="210.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<rect x="111.8" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<rect x="144.79999999999998" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<rect x="177.79999999999998" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<rect x="210.79999999999998" y="75.0" width="26.2" height="11.9" fill="#45475a" stroke="#cdd6f4" stroke-width="2.4"/>
|
||||
<ellipse cx="173.4" cy="157.2" rx="8.4" ry="8.2" fill="#000000" fill-opacity="0.45"/>
|
||||
<ellipse cx="171.9" cy="154.5" rx="7.3" ry="7.2" fill="#585b70" stroke="#cdd6f4" stroke-width="2"/>
|
||||
<rect x="165.5" y="149.5" width="3.2" height="2.6" fill="#a6adc8"/>
|
||||
<rect x="145.5" y="195.2" width="57.6" height="12.4" fill="#585b70" stroke="#cdd6f4" stroke-width="2.2"/>
|
||||
<rect x="144.5" y="204" width="59.3" height="6.5" fill="#cdd6f4"/>
|
||||
<g transform="translate(0 0)">
|
||||
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#585b70" stroke="#cdd6f4" stroke-width="2.3" stroke-linejoin="miter"/>
|
||||
<path d="M78.1 106V143" stroke="#cdd6f4" stroke-width="3.4" fill="none"/>
|
||||
<g fill="none" stroke="#89b4fa" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
|
||||
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
|
||||
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
|
||||
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="translate(190 0)">
|
||||
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#585b70" stroke="#cdd6f4" stroke-width="2.3" stroke-linejoin="miter"/>
|
||||
<path d="M78.1 106V143" stroke="#cdd6f4" stroke-width="3.4" fill="none"/>
|
||||
<g fill="none" stroke="#89b4fa" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
|
||||
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
|
||||
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
|
||||
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="clipboard-bridge">
|
||||
<rect x="390.3" y="83.6" width="100.6" height="82.6" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="387.7" y="81.2" width="97.8" height="79.7" fill="#181825" stroke="#cdd6f4" stroke-width="2.9"/>
|
||||
<rect x="389.2" y="82.7" width="94.9" height="13" fill="#45475a"/>
|
||||
<path d="M387.7 97H485.5" stroke="#cdd6f4" stroke-width="3.1"/>
|
||||
<rect x="394.7" y="86.6" width="4.6" height="5" fill="#fab387"/>
|
||||
<rect x="402.7" y="86.6" width="4.6" height="5" fill="#f9e2af"/>
|
||||
<rect x="410.8" y="86.6" width="4.6" height="5" fill="#a6adc8"/>
|
||||
<rect x="398" y="107.4" width="10.6" height="9.4" fill="#7f849c"/>
|
||||
<rect x="412.4" y="107.9" width="16.8" height="6" fill="#7f849c"/>
|
||||
<rect x="434" y="107.9" width="42.4" height="6.2" fill="#7f849c"/>
|
||||
<rect x="412.2" y="117.3" width="64" height="2.6" fill="#6c7086"/>
|
||||
<rect x="412.2" y="124.9" width="55.3" height="3.6" fill="#6c7086"/>
|
||||
<rect x="412.2" y="133.2" width="50.3" height="3.8" fill="#6c7086"/>
|
||||
<rect x="412.2" y="142" width="13.2" height="3.4" fill="#6c7086"/>
|
||||
<rect x="430" y="142" width="14.4" height="3.4" fill="#6c7086"/>
|
||||
<rect x="449" y="142" width="27.6" height="3.2" fill="#313244"/>
|
||||
<rect x="412.2" y="150" width="12.6" height="2.9" fill="#6c7086"/>
|
||||
<path d="M455.2 162V187H476M497 187H508.2V163.2H523" fill="none" stroke="#89b4fa" stroke-width="3" stroke-linejoin="miter"/>
|
||||
<rect x="477.5" y="178.3" width="23" height="21" fill="#000000" fill-opacity="0.45"/>
|
||||
<path d="M484.7 173.2V177" stroke="#cdd6f4" stroke-width="2.2"/>
|
||||
<rect x="476.4" y="177.5" width="20.2" height="18.1" fill="#585b70" stroke="#cdd6f4" stroke-width="2.2"/>
|
||||
<rect x="481.4" y="178.6" width="4.6" height="16" fill="#313244"/>
|
||||
<rect x="477.2" y="191" width="4.6" height="4.4" fill="#f9e2af"/>
|
||||
<rect x="481.6" y="183.8" width="3.4" height="3" fill="#fab387"/>
|
||||
<rect x="525.4" y="114.6" width="60.6" height="75" fill="#000000" fill-opacity="0.45"/>
|
||||
<rect x="524.7" y="112.5" width="56.4" height="69.4" fill="#45475a" stroke="#cdd6f4" stroke-width="3"/>
|
||||
<rect x="531.6" y="118.6" width="42.3" height="53.4" fill="#1e1e2e"/>
|
||||
<rect x="531.6" y="172" width="42.3" height="8.4" fill="#45475a"/>
|
||||
<path d="M531.6 175.8H575" stroke="#cdd6f4" stroke-width="1.8" stroke-opacity="0.8"/>
|
||||
<path d="M547.4 103.3H556.6V106.2H559.6V109.2H564.9V120.3H540.1V109.2H545V106.2H547.4Z" fill="#585b70" stroke="#cdd6f4" stroke-width="2" stroke-linejoin="miter"/>
|
||||
<rect x="549.4" y="108.4" width="2.8" height="1.9" fill="#181825"/>
|
||||
<rect x="536.6" y="131.8" width="32.3" height="3.4" fill="#7f849c"/>
|
||||
<rect x="536.6" y="138.8" width="32.3" height="3.4" fill="#7f849c"/>
|
||||
<rect x="536.6" y="145.8" width="32.3" height="3.4" fill="#7f849c"/>
|
||||
<rect x="536.6" y="152.8" width="26.8" height="3.4" fill="#7f849c"/>
|
||||
<rect x="536.6" y="159.8" width="14.6" height="3.4" fill="#7f849c"/>
|
||||
</g>
|
||||
<g id="bullet-ring">
|
||||
<circle cx="783" cy="74.5" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="798" cy="72" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="812.5" cy="74.5" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="769.5" cy="81" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="826.5" cy="81" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="756.5" cy="90" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="839" cy="90" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="746.5" cy="103" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="849" cy="103" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="740.5" cy="117" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="854.5" cy="117" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="738" cy="134" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="856.5" cy="133.5" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="741" cy="149" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="854" cy="149" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="747.5" cy="163" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="848" cy="163" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="757.5" cy="175" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="770" cy="184" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="826.5" cy="184" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="838.5" cy="175" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="784" cy="191" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="798" cy="192.5" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="812" cy="190.5" r="3.8" fill="#89b4fa"/>
|
||||
<circle cx="833.5" cy="109" r="4.1" fill="#fab387"/>
|
||||
<circle cx="797.4" cy="133" r="10.9" fill="#cdd6f4"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,118 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="263" viewBox="0 0 960 263" role="img" aria-labelledby="title desc">
|
||||
<title id="title">REA showcases</title>
|
||||
<desc id="desc">Three illustrations. A brick-breaker screen with a speaker on each side stands for the DX-Ball sound-pan showcase. A browser window joined by a wire to a clipboard stands for the Notion clipboard-bridge showcase. A ring of 24 dots with one orange dot around a dark centre stands for the TH04 bullet-ring showcase.</desc>
|
||||
<rect width="960" height="263" fill="#fafbfb"/>
|
||||
<rect x="43" y="16.5" width="262" height="235.5" fill="#f4f6f5"/>
|
||||
<rect x="354" y="14.5" width="258" height="237.5" fill="#f4f6f5"/>
|
||||
<rect x="664" y="16.5" width="260" height="235.5" fill="#f4f6f5"/>
|
||||
<g id="dx-ball">
|
||||
<rect x="108.5" y="54" width="140" height="160" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="106.85" y="52.1" width="136" height="157" fill="#f4f6f5" stroke="#191a1d" stroke-width="3.7"/>
|
||||
<rect x="110.9" y="55.9" width="128.2" height="149.6" fill="none" stroke="#a5a6a9" stroke-width="2.2"/>
|
||||
<rect x="112.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="145.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="178.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="211.6" y="58.5" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="112.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="145.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="178.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="211.6" y="77.39999999999999" width="28.4" height="14.3" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="111.8" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<rect x="144.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<rect x="177.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<rect x="210.79999999999998" y="57.900000000000006" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<rect x="111.8" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<rect x="144.79999999999998" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<rect x="177.79999999999998" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<rect x="210.79999999999998" y="75.0" width="26.2" height="11.9" fill="#5a636d" stroke="#191a1d" stroke-width="2.4"/>
|
||||
<ellipse cx="173.4" cy="157.2" rx="8.4" ry="8.2" fill="#000000" fill-opacity="0.27"/>
|
||||
<ellipse cx="171.9" cy="154.5" rx="7.3" ry="7.2" fill="#4d5863" stroke="#191a1d" stroke-width="2"/>
|
||||
<rect x="165.5" y="149.5" width="3.2" height="2.6" fill="#aeb1b4"/>
|
||||
<rect x="145.5" y="195.2" width="57.6" height="12.4" fill="#525c65" stroke="#191a1d" stroke-width="2.2"/>
|
||||
<rect x="144.5" y="204" width="59.3" height="6.5" fill="#191a1d"/>
|
||||
<g transform="translate(0 0)">
|
||||
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#56616b" stroke="#191a1d" stroke-width="2.3" stroke-linejoin="miter"/>
|
||||
<path d="M78.1 106V143" stroke="#191a1d" stroke-width="3.4" fill="none"/>
|
||||
<g fill="none" stroke="#274b77" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
|
||||
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
|
||||
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
|
||||
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="translate(190 0)">
|
||||
<path d="M61.5 117.5H69.6L78 108.5V141.2L69.6 132.8H61.5Z" fill="#56616b" stroke="#191a1d" stroke-width="2.3" stroke-linejoin="miter"/>
|
||||
<path d="M78.1 106V143" stroke="#191a1d" stroke-width="3.4" fill="none"/>
|
||||
<g fill="none" stroke="#274b77" stroke-width="2.2" stroke-linecap="butt" stroke-dasharray="2.6 1.4">
|
||||
<path d="M84.8 118.2Q87.6 125 84.8 131.8"/>
|
||||
<path d="M90.2 112.4Q94.5 125 90.2 137.6"/>
|
||||
<path d="M95.8 108.2Q101.5 125 95.8 141.8"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="clipboard-bridge">
|
||||
<rect x="390.3" y="83.6" width="100.6" height="82.6" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="387.7" y="81.2" width="97.8" height="79.7" fill="#f4f6f5" stroke="#191a1d" stroke-width="2.9"/>
|
||||
<rect x="389.2" y="82.7" width="94.9" height="13" fill="#5a636d"/>
|
||||
<path d="M387.7 97H485.5" stroke="#191a1d" stroke-width="3.1"/>
|
||||
<rect x="394.7" y="86.6" width="4.6" height="5" fill="#c3693b"/>
|
||||
<rect x="402.7" y="86.6" width="4.6" height="5" fill="#be9a53"/>
|
||||
<rect x="410.8" y="86.6" width="4.6" height="5" fill="#b3b3b4"/>
|
||||
<rect x="398" y="107.4" width="10.6" height="9.4" fill="#666671"/>
|
||||
<rect x="412.4" y="107.9" width="16.8" height="6" fill="#666c74"/>
|
||||
<rect x="434" y="107.9" width="42.4" height="6.2" fill="#666c74"/>
|
||||
<rect x="412.2" y="117.3" width="64" height="2.6" fill="#7a7c82"/>
|
||||
<rect x="412.2" y="124.9" width="55.3" height="3.6" fill="#7a7c82"/>
|
||||
<rect x="412.2" y="133.2" width="50.3" height="3.8" fill="#6d7279"/>
|
||||
<rect x="412.2" y="142" width="13.2" height="3.4" fill="#6d7279"/>
|
||||
<rect x="430" y="142" width="14.4" height="3.4" fill="#6d7279"/>
|
||||
<rect x="449" y="142" width="27.6" height="3.2" fill="#e8e8e8"/>
|
||||
<rect x="412.2" y="150" width="12.6" height="2.9" fill="#6d7279"/>
|
||||
<path d="M455.2 162V187H476M497 187H508.2V163.2H523" fill="none" stroke="#274b77" stroke-width="3" stroke-linejoin="miter"/>
|
||||
<rect x="477.5" y="178.3" width="23" height="21" fill="#000000" fill-opacity="0.27"/>
|
||||
<path d="M484.7 173.2V177" stroke="#191a1d" stroke-width="2.2"/>
|
||||
<rect x="476.4" y="177.5" width="20.2" height="18.1" fill="#525c65" stroke="#191a1d" stroke-width="2.2"/>
|
||||
<rect x="481.4" y="178.6" width="4.6" height="16" fill="#323435"/>
|
||||
<rect x="477.2" y="191" width="4.6" height="4.4" fill="#be9a53"/>
|
||||
<rect x="481.6" y="183.8" width="3.4" height="3" fill="#c3693b"/>
|
||||
<rect x="525.4" y="114.6" width="60.6" height="75" fill="#000000" fill-opacity="0.27"/>
|
||||
<rect x="524.7" y="112.5" width="56.4" height="69.4" fill="#545e69" stroke="#191a1d" stroke-width="3"/>
|
||||
<rect x="531.6" y="118.6" width="42.3" height="53.4" fill="#ededec"/>
|
||||
<rect x="531.6" y="172" width="42.3" height="8.4" fill="#545e69"/>
|
||||
<path d="M531.6 175.8H575" stroke="#191a1d" stroke-width="1.8" stroke-opacity="0.8"/>
|
||||
<path d="M547.4 103.3H556.6V106.2H559.6V109.2H564.9V120.3H540.1V109.2H545V106.2H547.4Z" fill="#545e69" stroke="#191a1d" stroke-width="2" stroke-linejoin="miter"/>
|
||||
<rect x="549.4" y="108.4" width="2.8" height="1.9" fill="#f4f6f5"/>
|
||||
<rect x="536.6" y="131.8" width="32.3" height="3.4" fill="#706f74"/>
|
||||
<rect x="536.6" y="138.8" width="32.3" height="3.4" fill="#706f74"/>
|
||||
<rect x="536.6" y="145.8" width="32.3" height="3.4" fill="#706f74"/>
|
||||
<rect x="536.6" y="152.8" width="26.8" height="3.4" fill="#706f74"/>
|
||||
<rect x="536.6" y="159.8" width="14.6" height="3.4" fill="#706f74"/>
|
||||
</g>
|
||||
<g id="bullet-ring">
|
||||
<circle cx="783" cy="74.5" r="3.8" fill="#274b77"/>
|
||||
<circle cx="798" cy="72" r="3.8" fill="#274b77"/>
|
||||
<circle cx="812.5" cy="74.5" r="3.8" fill="#274b77"/>
|
||||
<circle cx="769.5" cy="81" r="3.8" fill="#274b77"/>
|
||||
<circle cx="826.5" cy="81" r="3.8" fill="#274b77"/>
|
||||
<circle cx="756.5" cy="90" r="3.8" fill="#274b77"/>
|
||||
<circle cx="839" cy="90" r="3.8" fill="#274b77"/>
|
||||
<circle cx="746.5" cy="103" r="3.8" fill="#274b77"/>
|
||||
<circle cx="849" cy="103" r="3.8" fill="#274b77"/>
|
||||
<circle cx="740.5" cy="117" r="3.8" fill="#274b77"/>
|
||||
<circle cx="854.5" cy="117" r="3.8" fill="#274b77"/>
|
||||
<circle cx="738" cy="134" r="3.8" fill="#274b77"/>
|
||||
<circle cx="856.5" cy="133.5" r="3.8" fill="#274b77"/>
|
||||
<circle cx="741" cy="149" r="3.8" fill="#274b77"/>
|
||||
<circle cx="854" cy="149" r="3.8" fill="#274b77"/>
|
||||
<circle cx="747.5" cy="163" r="3.8" fill="#274b77"/>
|
||||
<circle cx="848" cy="163" r="3.8" fill="#274b77"/>
|
||||
<circle cx="757.5" cy="175" r="3.8" fill="#274b77"/>
|
||||
<circle cx="770" cy="184" r="3.8" fill="#274b77"/>
|
||||
<circle cx="826.5" cy="184" r="3.8" fill="#274b77"/>
|
||||
<circle cx="838.5" cy="175" r="3.8" fill="#274b77"/>
|
||||
<circle cx="784" cy="191" r="3.8" fill="#274b77"/>
|
||||
<circle cx="798" cy="192.5" r="3.8" fill="#274b77"/>
|
||||
<circle cx="812" cy="190.5" r="3.8" fill="#274b77"/>
|
||||
<circle cx="833.5" cy="109" r="4.1" fill="#c3693b"/>
|
||||
<circle cx="797.4" cy="133" r="10.9" fill="#1c222a"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
@@ -29,6 +29,16 @@ the selected object as a host process, or requests runtime library resolution. T
|
||||
uses isolated mode and an owned cache. Exact upstream profiles are recorded in
|
||||
[upstream provenance](https://github.com/morluto/rea/blob/main/third_party/pwntools/README.md).
|
||||
|
||||
The adapter skips pwntools kernel configuration and version discovery: those
|
||||
memory-content scans supply none of this report's fields. Ordinary `IKCFG_ST`
|
||||
data is not interpreted as a kernel-config request, and virtual zero-filled
|
||||
LOAD tails are reported from their headers without materializing them for that
|
||||
scan. GOT/PLT inference and the advertised static mitigation indicators still
|
||||
use the pinned upstream implementations. Header-table file ranges are checked
|
||||
before dependency seeks. Structural inventory and the convenience-map engine
|
||||
still parse the snapshot separately; eliminating that remaining duplication
|
||||
requires preserving the upstream GOT/PLT and mitigation facts.
|
||||
|
||||
## Interpreting results
|
||||
|
||||
- Artifact path, SHA-256 and size identify original selected bytes. All parsing
|
||||
@@ -90,6 +100,10 @@ one section or symbol, or a stable page without re-running the decoder. CLI
|
||||
one-shot invocations pass portable inline Evidence JSON; an MCP session uses
|
||||
the retained `evidence_id`. Page `limit` is required and bounded by the
|
||||
measured MCP stdio budget documented on the tool contract.
|
||||
Malformed authenticated layout input remains rejected with the owning schema's
|
||||
safe field diagnostics under `source.normalized_result`, including the field
|
||||
path and type or value reason. Evidence authentication and decoder validation
|
||||
remain separate boundaries.
|
||||
|
||||
Over MCP, `inspect_binary_layout` with `"detail": "summary"` retains the
|
||||
complete layout Evidence in the session and returns only its summary view.
|
||||
@@ -170,6 +184,31 @@ subject to earlier cancellation or the startup deadline. `output_incomplete`
|
||||
marks a snapshot taken before stream closure; cleanup ownership remains with
|
||||
the session until release succeeds.
|
||||
|
||||
A kernel or container OOM killer ends Ghidra with `SIGKILL` and prints no
|
||||
`OutOfMemoryError`. When a process or protocol failure records
|
||||
`exit_signal: "SIGKILL"` and no `stop_requested`, REA reports the signal
|
||||
instead of generic `rea doctor` advice. The process supervisor records
|
||||
`stop_requested: true` once REA begins stopping Ghidra, before it sends any
|
||||
signal, so REA's own SIGKILL is never reported as an outside kill.
|
||||
|
||||
On Linux with cgroup v2, REA also reads its own cgroup's `memory.events` before
|
||||
launch and again after the kill, locating the group through the cgroup2 mount
|
||||
in `/proc/self/mountinfo`. `memory_cgroup` records the group path,
|
||||
`oom_kill_before`, `oom_kill_after` and `memory_max`. If the count rose, REA
|
||||
returns `resource_constraint` with `resource: "memory"` and `cgroup_memory_max`
|
||||
in `reported_limits`. The count covers every process in REA's cgroup and its
|
||||
child groups, which can include processes outside REA's process tree when the
|
||||
group is shared, so a rise shows that the kernel OOM killer ended a process in
|
||||
that cgroup or a child group, not which process it chose. `memory_max` is the
|
||||
limit in bytes, `"max"` when the group sets none, or `null` when it could not be
|
||||
read; a limit on a parent group is not read. The limit also covers Ghidra's
|
||||
native decompiler processes, whose number follows the available CPUs, so the
|
||||
advice does not recommend a larger Java heap. When the session had already
|
||||
completed an operation, the advice keeps the instruction to reopen the target
|
||||
with `open_binary`. Without a cgroup v2 memory group, or when the count did not
|
||||
rise, the failure stays `execution_failure` and names the signal as a possible
|
||||
out-of-memory kill.
|
||||
|
||||
## Portable PE resources
|
||||
|
||||
`inspect_pe_resources` / `inspect-pe-resources` inspects an explicit PE32 or
|
||||
@@ -193,6 +232,14 @@ reported. Directory headers, entries, data entries and payloads retain original
|
||||
file offsets and lengths. Each opaque payload has its own SHA-256; code page and
|
||||
reserved data-entry fields are preserved.
|
||||
|
||||
Every `directories[]` row retains its identity `path` and the matching incoming
|
||||
`entry_locations`: both are empty for the root, then contain the type and
|
||||
optionally the name. Empty branches keep these identities even when they have
|
||||
no payload leaves. Shared directory bytes produce a row for each incoming path,
|
||||
with the original directory range and each path's own entry locations. These
|
||||
rows are included in the metadata budget. A present empty root remains distinct
|
||||
from an absent resource data directory (`directory: null`, `directories: []`).
|
||||
|
||||
RT_GROUP_ICON records expose original image descriptors and RT_ICON candidates.
|
||||
A reference resolves only when an icon with the same numeric ID and language
|
||||
exists. Missing references, other-language candidates and declared-size mismatch
|
||||
@@ -299,3 +346,118 @@ arm64 host and Node version. Cold disk I/O, token counts and an agent
|
||||
investigation were not measured; there was no previous equivalent tool to
|
||||
benchmark. These figures do not establish a portable peak-memory bound,
|
||||
constant memory for retained Evidence or faster agent task completion.
|
||||
|
||||
## Portable native exports
|
||||
|
||||
`inspect_native_exports` / `inspect-native-exports` reads the export table of an
|
||||
explicit PE or ELF image on any supported host, without an active target,
|
||||
native engine, `objdump` or Windows SDK:
|
||||
|
||||
```sh
|
||||
rea inspect-native-exports ./runtimes/linux-x64/native/libexample.so --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_native_exports",
|
||||
"arguments": { "path": "/artifacts/native/example.dll" }
|
||||
}
|
||||
```
|
||||
|
||||
The complete Evidence identifies the stable artifact snapshot by path, byte
|
||||
count and SHA-256. Complete delivery remains the default. For a small initial
|
||||
MCP reply, select `detail: "summary"`; this retains the complete Evidence and
|
||||
returns artifact identity, table coverage and row counts. Use its
|
||||
`normalized_result.parent_evidence_id` with `inspect_analysis_view`:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<parent digest>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "native-exports",
|
||||
"collection": "exports",
|
||||
"names": ["selected_name"],
|
||||
"fields": ["name"],
|
||||
"offset": 0,
|
||||
"limit": 32
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Names match exactly and preserve every same-name version or alias. Optional
|
||||
`indices` selects original row positions, intersecting a name filter when both
|
||||
are supplied; empty filters select nothing. `source_index` always remains and,
|
||||
with the parent ID, identifies the original export or issue row. Fields default
|
||||
to the complete row; an empty field list returns only its identity. ELF `name`
|
||||
selection also returns raw-byte and encoding qualifications. Fields from the
|
||||
other format are rejected. `collection: "issues"` pages the complete diagnostic
|
||||
records, with optional original indices. Offset and limit default to 0 and 64
|
||||
and apply to matching rows in original order.
|
||||
|
||||
View `coverage` describes the selected page; `summary.coverage` or
|
||||
`exports.coverage` preserves source-table coverage. An exhausted or empty page
|
||||
does not make a partial source complete. Retained views never reopen the file.
|
||||
The CLI uses the same projector and requires a durable bundle for a summary:
|
||||
|
||||
```sh
|
||||
rea inspect-native-exports ./libexample.so --detail summary --evidence_output ./exports.bundle.json --json
|
||||
# Pass the summary's parent ID and the same view as JSON to inspect-analysis-view,
|
||||
# together with --evidence-bundle ./exports.bundle.json.
|
||||
```
|
||||
|
||||
- PE rows reproduce the export directory: each used export-address-table slot
|
||||
with its biased `ordinal` (`OrdinalBase` plus the slot index), one row per
|
||||
name (aliases share an ordinal), `address`
|
||||
as the preferred-base virtual address, or `forwarder` text when the slot
|
||||
forwards to another DLL. Unused zero-address slots are omitted;
|
||||
ordinal-only exports have a null `name`. The name-ordinal table stores an
|
||||
unbiased 16-bit EAT index; reported biased ordinals may exceed 16 bits. The
|
||||
directory's DLL name, ordinal base and timestamp are reported, and an image
|
||||
without an export directory is a complete empty inventory.
|
||||
- ELF rows list defined `.dynsym` symbols with global, weak or GNU-unique
|
||||
binding and default or protected visibility. Each row keeps `value`
|
||||
(`st_value` as stored; bit 0 marks Thumb code on 32-bit ARM), `size`, `type`
|
||||
and `binding`. ELF string tables do not declare a text encoding. Names that
|
||||
contain non-ASCII bytes are decoded as UTF-8 when those bytes form valid
|
||||
UTF-8; `name_utf8_decodable` records whether the non-ASCII name bytes are
|
||||
valid UTF-8 and `name_bytes_base64` retains those exact bytes (`null` for
|
||||
ASCII names). When they are not valid UTF-8, `name` uses a byte-preserving
|
||||
Latin-1 mapping and the raw bytes remain available. Older retained results
|
||||
report null for the newly added encoding fields. `version` is null for an
|
||||
unversioned symbol. A versioned
|
||||
symbol reports its GNU index, a name resolved from `.gnu.version_d` or
|
||||
`.gnu.version_r`, the corresponding `name_bytes_base64` and
|
||||
`name_utf8_decodable` facts, and the `hidden` flag from `.gnu.version`.
|
||||
Version names use the same UTF-8-when-valid, otherwise byte-preserving
|
||||
Latin-1 display mapping as symbol names; ELF does not declare either
|
||||
string's text encoding. Older retained results report null for the added
|
||||
version-name encoding fields. `"unknown"`
|
||||
means the symbol-version table could not be read. A hidden version is not
|
||||
the default version that a by-name `dlsym` lookup selects.
|
||||
Definition names take precedence when both tables name the same index.
|
||||
|
||||
Unreadable names, out-of-range name ordinals and unreadable forwarders are
|
||||
listed in `issues`, their entries are not reported, and coverage is `partial`.
|
||||
PE export names are bounded null-terminated byte strings and may continue
|
||||
across adjacent file-backed section mappings when the virtual bytes are
|
||||
unambiguous. Gaps, zero-fill and conflicting mappings remain unreadable names.
|
||||
Unresolved symbol versions keep their rows: the version name is null when no
|
||||
definition or requirement names the index, and `version` is
|
||||
`"unknown"` when the version table cannot be read; coverage is likewise
|
||||
`partial`. A dynamic symbol table without its null entry is malformed. An ELF
|
||||
without section headers is `unsupported`. With section headers, a missing
|
||||
`SHT_DYNSYM` section is also `unsupported` when a `PT_DYNAMIC` segment is
|
||||
observed, including a directory containing only the inactive null section.
|
||||
Enumerating dynamic symbols from that segment is not implemented. A present
|
||||
section directory with neither a dynamic symbol table nor a dynamic segment
|
||||
reports a complete empty inventory. Mach-O exports are reported by
|
||||
`inspect_macho`.
|
||||
|
||||
`max_file_bytes` defaults to 256 MiB (maximum 512 MiB; CLI `--max-file-bytes`).
|
||||
Export directories in PE headers are read like those in sections. An export or
|
||||
dynamic symbol table above 262144 entries, more than 262144 rows, or more than
|
||||
16 MiB of retained names fails without partial success. Export rows do not establish that a symbol is reachable, safe to call
|
||||
or loaded at runtime.
|
||||
+155
-18
@@ -12,16 +12,30 @@ For request/response bodies and traffic debugging, see
|
||||
[Fiddler Everywhere captures and MCP](web-network-captures.md#reading-fiddler-captures-saz).
|
||||
Inspect native SAZ or HAR 1.2 offline with REA, or connect Everywhere's separate MCP server.
|
||||
|
||||
HTTP discovery and browser WebSocket inputs require an explicit port. An
|
||||
explicit default `:80` remains valid and is retained in the canonical endpoint;
|
||||
omitting the port remains invalid. HTTP `/json/list` discovery preserves usable
|
||||
siblings when optional metadata is malformed: attachment becomes unknown and
|
||||
row/field diagnostics appear in `limitations`. Target identity and socket
|
||||
authority are validated independently before selection and connection.
|
||||
|
||||
## Shipped surfaces
|
||||
|
||||
- `list_browser_targets` / `rea list-browser-targets` discovers all eligible page targets at the endpoint by default, with an optional exact-origin filter.
|
||||
- `inspect_web_page` / `rea inspect-web-page` captures DOM structure, accessibility nodes, scripts, resources, safe response/DOM metadata, attach-window network and console metadata, WebSocket frame sizes, workers, quota, and optionally selected storage key names, redacted storage-content fingerprints, or script sources.
|
||||
- `analyze_web_bundle` / `rea analyze-web-bundle` parses selected script artifacts without execution and derives chunk edges, route and endpoint candidates, vendor fingerprints, static WebMCP declarations, and optionally source-map/original-source evidence.
|
||||
- `observe_web_session` / `rea observe-web-session` arms an observation window of the requested duration for an external user action and records ordered reload, SPA navigation, redirect, failure, lifecycle, and target-termination metadata.
|
||||
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return every in-scope registration and its complete observed `input_schema` declaration and canonical `input_schema_sha256` digest inline. Declarations are untrusted JSON data, including declared types, required fields, enums, examples and reference URLs. REA does not resolve schema references. These fields replace `input_schema_shape`. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. Declared `readOnly`, `untrustedContent`, `consequential`, and `autosubmit` annotation hints are reported per registration: `true`/`false` are declared values, `null` means the declaration did not provide the hint in that capture, and a missing `consequential` key means the hint was not recorded, either in a historical capture or because the observed annotation was invalid. Consult discovery completeness and exclusions to distinguish these cases. Malformed annotation values or registration stacks retain the registration with incomplete coverage and an `invalid_protocol_value` exclusion. Other malformed protocol payloads can fail discovery explicitly. REA never exposes `WebMCP.invokeTool`.
|
||||
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return observed in-scope registrations and their complete observed `input_schema` declaration and canonical `input_schema_sha256` digest inline. Declarations are untrusted JSON data, including declared types, required fields, enums, examples and reference URLs. REA does not resolve schema references. An omitted optional `inputSchema` is a valid parameterless registration, represented by null schema and digest. These fields replace `input_schema_shape`. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. Declared `readOnly`, `untrustedContent`, `consequential`, and `autosubmit` annotation hints are reported per registration: `true`/`false` are declared values, `null` means the declaration did not provide the hint in that capture, and a missing `consequential` key means the hint was not recorded, either in a historical capture or because the observed annotation was invalid. Consult discovery completeness and exclusions to distinguish these cases. Malformed annotation values or registration stacks retain the registration with incomplete coverage and an `invalid_protocol_value` exclusion. Other malformed protocol payloads can fail discovery explicitly. REA never exposes `WebMCP.invokeTool`.
|
||||
|
||||
- `compare_web_captures` / `rea compare-web-captures` compares passive captures or exact-step-aligned browser scenarios. Scenario results commit reproducible literal normalization and expose alignment failures plus artifact-level action, screenshot, DOM, accessibility, URL, history, storage, and event differences. Missing or truncated evidence is never treated as equivalence.
|
||||
|
||||
For a compact route query, select `detail: "summary"` and a
|
||||
`view: {kind: "javascript-routes", collection: "routes", limit: 20}`. The
|
||||
shared [route inventory](javascript-route-inventory.md) preserves source IDs,
|
||||
precise locations, partial coverage, and the complete parent Evidence for
|
||||
subsequent filtered pages. Static candidates never establish complete application
|
||||
coverage or reachability.
|
||||
|
||||
For `analyze_web_bundle`, each `webmcp_declarations` entry's
|
||||
`schema_property_names` is a source-derived projection of statically known own
|
||||
property keys, not schema validation. `schema_property_names_coverage` reports
|
||||
@@ -47,6 +61,49 @@ from a protocol omission. Rejected DOM facts also mark derived URL and agent-hin
|
||||
metadata incomplete, while valid facts from other nodes and documents remain
|
||||
available in the capture.
|
||||
|
||||
Page network and console coverage starts independently at the first observed
|
||||
event from that domain or its successful enable acknowledgment. The capture's
|
||||
lifecycle start is earlier and does not establish domain readiness. Events
|
||||
received before the enable reply remain part of the observation.
|
||||
|
||||
Worker output is a final target-inventory snapshot. `attached` describes any
|
||||
debugging client, not an REA-owned observing session. The selected page-session
|
||||
collector does not collect worker-session requests, console events, or scripts,
|
||||
and workers that disappeared before the snapshot may be missing; worker
|
||||
coverage is therefore attach-limited. Missing ownership metadata is reported as
|
||||
`unattributed_target` with unavailable worker attribution, separately
|
||||
from a proven foreign owner excluded as `out_of_target_scope`. A shared origin
|
||||
alone establishes neither relationship.
|
||||
|
||||
Passive comparison keeps lookup identity, cross-capture correspondence, content
|
||||
evidence, and inventory coverage separate:
|
||||
|
||||
| Observation | Comparison outcome |
|
||||
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
|
||||
| Renamed WebMCP tool keys/frame IDs with one matching name, owner origin, and frame URL | Compare the registration content; local IDs do not prove change |
|
||||
| Duplicate WebMCP owners with the same name, origin, and frame URL | Unknown correspondence; complete group count differences still prove change |
|
||||
| Matching script inventory keys with retained source digests | Compare the retained SHA-256 values even without a producer hash |
|
||||
| One corresponding script source is unavailable, or neither source nor producer hash is available | Source equality remains unknown; inventory changes and other proven differences survive |
|
||||
| A partial HTTP response group is a subset of the complete peer | Unknown; omitted repeated responses can explain the difference |
|
||||
| An observed HTTP response fingerprint is absent from the complete peer group | Changed; omissions cannot explain that retained response |
|
||||
| A WebSocket connection or frame inventory is incomplete | Do not align retained payloads by position |
|
||||
|
||||
Storage fingerprints decode supported CDP primitive/JSON values before hashing.
|
||||
Debugger descriptions, previews, and remote references do not represent storage
|
||||
content. Actual JSON properties with those names remain content. Reference-only
|
||||
objects, unsupported values, and opaque cookie partition identities remain
|
||||
incomplete. Previously exported fingerprints are not recomputed; recapture both
|
||||
sides when comparing captures produced across this decoder change.
|
||||
|
||||
Script comparison groups observations by reported URL and compares unordered
|
||||
content samples. Producer inventory keys include optional metadata, so a changed
|
||||
key cannot by itself prove script removal or addition. Missing module, length,
|
||||
language or source-hash facts remain unknown; shared known values and complete
|
||||
retained source digests can still prove a group change. Partial inventories use
|
||||
the same omission rule as repeated HTTP samples. WebMCP dropped-tool counts also
|
||||
prevent equality, and two incomplete owner inventories cannot prove tool
|
||||
correspondence merely from their different retained registrations.
|
||||
|
||||
- `capture_web_screenshot` / `rea capture-web-screenshot` returns a content-addressed visible-viewport PNG.
|
||||
- `compare_web_screenshots` / `rea compare-web-screenshots` performs bounded local PNG pixel comparison without OCR or external services.
|
||||
- The CLI comparison commands accept each capture, screenshot artifact or normalization policy as inline JSON or a JSON file path. Use files for screenshot artifacts and scenario captures, whose embedded PNG bytes can exceed the host's command-line length limit.
|
||||
@@ -90,6 +147,11 @@ preserve these authority and lifecycle differences. They share one exclusion
|
||||
vocabulary (`BrowserExclusionReason`): every denial reason maps onto it, even
|
||||
where a wire schema keeps a narrower historical bucket.
|
||||
|
||||
Passive page and Inspector capture enable the `Debugger` domain. A target's
|
||||
`debugger` statement can then pause execution; these observation paths do not
|
||||
suppress pauses with `Debugger.setSkipAllPauses` or resume the target. Their
|
||||
lack of explicit pause commands does not guarantee uninterrupted execution.
|
||||
|
||||
CDP attachment is not a guarantee that the browser preserves all state.
|
||||
Chromium can clear another client's `navigator.platform` emulation when REA's
|
||||
page session disconnects, even if that session sent no emulation commands.
|
||||
@@ -260,7 +322,11 @@ read as registration state only after domain availability is confirmed.
|
||||
Discovery is attach-only: the command needs a reachable loopback CDP endpoint
|
||||
and a target ID from `list_browser_targets`, and it never navigates, reloads,
|
||||
or evaluates the page. Observe the page in the state you want to measure;
|
||||
registered tools reflect the document as loaded.
|
||||
registered tools reflect what the protocol delivered. If an in-scope subframe
|
||||
already exists when discovery attaches, `webmcp_tools` remains attach-limited:
|
||||
Chromium replay may omit its older registrations. Later live registrations
|
||||
retain their frame ownership but do not establish recovery of those missing
|
||||
descriptors. REA does not reload, evaluate, or invoke tools to fill that gap.
|
||||
|
||||
Accessibility names/descriptions, console primitive text, JSON body shapes, WebSocket shapes, script content, and storage key names are selected independently:
|
||||
|
||||
@@ -302,6 +368,20 @@ are reported in the result.
|
||||
Call `list_browser_targets` first because target IDs are browser-instance-specific. REA rechecks the selected target's current type and origin immediately before attaching. Without an origin filter, discovery lists all eligible pages; an individual capture defaults to the selected page's current origin.
|
||||
|
||||
REA establishes a main-frame origin boundary before enabling Runtime, Debugger, or Network observation, rechecks it before document capture, and validates it again afterward. If the main frame navigates during final capture, REA discards the mixed result and returns `target_changed`.
|
||||
An observed navigation also takes precedence over failures from stale
|
||||
document reads, such as a script ID invalidated by reload; cancellation remains
|
||||
cancellation. Retry after navigation settles, or refresh target discovery if
|
||||
the selected page was replaced. Origin-scope refusals from page inspection,
|
||||
runtime observation, screenshots, and WebMCP discovery explain the permitted
|
||||
HTTP(S) origin scope and direct the caller to select a permitted document or
|
||||
explicitly revise `allowed_origins`. Page capture also preserves an observed
|
||||
main-frame scope violation when a later document read fails. An unresolved
|
||||
initial main frame does not establish an origin violation; its bounded startup
|
||||
retries retain a distinct refusal.
|
||||
|
||||
Screenshot capture requires a valid main-frame identity. Malformed identity
|
||||
produces a protocol failure; a readable child frame cannot substitute for the
|
||||
main frame.
|
||||
|
||||
The web-session timeline uses CDP monotonic seconds where the event producer
|
||||
provides a comparable timestamp; absent or invalid timestamps are `null` rather
|
||||
@@ -315,13 +395,32 @@ events retain their paired prior-hop response context when the timestamped
|
||||
next-request event falls inside the window. The pre-arm timeline buffer is bounded
|
||||
by original CDP message bytes. If clock sampling completes after that budget is
|
||||
exhausted, the operation returns `payload_limit` instead of a partial window.
|
||||
Cancellation and clock-request failures retain their own outcomes.
|
||||
Cancellation and clock-request failures retain their own outcomes. Session
|
||||
`load_failed` rows require a previously observed main-frame `Document` request;
|
||||
subresources, child-frame requests and failures without admitted request ownership
|
||||
are excluded. Redirects reuse their request identity, and completed/failed requests
|
||||
release it. Active request identity metadata uses the existing 8 MiB retained-event
|
||||
allowance; exhaustion fails the observation explicitly instead of guessing scope.
|
||||
|
||||
## Data minimization
|
||||
|
||||
The Link projection parses repeated CDP header field lines independently and
|
||||
retains their declarations in order, including derived untrusted agent hints.
|
||||
Commas inside URI targets or quoted parameters remain part of those values.
|
||||
Response metadata parses repeated CDP header field lines independently. The
|
||||
Link projection retains their declarations in order. `csp.directives` lists the
|
||||
directives of every CSP policy in order, across field lines and comma-separated
|
||||
policies, without recording policy boundaries, so a directive name can repeat.
|
||||
Single-valued policy fields combine their lines as one comma-separated value,
|
||||
as RFC 9110 specifies. Each link keeps its target in `href` and
|
||||
its relationship context in `context_url` with a separate `context_scope`;
|
||||
absent `anchor` uses the response URL, while a relative `anchor` resolves
|
||||
against that URL. Anchored links remain in the projection, but a `link_rel`
|
||||
agent hint is emitted only when the resolved context matches the response URL
|
||||
before userinfo credentials are removed. Following [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288.html),
|
||||
the `rel`, `anchor`, and `type` parameters use their first occurrence, and
|
||||
quoted-pair escapes are decoded as specified by [RFC 9110 §5.6.4](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4).
|
||||
The context fields are optional when reading older saved captures. Commas
|
||||
inside URI targets or quoted parameters remain part of those values.
|
||||
Well-known-resource hints require an exact supported pathname; path case is
|
||||
preserved because a case variant may identify a different HTTP resource.
|
||||
COOP and COEP projections retain the exact declared token after validating the
|
||||
complete Structured Field Item, including parameters such as `report-to`.
|
||||
These are declarations, not an assessment of the document's effective policy;
|
||||
@@ -331,15 +430,16 @@ REA retains local observation data and removes only URL userinfo credentials, ex
|
||||
|
||||
- URL observations preserve the complete local URL, including query values, fragments, duplicate parameters, and their order. Only username/password userinfo is removed; malformed original URL strings remain available with `origin: null`.
|
||||
- DOM snapshots retain node types, node names, value lengths, and attribute names, but not text or attribute values.
|
||||
- Accessibility structure and roles are retained by default, while names and descriptions require `include_accessibility_text: true`.
|
||||
- Supported DOM URL attributes are projected separately as resolved destinations. Empty `action` on HTML or XHTML forms and empty `formaction` on HTML or XHTML submit controls resolve to the document URL; other relative URL attributes resolve against the captured document base.
|
||||
- Accessibility structure and reported roles are retained by default, while names and descriptions require `include_accessibility_text: true`. The browser protocol may omit an individual role; that role remains unknown for comparison. Other observed accessibility differences can still establish a change.
|
||||
- Network observations retain method, status, MIME type, size, type, initiator stack location, and complete URLs with userinfo credentials removed. Redirects reported by CDP are attached as ordered `redirects` on the one request ID, with the prior request URL, CDP response URL, method/type, response status/MIME/encoded length, request timestamp, and the next `requestWillBeSent` event timestamp. CDP delivers `redirectResponse` with that next request event, so the latter is an event boundary rather than an exact response timestamp. Redirect response headers and bodies are discarded; hops outside the selected origins are discarded with the request chain. Headers are discarded after an allowlisted projection of length/encoding, structured CSP/Link/policy fields, and untrusted agent hints. Cookies and authorization headers are never retained.
|
||||
- Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free typed paths, types and observation counts, and discarded. Each path segment is either `{ "kind": "property", "name": "raw property name" }` or `{ "kind": "array-element" }`; array-element types include primitives and nested arrays. Literal `*` properties remain distinct from array elements. `Network.getResponseBody` is never sent unless JSON body shape capture is selected. Comparison uses body shapes only when both captures selected them, and compares request/response shapes only where both observations provide those facts. Unavailable or truncated selected coverage makes equivalence unknown; observed differences still report changed.
|
||||
- Request/response bodies are not requested or parsed by default. When selected, only allowed-origin JSON media types are read, converted immediately to complete value-free typed paths, types and observation counts, and discarded. Each path segment is either `{ "kind": "property", "name": "raw property name" }` or `{ "kind": "array-element" }`; array-element types include primitives and nested arrays. Literal `*` properties remain distinct from array elements. `Network.getResponseBody` is never sent unless JSON body shape capture is selected. A JSON-typed request body that CDP reports through `hasPostData` but omits from `postData`, or that cannot be parsed as JSON, makes `json_body_shapes` unavailable. That request's `body_shapes.status` is `partial`, not `included`, when only its response shape was captured. Base64 response bytes must be valid UTF-8 before JSON interpretation; invalid bytes leave the response shape unavailable and preserve any observed request shape and network metadata. Producer-decoded strings and genuine U+FFFD characters retain their reported meaning. Comparison uses body shapes only when both captures selected them, and compares request/response shapes only where both observations provide those facts. Unavailable or truncated selected coverage makes equivalence unknown; observed differences still report changed.
|
||||
For normalized passive inputs with complete request inventories on both sides,
|
||||
request additions and removals remain observable even when selected body shapes,
|
||||
response metadata, or WebSocket coverage are unavailable. HTTP request and
|
||||
WebSocket inventory coverage are evaluated separately; complete HTTP coverage
|
||||
cannot prove a missing WebSocket connection. Limited request inventory still
|
||||
prevents request-absence claims. This comparison rule does not make live CDP's
|
||||
cannot prove a missing WebSocket connection. A missing request in a limited inventory cannot prove absence; a retained
|
||||
request absent from a complete peer can prove a change. This comparison rule does not make live CDP's
|
||||
attach-limited inventory complete or establish body-shape equality.
|
||||
An unavailable HTTP resource type also leaves request identity incomplete:
|
||||
observations with the same method and URL remain possible matches across types,
|
||||
@@ -354,11 +454,15 @@ REA retains local observation data and removes only URL userinfo credentials, ex
|
||||
uses `websocket_connections` and `websocket_frames`. Drops in other event kinds
|
||||
and the aggregate total do not by themselves limit HTTP inventory. Proven
|
||||
modifications remain reportable despite incomplete inventory.
|
||||
Repeated identical HTTP content samples establish multiplicity changes only
|
||||
with complete request inventories; limited inventories retain known content
|
||||
comparisons without interpreting duplicate sample counts as changes.
|
||||
Repeated HTTP samples retain multiplicity. A partial group that is contained
|
||||
in a complete peer cannot prove modification, even when the omitted response
|
||||
had different content. A retained sample absent from the complete peer can
|
||||
prove modification. Two partial groups cannot establish response
|
||||
correspondence from their different retained samples alone.
|
||||
WebSocket stream inventories and connection counts likewise require complete
|
||||
WebSocket coverage. Redirect resource types use only shared known metadata:
|
||||
WebSocket coverage. Retained frame positions are compared only with complete
|
||||
connection/frame inventories; matching direction/opcode sequences alone do
|
||||
not prove alignment after drops. Redirect resource types use only shared known metadata:
|
||||
an omitted type leaves equality unknown rather than proving a modification.
|
||||
- Console observations with a stack source retain call type, argument types, the CDP `Runtime.Timestamp` in milliseconds since Unix epoch, and source location. An absent or invalid timestamp is `null` and makes `console_events` incomplete; an observed zero remains zero. Argument types are listed by CDP argument position. An absent or non-array argument inventory is `null`, and a malformed argument keeps its position as `unknown`; either makes `console_events` incomplete, while an observed empty inventory remains `[]`. A malformed stack or top stack frame is not replaced by its caller: the console event is excluded, and a request initiator location or WebMCP `registration_source` is `null`; each is reported as `invalid_protocol_value`. This epoch-millisecond clock is separate from the monotonic-second timestamps in `observe-web-session`. When console text capture is selected, already-delivered primitive values are retained verbatim with their original `argument_index`; objects, getters, and remote properties are never expanded. Text capture is `unavailable` when the argument inventory is unknown.
|
||||
- WebSocket observations retain direction, opcode, and payload byte length. A missing or malformed CDP opcode is returned as `null`, with opcode-dependent payload byte length and shape also `null`, and marks `websocket_frames` incomplete. Missing or wrong-typed payload data is not treated as empty; its byte length is `null`, and a text payload shape is unknown. Numeric opcode `0` and an observed empty payload remain distinct from missing data. Opcode is nullable in the public inspection schema, so clients must handle unknown protocol values. When shape capture is selected, text messages are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw payloads are never retained.
|
||||
@@ -370,6 +474,11 @@ Frames, resources, scripts, events, and workers outside the selected origin scop
|
||||
Retained workers include validated opener-target and parent-frame IDs when CDP
|
||||
provides them; these relationships do not expand the exact-origin boundary or
|
||||
prove static module ownership.
|
||||
Workers without either relationship are withheld with an `unattributed_target`
|
||||
exclusion and `workers` in `unavailable_sections`: a same-origin URL does not
|
||||
establish which page owns a worker. A supplied relationship to another target or
|
||||
frame remains `out_of_target_scope`. Missing ownership also remains unknown when
|
||||
the worker URL is empty; withheld target details are not returned.
|
||||
|
||||
## Completeness and limits
|
||||
|
||||
@@ -384,6 +493,14 @@ validation and resource bounds. Omitted script metadata makes its affected
|
||||
section explicitly truncated.
|
||||
Disallowed-origin entries are filtered before retention.
|
||||
|
||||
Script metadata and selected source reads use one fixed admitted-script snapshot.
|
||||
New in-scope script IDs still retained when the final CDP read completes are
|
||||
counted in `scripts.total` and `completeness.dropped_events.scripts`, with
|
||||
`scripts` marked truncated; they do not extend the source-read pass. Repeated
|
||||
late events for the same ID count once. The original snapshot is not replaced
|
||||
or reduced by later updates to an existing ID. These omissions do not imply
|
||||
that a source byte or time budget was exhausted.
|
||||
|
||||
Selected script-source collection shares a 32 MiB aggregate UTF-8 retention
|
||||
budget and the `command_timeout_ms` deadline (default 20000 ms) across the
|
||||
capture. The byte budget has no runtime override. An oversized source is
|
||||
@@ -438,6 +555,12 @@ An explicitly empty MIME type is valid. The optional content size may be absent;
|
||||
an invalid supplied size remains unknown with incomplete resource coverage while
|
||||
the resource's other facts are retained. Zero and nonnegative fractional sizes
|
||||
are preserved.
|
||||
`resource_key` remains the exact observation identity used by script
|
||||
reconciliation. Capture comparison groups resources by URL, origin, type and
|
||||
MIME type, then compares known sizes within a group. If a matched group contains
|
||||
an unknown size, comparison reports unknown rather than treating the observation
|
||||
as a resource addition or removal; complete inventory coverage still establishes
|
||||
when an entire group is absent.
|
||||
|
||||
CDP discovery has a 10-second response deadline. HTTP discovery documents
|
||||
use an 8 MiB budget. Browser-socket discovery (`Browser.getVersion`,
|
||||
@@ -467,20 +590,34 @@ Network and console coverage starts only after REA attaches and enables the rele
|
||||
|
||||
Source maps are not fetched by inspection. Bundle analysis fetches them only when `fetch_source_maps` is selected, with exact-origin redirect checks and no credentials, cookies, or referrer. Each fetch operation uses `source_map_fetch_timeout_ms` (default 30000) and a 64 MiB aggregate streamed-response budget across redirects. The byte budget has no runtime override. A fetch overrun is an item limitation, not a thrown analysis timeout. A deadline or byte-limit failure remains explicitly unavailable and makes bundle completeness partial. Source-map decoding also bounds raw resolved identities to 64 MiB and aggregate inline evidence to 32 MiB. Failed maps preserve their failure context when it fits; if another context cannot fit, collection stops with the full requested count, the returned processed count, and an explicit unknown-results limitation. Retained mappings and original sources are returned inline. See [source-location limits](web-source-location.md) for the structural decoder bounds. Original source artifacts with `.ts`, `.tsx`, `.mts`, or `.cts` suffixes carry `text/typescript`; other source names retain `text/javascript`.
|
||||
|
||||
source-map items retain the script's `declared_url` separately from the observed
|
||||
`effective_url`. successful maps use the latter as their source-resolution base.
|
||||
failed fetches retain the last response URL observed within the approved origins;
|
||||
`effective_url` is `null` when no response was observed. query parameters are
|
||||
When fetching is selected, source-map `excluded` facts preserve the capture
|
||||
ledger's reasons and counts for declarations rejected before download. No
|
||||
disallowed URL details are retained or fetched. `requested` and `processed`
|
||||
count the admitted fetch requests; exclusions are counted separately. Excluded
|
||||
declarations make the collection unavailable, or partial when maps were retained.
|
||||
An empty collection with no declarations remains `not_requested`.
|
||||
|
||||
Source-map items retain the script's `declared_url` separately from the observed
|
||||
`effective_url`. Successful maps use the latter as their source-resolution base.
|
||||
Failed fetches retain the last response URL observed within the approved origins;
|
||||
`effective_url` is `null` when no response was observed. Query parameters are
|
||||
preserved, and both URLs count toward the inline evidence budget.
|
||||
Current fetches emit a string or null; historical observations may omit
|
||||
`effective_url` without changing their stored content or Evidence identity.
|
||||
An observed URL must fit before its response body is read. If it cannot fit,
|
||||
remaining map results are explicitly unknown; failed cancellation retains its
|
||||
cleanup context and resource constraint.
|
||||
Indexed maps whose decoded mappings cross the next section's starting position
|
||||
are invalid under [ECMA-426](https://tc39.es/ecma426/2024/), rather than complete
|
||||
maps with clipped mapping rows. Fetch collection and source tracing share this
|
||||
validation.
|
||||
|
||||
## Non-goals and threat model
|
||||
|
||||
The browser provider deliberately does not expose generic CDP commands, `Runtime.evaluate`, WebMCP invocation, navigation, input, downloads, page closure, or browser closure. Screenshot capture is the sole pixel surface and returns an image only when requested. REA disables the domains it enabled, detaches the target session, and closes only its own WebSocket.
|
||||
Discovery and attachment errors remain primary if closing REA's socket also
|
||||
fails. The cleanup failure is reported alongside the original command failure;
|
||||
it cannot replace the operation diagnostic.
|
||||
|
||||
This feature is not a browser sandbox or network containment mechanism. The attached page and browser continue running with their existing privileges and may make external requests independently of REA. CDP gives deep access to the selected browser profile, so use a dedicated profile, include only origins you intend to inspect, and treat other same-user processes as outside this boundary.
|
||||
|
||||
|
||||
@@ -15,22 +15,32 @@ there is no separate origin allowlist or request-interception layer. Environment
|
||||
settings (including service workers blocked by default), empty storage, and a
|
||||
final sanitized URL capture are supplied by default. Set
|
||||
`environment.service_workers` to `allow` when the application requires them.
|
||||
The connected service-worker policy owner retains its exact CDP transport
|
||||
through discovery, attachment and restoration. Failed admission preserves its
|
||||
primary diagnostic when socket cleanup also fails; later policy cleanup retries
|
||||
that retained transport and clears cleanup uncertainty only after close succeeds.
|
||||
For attached browsers, `block` rejects captures when the selected page or its
|
||||
frames are controlled by a service worker or already have a registration,
|
||||
including after navigation. Existing caller registrations are never removed.
|
||||
including after navigation. Policy checks bracket each artifact capture, including
|
||||
the final step. If an external worker takes control while artifacts are being
|
||||
captured, REA rejects the capture and retains the completed artifacts in
|
||||
`details.diagnostics.collected_artifacts`; they do not establish policy-compliant
|
||||
completion. Existing caller registrations are never removed.
|
||||
Navigation can run destination code before a newly discovered worker is rejected;
|
||||
that rejection does not roll back browser effects. REA removes its owned
|
||||
future-document scripts, restores live frame methods, then disconnects.
|
||||
Add secret declarations, storage seeds, and additional artifact or event
|
||||
capture only when needed. Action, secret, and storage counts are not capped.
|
||||
capture only when needed. Missing environment-backed secrets fail as input errors before browser acquisition, naming the declaration and required variable. An explicitly set empty string is a resolved value. Action, secret, and storage counts are not capped.
|
||||
Duration, action, and navigation timeouts remain separate from CDP command
|
||||
deadlines. `command_timeout_ms` (default 20000; omit it to keep that default
|
||||
and leave it out of Evidence) bounds launch startup, the connect-over-CDP
|
||||
handshake, target lookup, environment commands, and storage CDP commands,
|
||||
including replies from attached child targets. Set it in the scenario JSON.
|
||||
including replies from attached child targets. It also bounds the initial
|
||||
`start_url` navigation through the load event; a redirect or replacement
|
||||
navigation cannot leave startup waiting indefinitely. Set it in the scenario JSON.
|
||||
`capture-browser-scenario` has no `--command-timeout-ms` option.
|
||||
It does not change action `timeout_ms`, `wait_for_timeout.duration_ms`, or
|
||||
navigation. Service-worker restoration keeps a fixed 5000 ms grace and does
|
||||
action navigation. Service-worker restoration keeps a fixed 5000 ms grace and does
|
||||
not inherit a raised `command_timeout_ms`. CDP discovery stays on its own
|
||||
fixed deadline. When service workers are blocked, the registration check
|
||||
before discovery has its own fixed 10000 ms deadline; `command_timeout_ms`
|
||||
@@ -211,8 +221,9 @@ validation.
|
||||
|
||||
Cookies are installed before the initial navigation. Local storage entries are
|
||||
seeded once per browser storage partition; session storage entries are seeded
|
||||
once per origin in each top-level browsing context. Seeding occurs before the
|
||||
first document scripts for that area. An origin first reached by a later action
|
||||
once per origin in each instrumented top-level browsing context. Seeding occurs
|
||||
before the first document scripts for the selected page and its instrumented
|
||||
frames. An origin first reached by a later action
|
||||
receives its seed on that first visit. Application updates, deletions, and
|
||||
empty values then persist across later navigations in the same area; a new
|
||||
scenario starts with its own seeds. Partitioned frames can receive separate
|
||||
@@ -220,6 +231,17 @@ local storage seeds even when they share an origin. The pre-script mechanism
|
||||
uses CDP debugger instrumentation. The observer resumes only pauses whose top
|
||||
frame belongs to its private seed script; page breakpoints and `debugger`
|
||||
statements remain under the attached debugger client's control.
|
||||
If Chromium leaves the private isolated world's origin empty, REA looks up
|
||||
that exact frame's reported security origin before selecting a seed. It does
|
||||
not infer the origin from a frame URL or serialized storage key. Unresolved
|
||||
frame identity or security origin fails initialization rather than silently
|
||||
running with an unknown initial state.
|
||||
Popup discovery does not establish that its first script was paused by the
|
||||
selected page's storage instrumentation. When non-cookie seeds are declared
|
||||
and a popup is discovered, the capture retains completed observations and
|
||||
marks `storage` coverage missing, with equality ineligible. This applies even
|
||||
if popup lifecycle events were not selected. Popup first-script storage
|
||||
initialization is not currently guaranteed.
|
||||
|
||||
The result is Evidence with an initial state followed by one record per
|
||||
declared action. Each step reports action status, elapsed time, sanitized URLs,
|
||||
@@ -366,8 +388,16 @@ Proxy-Authorization, Cookie, and Set-Cookie values are null with
|
||||
and their standard URI/form or JSON-escaped representations are replaced without
|
||||
decoding unrelated binary bytes. Other encodings are not interpreted. Digests describe the
|
||||
retained representation after redaction.
|
||||
The selected body-byte limit also bounds that retained representation after
|
||||
secret expansion, before base64 serialization. Credential header values are
|
||||
discarded before retained-header allocation accounting; their names and null
|
||||
projections still count. The transport/API has already materialized incoming
|
||||
bodies and headers, so these retained limits do not bound producer-side input
|
||||
allocation.
|
||||
|
||||
Only completed responses are read. At the end of the final step, observation
|
||||
Only completed responses are read. The producer-observed terminal state is
|
||||
recorded independently of whether its event metadata fit the retention budget. An omitted completion event cannot turn a
|
||||
finished transaction into an unfinished one. When the final step ends, observation
|
||||
stops; requests still in flight emit `request-unfinished`, and their observed
|
||||
responses have explicitly unavailable bodies. Already-admitted content reads
|
||||
drain before browser cleanup. At most 16 reads run at once; queued work is
|
||||
@@ -387,6 +417,24 @@ observed) and excludes the sequence reference from normalization.
|
||||
Playwright scenario capture does not supply initiator stacks; passive CDP
|
||||
observation retains its own initiator evidence.
|
||||
|
||||
## Storage value fingerprints
|
||||
|
||||
New storage snapshots include `fingerprint_version: 2`. Non-secret, well-formed
|
||||
JavaScript strings retain their SHA-256 over UTF-8. Strings with unpaired UTF-16
|
||||
surrogates use SHA-256 over a raw `0xff` byte followed by their exact UTF-16LE
|
||||
code units; this prefix cannot occur in UTF-8. Declared secrets remain
|
||||
`redacted-secret` with a null digest, without a second or legacy digest.
|
||||
|
||||
A missing version identifies legacy UTF-8 fingerprints, which replaced unpaired
|
||||
surrogates and cannot establish value identity. Storage containing any legacy
|
||||
hashed value is conservatively compared as `unknown`, including old-to-old
|
||||
comparisons; recapture both sides to compare reliably. This also conservatively
|
||||
withholds some otherwise provable legacy differences. Empty or entirely
|
||||
secret-redacted storage keeps its existing comparison semantics. The version is
|
||||
checked before normalization and excluded from content comparison: changing
|
||||
encoding metadata alone does not establish a website change. Existing captures
|
||||
and their completeness records are not rewritten.
|
||||
|
||||
## Scenario comparison
|
||||
|
||||
`compare_web_captures` and `compare-web-captures` also accept two complete
|
||||
@@ -419,6 +467,15 @@ sorted canonically before application, making the same policy reproducible
|
||||
regardless of input order. Rules operate only on already-redacted durable
|
||||
capture fields and are preserved in the result.
|
||||
|
||||
Cookies, local storage, and session storage are compared as unordered inventories
|
||||
after caller literal rules apply. Enumeration order alone does not establish a
|
||||
storage change. Canonical ordering uses the complete entry, preserving cookie
|
||||
scope/flags, value fingerprints, and duplicate entries even when redaction or
|
||||
literal rules make names collide. Captured storage arrays retain their original
|
||||
order. The `compare_storage_entries_as_unordered_collection` built-in rule
|
||||
participates in the normalization commitment; new storage comparison digests
|
||||
therefore differ from earlier order-sensitive comparisons.
|
||||
|
||||
Each aligned step reports changed, unchanged, or unknown. Changed and unknown
|
||||
action, screenshot, DOM, accessibility, URL, history, storage, and event
|
||||
artifacts carry their normalized before/after digests and capture states.
|
||||
@@ -431,6 +488,11 @@ selection. Captures with different selections report event evidence as unknown
|
||||
with a recovery instruction, even when no selected family emitted an event.
|
||||
Captures written before this field existed omit it, so their event evidence is
|
||||
also unknown.
|
||||
Retained-capture readers admit that omission without deriving a selection from
|
||||
observed events; other artifact families remain comparable. Current capture
|
||||
producers still require and publish the selected event families. Manifest,
|
||||
environment and script-export readers share the retained-capture admission
|
||||
rules and preserve the same missing fact.
|
||||
|
||||
Network normalization also commits the removal of capture-local transaction IDs,
|
||||
attribution to the requesting step, and unordered transaction comparison. These
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# Static analysis in GitHub Actions
|
||||
|
||||
The [minimal workflow](https://github.com/morluto/rea/blob/main/examples/github-actions/rea-analysis.yml) builds a small
|
||||
JavaScript application, analyzes its files without executing them, and uploads
|
||||
machine-readable results. It uses the existing CLI, not a separate GitHub Action
|
||||
or an MCP client. It does not promise support for every REA provider.
|
||||
|
||||
## Run the example
|
||||
|
||||
1. Copy the example to `.github/workflows/rea-analysis.yml` in your repository and
|
||||
commit it to the default branch. No existing application or package manifest is
|
||||
needed for the self-contained demonstration.
|
||||
2. Enable GitHub Actions if necessary. Select **REA static analysis example** in
|
||||
the Actions tab and choose **Run workflow**.
|
||||
3. Download the `rea-analysis` artifact after the run. Its files are:
|
||||
|
||||
| File | Meaning |
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| `analysis.json` | Original CLI JSON, including complete Evidence on success or the error report when one is emitted. |
|
||||
| `analysis.stderr` | Original diagnostics/progress; an empty file is normal when there are none. |
|
||||
| `exit-code.txt` | Original analysis process exit code. |
|
||||
|
||||
The example pins Node.js 24.22.1, REA 6.7.0, and action commit revisions. It installs
|
||||
REA into the runner's temporary directory rather than changing the application's
|
||||
dependencies. This pins the tool version, not every transitive npm dependency;
|
||||
use a reviewed, committed package lock and `npm ci` if your integration requires
|
||||
that stronger dependency-reproducibility guarantee.
|
||||
|
||||
## Prerequisites and trust
|
||||
|
||||
The runner is GitHub-hosted Ubuntu 24.04 with Bash, npm, and `ps` from `procps`.
|
||||
Other Linux images must supply `procps`; REA's supervised process controls use it.
|
||||
This static JavaScript operation needs no Hopper, Ghidra, IDA, Java, browser,
|
||||
Docker, GPU, external API key, or user-supplied GitHub secret. The workflow only
|
||||
requests `contents: read`; artifact upload uses the runner's normal Actions
|
||||
credentials.
|
||||
|
||||
Replace **Build a known artifact** with your reviewed build, and set
|
||||
`ARTIFACT_PATH` to its application directory (relative to the checkout) or an ASAR.
|
||||
The demonstration creates its own tiny application; it deliberately throws if
|
||||
executed. REA reads it statically. Your replacement build itself can execute code:
|
||||
review it separately and do not add untrusted pull-request builds to a privileged
|
||||
`pull_request_target` workflow. The example is manually triggered, not an
|
||||
untrusted-PR execution policy.
|
||||
|
||||
Reports can contain paths, URLs, source-derived facts, and caller-selected data.
|
||||
Only analyze artifacts and publish reports you are authorized to share. Do not
|
||||
rely on REA inferring secrets from their names. Review artifact access and
|
||||
retention for your repository; the example retains reports for seven days.
|
||||
|
||||
## Failure, partial results, and unsupported capabilities
|
||||
|
||||
The analysis step captures stdout and stderr separately, records `$?`, and exits
|
||||
with that same status. There is no `continue-on-error`, `|| true`, output filter,
|
||||
or pipeline that could turn a failed analysis green. Before checkout or other
|
||||
fallible steps, the workflow creates a fresh report directory under `RUNNER_TEMP`.
|
||||
Analysis and upload both use that directory, never a checked-in `reports/rea` or
|
||||
other workspace files. The upload step uses `always()` when that directory was
|
||||
prepared, so available diagnostics survive an analysis failure. If installation
|
||||
or the build failed before analysis began, it is empty and upload warns rather
|
||||
than publishing stale results or inventing a successful result. Runner loss or
|
||||
cancellation can still prevent upload.
|
||||
|
||||
`--json --full-output` selects a machine-readable envelope; it does **not** mean
|
||||
that analysis was complete. A successful operation can retain partial analysis,
|
||||
unknowns, and limitations. Exit zero is not a security verdict, proof of complete
|
||||
coverage, or a policy gate for those findings. Inspect the saved Evidence and
|
||||
coverage before making such a decision. See [output and exit status](cli.md#output-and-exit-status).
|
||||
|
||||
A nonzero exit can represent invalid input, unavailable tooling, an unsupported
|
||||
host/capability, or another operation failure. Keep the original JSON error (when
|
||||
emitted), stderr, and exit code, and use their category/message to diagnose it.
|
||||
An early startup failure may leave `analysis.json` empty; do not parse that as
|
||||
successful analysis. Select a supported operation or explicitly provision its
|
||||
[documented prerequisites](installation.md); do not silently switch providers,
|
||||
execute the artifact, or ignore the failure to obtain a green job.
|
||||
|
||||
To exercise the failure path, temporarily set `ARTIFACT_PATH` to
|
||||
`build/missing-artifact` without changing the demo build. Analysis must fail,
|
||||
`exit-code.txt` must be nonzero, and the available reports must still upload.
|
||||
To exercise unsupported input, set `ARTIFACT_PATH` to `build/rea-ci-demo/index.js`:
|
||||
this operation accepts an application directory or ASAR, not a single source file.
|
||||
The retained JSON error has category `unsupported_target`; the step exits nonzero.
|
||||
Restore the correct path before using the example normally.
|
||||
|
||||
To exercise partial analysis, append `export const broken = ;` to the demo's
|
||||
JavaScript. REA 6.7.0 retains `data.normalized_result.statistics.parse_failures`
|
||||
and an incomplete-coverage limitation while the operation can still exit zero.
|
||||
Inspect those facts instead of treating process success as full coverage. The
|
||||
report JSON and diagnostics remain unfiltered in all three cases.
|
||||
+418
-14
@@ -3,6 +3,8 @@
|
||||
Use the CLI for a direct inspection or a script. It uses the same application
|
||||
workflows and evidence contracts as REA's MCP server. Each CLI invocation is a
|
||||
separate process; save results when you want to use them in a later command.
|
||||
For a pinned, noninteractive workflow with retained reports, see
|
||||
[static analysis in GitHub Actions](ci-analysis.md).
|
||||
|
||||
## Run a command
|
||||
|
||||
@@ -37,6 +39,11 @@ pass an absolute path. REA reads and authenticates the file, then projects it
|
||||
like retained Evidence, so a large file is bounded by the file reader rather
|
||||
than charged as inline input.
|
||||
|
||||
Local file inspection commands reject empty paths and NUL bytes before resolving
|
||||
relative inputs, auxiliary files or output destinations. An empty string never
|
||||
selects the working directory; use an explicit `.` when selecting that directory
|
||||
is supported by the command.
|
||||
|
||||
For example, a 334 MB Obsidian application Evidence file yields a summary view
|
||||
of about 10 KB. Replace the view with
|
||||
`{"kind": "page", "collection": "modules", "offset": 0, "limit": 32}` for a
|
||||
@@ -44,6 +51,122 @@ module page. See
|
||||
[JavaScript application workflows](javascript-application-workflows.md) for
|
||||
views, feature traces and comparisons that take the same saved Evidence.
|
||||
|
||||
## JSON Lines record output
|
||||
|
||||
Use `--format jsonl` to consume row-oriented results one record per line:
|
||||
|
||||
```bash
|
||||
cat > observations-view.json <<'JSON'
|
||||
{"source":{"kind":"evidence-file","path":"app-evidence.json"},"view":{"kind":"javascript-facts","property_keys":[],"offset":0,"limit":32}}
|
||||
JSON
|
||||
rea inspect-analysis-view observations-view.json --format jsonl > observations.jsonl
|
||||
```
|
||||
|
||||
This request selects up to 32 observation identities and their provenance,
|
||||
without observation property values.
|
||||
|
||||
This changes JSONL object-collection framing. Scripts that need one complete
|
||||
report or a scalar field should use `--format json`. For a full application
|
||||
graph, first project a supported analysis view rather than selecting an
|
||||
arbitrary nested graph array.
|
||||
|
||||
A top-level array emits its elements in source order. An object must have one
|
||||
clear `rows`, `items`, `records`, or `occurrences` array, directly or under
|
||||
`page`. Evidence uses the same boundaries inside `normalized_result`.
|
||||
An arbitrary report or a scalar selection is refused with an actionable error;
|
||||
use `--format json` for a complete document. If several collections exist,
|
||||
select one with `--filter-output`, for example `normalized_result.rows`.
|
||||
|
||||
Object collections finish with a control record:
|
||||
|
||||
```json
|
||||
{
|
||||
"$rea": "jsonl/v1",
|
||||
"type": "metadata",
|
||||
"records": {
|
||||
"path": ["normalized_result", "rows"],
|
||||
"emitted": 2,
|
||||
"source": 10,
|
||||
"filter": "normalized_result.rows[0,2]"
|
||||
},
|
||||
"context": {
|
||||
"evidence_id": "ev_…",
|
||||
"normalized_result": {
|
||||
"coverage": {
|
||||
"status": "page",
|
||||
"examined": 10,
|
||||
"total": 100,
|
||||
"next_offset": 10,
|
||||
"exhausted": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This abbreviated example illustrates framing. The actual `context` preserves
|
||||
all non-row fields, including Evidence subject, provider, authority, confidence,
|
||||
locations, links, limitations, parent/view digests, pagination and coverage.
|
||||
With `--full-output`, it also retains the command envelope and its metadata.
|
||||
A data filter selects row fields/slices while this context remains available;
|
||||
excluded peer record collections and unselected Evidence `raw_result` are not
|
||||
restored. An explicitly selected `raw_result` remains selected. Retain the
|
||||
original Evidence for complete observations.
|
||||
|
||||
`records.emitted` and `records.source` count the selected output and its source
|
||||
array; they do not assert analysis completeness. Source coverage and unknowns
|
||||
retain their original meaning. Existing row identities and indices remain
|
||||
unchanged unless your field selection excludes them. Empty object collections
|
||||
still emit metadata; an empty top-level array emits no lines. Bare top-level
|
||||
arrays have no synthetic coverage record.
|
||||
|
||||
Command and output-validation failures emit `{"$rea":"jsonl/v1","type":"error","ok":false,"result":…}`
|
||||
and retain the original diagnostic payload. SDK validation/sentinel errors use
|
||||
the SDK's exit status rather than guessing from data fields. `$rea` is reserved
|
||||
at the top level of a data record, so control records are unambiguous; collisions
|
||||
are refused before rows are written. Nested `$rea` values are ordinary data.
|
||||
A missing final metadata record or nonzero exit can indicate an interrupted
|
||||
export; do not treat already emitted rows as a complete result.
|
||||
|
||||
`--token-count` estimates this exact representation, including metadata, one
|
||||
line at a time. Errors remain error records even with token counting enabled.
|
||||
Token windows cannot preserve JSON Lines and are rejected. JSON encoding and
|
||||
stdout writes are incremental, including large escaped strings; this is output
|
||||
streaming over a completed result, not incremental analysis. The analysis still
|
||||
needs its normal resources. A single record must fit the runtime string limit
|
||||
for token estimation; remove `--token-count` to stream a larger record.
|
||||
|
||||
Standalone version and MCP doctor diagnostics remain single JSON documents
|
||||
in JSON Lines mode. Incur's streaming generator events and successful sentinel
|
||||
responses retain their SDK protocol. MCP application results and Evidence
|
||||
contracts are unchanged.
|
||||
|
||||
## Shell completion
|
||||
|
||||
The CLI includes optional shell completion. Run `rea completions --help` to
|
||||
see the available shells and their setup commands. For Bash, load the hook
|
||||
into the current shell:
|
||||
|
||||
```bash
|
||||
eval "$(rea completions bash)"
|
||||
```
|
||||
|
||||
The Bash hook supports macOS system Bash 3.2. It uses optional completion
|
||||
features when the shell provides them and falls back to portable registration.
|
||||
Completion includes standard global options and the active command's options;
|
||||
`rea mcp ` offers only `add` and `doctor`. Output-option values remain data when
|
||||
resolving the command scope.
|
||||
|
||||
For future interactive non-login Bash sessions, add the command to
|
||||
`~/.bashrc`. Interactive login Bash sessions read the first available file in
|
||||
`~/.bash_profile`, `~/.bash_login`, and `~/.profile`; add the command to the
|
||||
file your shell reads, or have that file source `~/.bashrc`. Remove the command
|
||||
from the startup file to stop loading completion, and run `complete -r rea` to
|
||||
remove the registration from the current shell. These are manual, opt-in
|
||||
steps.
|
||||
Completion is a discovery aid; use `rea COMMAND --help` to confirm the
|
||||
available options and subcommands.
|
||||
|
||||
## Managed assemblies (.NET)
|
||||
|
||||
For a .NET PE/CLI assembly, inspect its identity and deployment form before
|
||||
@@ -136,8 +259,8 @@ The command decodes a version 24, 27, 29, 31 or 39 `global-metadata.dat` and rep
|
||||
images, type, method and member names, string literals, index ranges, header
|
||||
sections and SHA-256. With `--binary PATH` it also reads the matching
|
||||
`GameAssembly.dylib`, `GameAssembly.dll` or `libil2cpp.so` (never runs it) and
|
||||
links each method to its code address; `--ghidra-seeds PATH` then writes Ghidra
|
||||
function seeds. It does not decrypt metadata. MCP defaults to compact counts
|
||||
links each method to its code address; `--generic-instances` adds generic
|
||||
method instances and `--ghidra-seeds PATH` then writes Ghidra function seeds. It does not decrypt metadata. MCP defaults to compact counts
|
||||
with retained Evidence; query indexed rows with filters, batch indices and
|
||||
selected fields. The CLI defaults to complete Evidence;
|
||||
`--detail summary --evidence_output FILE` exports the full result to a new file
|
||||
@@ -155,6 +278,29 @@ dependency edges, and the section's file range. A missing `.dep-v0` section is
|
||||
null metadata, not proof that the binary has no dependencies. See
|
||||
[Rust binaries](rust-binaries.md).
|
||||
|
||||
Inspect a PE Authenticode certificate table without executing the image or
|
||||
verifying signatures:
|
||||
|
||||
```bash
|
||||
rea inspect-pe-certificates /absolute/path/to/application.exe --json
|
||||
```
|
||||
|
||||
The workflow reports `WIN_CERTIFICATE` entries and, for PKCS#7 signedData,
|
||||
embedded X.509 serial, issuer, subject, and validity fields. A missing security
|
||||
directory is observed absence of that table. See
|
||||
[PE Authenticode certificate tables](pe-certificates.md).
|
||||
|
||||
Inspect PE import and delay-load tables without executing the image:
|
||||
|
||||
```bash
|
||||
rea inspect-pe-imports /absolute/path/to/application.exe --json
|
||||
```
|
||||
|
||||
The workflow reports DLL names, named and ordinal functions, delay-load
|
||||
descriptors, and a pefile-compatible import hash of the standard import table.
|
||||
A missing import directory is observed absence of that table. See
|
||||
[PE import and delay-load tables](pe-imports.md).
|
||||
|
||||
Inspect ELF PT_NOTE and SHT_NOTE tables without executing the image:
|
||||
|
||||
```bash
|
||||
@@ -165,6 +311,38 @@ The workflow reports note owners, types, descriptor hashes, and GNU build-id
|
||||
or ABI-tag fields when those descriptors match. A missing note table is
|
||||
observed absence of those declarations. See [ELF note tables](elf-notes.md).
|
||||
|
||||
Inspect an Apple `.ips` crash report without executing a target:
|
||||
|
||||
```bash
|
||||
rea inspect-apple-ips /absolute/path/to/report.ips --json
|
||||
```
|
||||
|
||||
The workflow reports first-line metadata, process/exception/termination facts,
|
||||
threads, usedImages, lossless integer registers, and documented crash classes
|
||||
when `bug_type` is `"309"`. Other DiagnosticReports types return no crash
|
||||
result. See [Apple IPS crash reports](apple-ips.md).
|
||||
|
||||
Inspect a CPython 3.7–3.13 `.pyc` header and nested marshal code objects
|
||||
without executing bytecode or installing Python:
|
||||
|
||||
```bash
|
||||
rea inspect-python-pyc /absolute/path/to/module.pyc --json
|
||||
```
|
||||
|
||||
The workflow reports the pinned CPython version for the magic, PEP 552 hash or
|
||||
mtime/size fields, nested function names, and bytecode SHA-256. Bytecode is
|
||||
not decompiled. See [CPython pyc inspection](python-pyc.md).
|
||||
|
||||
Inspect an Erlang BEAM file without executing bytecode:
|
||||
|
||||
```bash
|
||||
rea inspect-erlang-beam /absolute/path/to/app.beam --json
|
||||
```
|
||||
|
||||
The workflow reports IFF chunk names, atom tables, exports, imports, lambdas,
|
||||
and the Code chunk header. Missing optional tables stay null. See
|
||||
[Erlang BEAM files](erlang-beam.md).
|
||||
|
||||
Configure [Hopper or Ghidra](installation.md#hopper), the
|
||||
[IDA adapter](ida-provider.md), or [Kuna](kuna-provider.md) before analyzing a
|
||||
native target. Substitute
|
||||
@@ -183,6 +361,16 @@ rea trace /absolute/path/to/program "search" --provider ghidra --json
|
||||
a function dossier; `decompile` returns pseudocode. For assembly instructions
|
||||
alone, use `rea instructions`. A macOS `.app` bundle can be supplied directly.
|
||||
|
||||
A complete dossier can reach several megabytes of value-flow and references.
|
||||
`rea function ... --detail summary --evidence_output /absolute/path/dossier.json` returns the
|
||||
compact summary and saves the complete dossier as an Evidence bundle, never
|
||||
overwriting an existing file. Page that bundle without reanalysis by naming it
|
||||
as an `inspect-analysis-view` source, selecting the summary's
|
||||
`normalized_result.parent_evidence_id` when the bundle holds several records:
|
||||
`{"source": {"kind": "evidence-file", "path": "/absolute/path/dossier.json", "evidence_id": "ev_…"}, "view": {"kind": "native", "facet": "pseudocode", "offset": 0, "limit": 4096}}`.
|
||||
MCP `analyze_function` defaults to the same summary and retains the complete
|
||||
dossier in the session for `inspect_analysis_view`.
|
||||
|
||||
Run `rea --help` or a command's `--help` for arguments and output options.
|
||||
The [generated catalog](mcp-contracts.md#generated-catalog) lists all CLI commands and MCP
|
||||
tools. Provider-specific target support is described in the
|
||||
@@ -251,7 +439,11 @@ an exact result when the target bytes, operation, parameters, provider and
|
||||
settings match. Mutations and cursor-dependent calls are excluded from the
|
||||
cache. Snapshot files are local and use owner-only permissions.
|
||||
|
||||
Dispatching a metadata write invalidates the active session's snapshot cache,
|
||||
Importing an Evidence bundle refreshes retained subject display names and paths.
|
||||
It preserves cached analysis results, including observations for other targets.
|
||||
Snapshot bindings that use a replaced record retain its updated display path.
|
||||
|
||||
Dispatching a provider metadata write invalidates the active session's snapshot cache,
|
||||
even if its reply fails: the provider may already have applied the write.
|
||||
Close and reopen the target before importing or saving another snapshot.
|
||||
|
||||
@@ -276,6 +468,20 @@ cached result. `close_binary` accepts `snapshot_path` and optional
|
||||
`overwrite: true` to save before releasing provider resources. A failed save
|
||||
leaves the session open so the caller can resolve the output failure.
|
||||
|
||||
If a selected snapshot's file descriptor cannot close, `open_binary` reports
|
||||
`cleanup_incomplete`. `binary_session` reports `cleanup_pending` even when no
|
||||
target opened. Retry `close_binary` to release the retained descriptor before
|
||||
opening another target.
|
||||
|
||||
If a snapshot switch fails after the previous target was retired, restoration
|
||||
keeps that target's analysis commitments with the replacement client. This
|
||||
includes the native dispatch workflow profile on a profileless route, so its
|
||||
existing snapshot and fresh observations retain the same provenance.
|
||||
|
||||
For native dispatch, the snapshot binding retains the reader profile; workflow
|
||||
Evidence and `workflow_entries` retain the composed profile, which commits that
|
||||
reader and the workflow's analysis caveats. Restoration preserves both profiles.
|
||||
|
||||
The MCP save receipt reports `primitive_entries`, `workflow_entries`, and
|
||||
`evidence_records` separately. Zero primitive bindings can still accompany retained workflow
|
||||
results and Evidence. These are cached observations, not a saved provider
|
||||
@@ -293,9 +499,22 @@ canonical form, or compare two supplied bundles:
|
||||
rea evidence-import /absolute/path/to/evidence/bundle.json
|
||||
rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
|
||||
rea compare /absolute/path/to/evidence/left.json /absolute/path/to/evidence/right.json
|
||||
rea compare left.json right.json --record_pairs '[{"left_evidence_id":"ev_<left-sha256>","right_evidence_id":"ev_<right-sha256>"}]' --json
|
||||
```
|
||||
|
||||
`--record_pairs` supplies an explicit JSON array of observation pairs, matching
|
||||
MCP `compare_bundles.record_pairs`. Pair IDs must exist on their named sides and
|
||||
be one-to-one. Without pairs, one-sided membership remains unknown; REA does
|
||||
not infer correspondence from position or display names.
|
||||
|
||||
Exports preserve an existing destination unless `--overwrite` is explicit.
|
||||
|
||||
Atomic Evidence output stages on the selected destination's filesystem. If the
|
||||
OS pathname ceiling prevents its internal staging name, REA retries in a shorter
|
||||
ancestor on that same filesystem and removes the unused stage. Existing output
|
||||
protection still applies. A genuine pathname refusal uses `resource_constraint`
|
||||
with `details.reason: path-limit` and `boundary: filesystem-path`.
|
||||
|
||||
For application traces and version comparisons, see
|
||||
[JavaScript application workflows](javascript-application-workflows.md).
|
||||
MCP [retained Evidence references](mcp-contracts.md#retained-application-evidence-inputs)
|
||||
@@ -308,6 +527,13 @@ by file order. Omit `--evidence-bundle` when the request supplies inline Evidenc
|
||||
|
||||
## Import historical source
|
||||
|
||||
Historical source inventories preserve exact filenames, including literal backslashes
|
||||
on POSIX hosts. Relative import lookup uses the recorded source file's owning
|
||||
directory and decodes URL percent escapes once; encoded slash, NUL and malformed
|
||||
escapes remain unresolved. A decoded backslash remains part of a filename rather
|
||||
than a directory separator. This inventory lookup does not claim that Node can
|
||||
load that filename or apply Node's runtime file-URL admission policy.
|
||||
|
||||
Import an older source tree to compare with the current artifact:
|
||||
|
||||
```bash
|
||||
@@ -317,12 +543,26 @@ rea import-reference-source /absolute/path/to/source --detail summary --outline-
|
||||
|
||||
The default (`--detail full`) returns the complete `HistoricalSourceGraph`.
|
||||
Choose `--detail summary` for a `HistoricalSourceSummary` of that same committed
|
||||
inventory: its `HistoricalSourceManifest` contains the full graph and root
|
||||
inventory: its `HistoricalSourceManifest` contains the semantic graph and root
|
||||
digests, with provenance, VCS facts, languages, manifest paths, entry and
|
||||
relationship counts, parse-failure counts, file/directory states, exclusions
|
||||
and limitations. The summary is a presentation view, not a graph accepted by
|
||||
source-to-bundle comparison; keep the full output for that workflow.
|
||||
|
||||
Known filesystem failures commit the operation, parsed errno, entry-relative
|
||||
location and recovery guidance. Entry `local_diagnostics`, when present,
|
||||
retains the exact host message, including local paths, outside the semantic
|
||||
graph commitment. Equivalent permission failures therefore retain their manifest
|
||||
identity after relocation while preserving original source diagnostics in full
|
||||
output and visible summary outline entries. Other substantive limitations still
|
||||
participate in the graph commitment.
|
||||
|
||||
An unreadable child directory remains a partial inventory entry. Directory-scoped
|
||||
ignore policy is required before enumerating a directory that can be opened;
|
||||
an unreadable or invalid policy still fails the import. Project negations cannot
|
||||
override defaults or caller exclusions; caller patterns retain their own ordered
|
||||
negations. Configured secret exclusions remain final.
|
||||
|
||||
`--outline-depth` requires `--detail summary` and accepts a nonnegative safe
|
||||
integer. Depth 1 shows root children, depth 2 (the default) also shows their
|
||||
children, and depth 0 omits all outline entries. Paths retain canonical Unicode
|
||||
@@ -342,11 +582,26 @@ output, not the amount of source examined. A shallow tree or many exclusions can
|
||||
still produce a large summary; depth 0 removes the outline but preserves the
|
||||
other facts. No fixed output-size or runtime improvement is promised.
|
||||
|
||||
Import failures use the shared error contract (`code`, `category`, `message`,
|
||||
`retryable`, and `remediation`). When reads completed before failure,
|
||||
`details.diagnostics.partial` retains the reader facts, with file bytes as
|
||||
arrays of byte values. Unresolved cleanup uses `cleanup_incomplete` and
|
||||
retains the source failure and resource paths in `details`.
|
||||
|
||||
The import records hashes and metadata for the supplied files, separately
|
||||
from observations of the current app. For a directory inside a Git checkout,
|
||||
it records the HEAD of the nearest enclosing repository; whether the working
|
||||
tree matches that commit remains unknown. It honors the source tree's `.gitignore`
|
||||
and applies default exclusions for common generated and dependency paths such
|
||||
tree matches that commit remains unknown. Root selection follows filesystem
|
||||
symlink/parent semantics before canonical identity is derived. It honors
|
||||
`.gitignore` files at the selected root and in traversed child directories,
|
||||
with patterns relative to their containing directory and deeper negations
|
||||
applied only when the parent was not excluded. Matching uses the nearest
|
||||
repository's local `core.ignorecase` setting (false when unset), including the
|
||||
shared configuration of linked worktrees. Policy and Git metadata reads retain
|
||||
their descriptor cleanup under the import's resource owner; unreadable policy
|
||||
is a typed failure, not an empty policy. Above-root `.gitignore` files, global
|
||||
excludes, `.git/info/exclude`, config includes and worktree-specific config are
|
||||
not loaded. The import also applies default exclusions for common generated and dependency paths such
|
||||
as `node_modules/`, `dist/`, and `*.log`. Pattern-based exclusions record the
|
||||
matched pattern and whether it came from project, default, caller, or sensitive
|
||||
path policy. Links into excluded paths retain their internal target metadata;
|
||||
@@ -365,11 +620,54 @@ Entries that change during an import retain an unavailable-entry diagnostic
|
||||
alongside readable siblings. If enumeration of an already admitted root fails,
|
||||
the graph keeps its observed children and records the root failure as a
|
||||
top-level limitation. That partial inventory does not establish absence among
|
||||
unexamined children.
|
||||
unexamined children. POSIX directory-entry bytes that are not valid UTF-8 cannot
|
||||
be represented as graph paths; the containing directory (or root) is marked
|
||||
unsupported, and REA does not access the entry through a replacement-character
|
||||
name. Symlink targets are read as bytes too: a target that is not valid UTF-8
|
||||
is retained as an unreadable symlink with its bytes reported in the limitation,
|
||||
and is never resolved through a replacement-character path. Readable siblings
|
||||
remain inventoried.
|
||||
|
||||
JavaScript and TypeScript import parsing requires valid UTF-8. Malformed source
|
||||
bytes retain their original hashes and sizes with a decoding diagnostic; REA
|
||||
does not infer module targets from replacement characters.
|
||||
does not infer module targets from replacement characters. Exact dynamic-import
|
||||
strings, expression-free templates and runtime-transparent TypeScript wrappers
|
||||
retain their targets; computed imports remain unknown. TypeScript import-equals
|
||||
dependencies remain `requires` relationships whether or not they are exported.
|
||||
Historical dependencies also include TypeScript import types (`import("./dep").T`
|
||||
and `typeof import("./dep")`) and declarations inside ambient module bodies;
|
||||
these source relationships do not assert runtime loading. Language labels retain
|
||||
the conventional case-sensitive `.C` (C++) and `.M` (Objective-C++) suffixes.
|
||||
|
||||
REA derives historical CommonJS paths only when the selected loader and its
|
||||
base are proven: this includes the exact
|
||||
`createRequire(new URL("./file.cjs", import.meta.url))` form and
|
||||
`require.resolve` with `{ paths: [__dirname] }`. Exact optional-call forms such
|
||||
as `require.resolve?.("./dep.cjs")` use the same loader and mutation facts. The
|
||||
nearest inventoried package manifest supplies `.js`/`.ts` package facts to the
|
||||
shared parser; explicit filename modes take precedence. A known ES module has no
|
||||
implicit CommonJS wrapper, including when a factory is derived from an unbound
|
||||
`require`. An unreadable, excluded or malformed manifest, or package scope outside
|
||||
the selected inventory, leaves otherwise unproven loader resolution unknown with
|
||||
the requested specifier and a reason. A nearest manifest without a `type` field
|
||||
uses the shared parser's JavaScript module-syntax facts. Unresolved TypeScript,
|
||||
JSX compilation and declaration contexts do not establish runtime wrapper bases.
|
||||
Other custom bases and search options remain partial with unknown resolution and
|
||||
the literal requested specifier. Overwritten resolver methods retain that uncertainty; they are not
|
||||
attributed to Node. `require.main` is a module object, so calling it does not
|
||||
establish a loader reference. These relationships do not establish execution;
|
||||
Node's [`require.resolve`](https://nodejs.org/docs/v24.22.1/api/modules.html#requireresolverequest-options)
|
||||
performs lookup without loading the target. An excluded `package.json`
|
||||
also leaves package-directory selection unknown rather than treating the
|
||||
manifest as absent and applying an `index` fallback.
|
||||
|
||||
Import requests use URL pathname semantics: query and fragment remain request
|
||||
identity, percent escapes decode exactly once for inventory lookup, and encoded
|
||||
separators or malformed escapes retain an unresolved relationship and diagnostic.
|
||||
URL-bearing relationships retain the authored `source_specifier` separately from
|
||||
the inventoried `to` path; distinct query/fragment requests remain distinct even
|
||||
when they name the same file. CommonJS requests retain literal filename semantics
|
||||
for percent signs, query marks and fragments.
|
||||
|
||||
Historical-source import requires safe no-follow file opens on Linux or
|
||||
macOS. Native Windows returns `unsupported_host`; use Linux REA inside WSL
|
||||
@@ -481,8 +779,74 @@ of the serve command's stopped document. See
|
||||
[agent environments](agent-environments.md#export-a-bundle) for the folder
|
||||
layout and what the smoke task checks.
|
||||
|
||||
## Compare an environment with its application
|
||||
|
||||
`rea environment-compare <manifest.json> --evidence <capture.json> --probe <probe.json> [--probe <probe.json> ...]`
|
||||
compares the environment the pair serves with probe captures of the application it came from. Each probe is capture
|
||||
Evidence of one call sequence recorded against the application after a reset; the environment's state is reset before
|
||||
each probe, every request to the application's origin is answered as the served environment answers it, and the
|
||||
answer is compared with the application's after ids are mapped and clocks and generated values are normalised. The command reads files only and
|
||||
makes no network call. It prints the report and exits `0`, or prints a structured `invalid_request` error and exits `1`
|
||||
for the same pair refusals as `environment-serve`, a probe that is not capture Evidence, or a browser probe captured
|
||||
without response bodies, or without the request bodies its writes need. See
|
||||
[agent environments](agent-environments.md#measure-fidelity-against-the-application) for the report.
|
||||
|
||||
```sh
|
||||
rea environment-compare manifest.json --evidence capture.json --probe probe.json --json
|
||||
```
|
||||
|
||||
## Report version and runtime
|
||||
|
||||
`rea --version` prints the package version. Add an output format to get the
|
||||
version with the runtime and platform context that a support request needs:
|
||||
|
||||
Version reports do not initialize analysis providers. Invalid global settings
|
||||
or output options still report their usual errors.
|
||||
|
||||
```bash
|
||||
rea --version --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "rea-agents",
|
||||
"version": "6.7.0",
|
||||
"runtime": { "id": "node", "version": "24.22.1" },
|
||||
"platform": "linux",
|
||||
"architecture": "x64",
|
||||
"supported_runtime": {
|
||||
"range": "^22.23.4 || ^24.22.1 || >=26.12.1",
|
||||
"satisfied": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Meaning |
|
||||
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `name`, `version` | Published package name and version; `version` matches plain `--version`. |
|
||||
| `runtime.id` | `node` or `bun`. |
|
||||
| `runtime.version` | Version of that runtime. |
|
||||
| `runtime.node_compatibility_version` | Bun only: the Node.js version it reports compatibility with. |
|
||||
| `platform`, `architecture` | Node.js `process.platform` and `process.arch`, such as `linux` and `x64`. |
|
||||
| `supported_runtime.range` | Supported Node.js range. |
|
||||
| `supported_runtime.satisfied` | Whether this runtime is supported, judged as `rea doctor` judges it; Bun by its Node.js compatibility version. |
|
||||
|
||||
The field names are stable. The report contains no paths, environment values,
|
||||
user or host names, and it starts no provider and reads no target. `--format`
|
||||
selects any CLI output format, such as `--format yaml`. JSON follows the usual
|
||||
rule: indented on a terminal, compact when piped or redirected. `--help` still
|
||||
takes precedence. With other output controls, `--version` keeps its existing
|
||||
behavior: valid controls such as `--filter-output` or `--full-output` print the
|
||||
plain version, and invalid combinations report the usual output-flag error,
|
||||
such as `UNSUPPORTED_OUTPUT_COMBINATION` for `--json` with `--token-limit`. Use
|
||||
`rea doctor` for provider and host readiness.
|
||||
|
||||
## Output and exit status
|
||||
|
||||
Commands reject surplus positional arguments before execution, including commands
|
||||
that accept no positionals. Use `rea <command> --help` for the declared argument
|
||||
shape; a final variadic argument continues to accept multiple values.
|
||||
|
||||
`rea mcp doctor --json` checks production MCP startup and reports child logs in
|
||||
`diagnostics`. It drains stderr while retaining at most its first 64 KiB of raw
|
||||
bytes as text, omitting a final UTF-8 code point if it crosses that byte limit.
|
||||
@@ -494,15 +858,34 @@ truncation alone does not change the diagnostic health or exit status.
|
||||
The default terminal format is TOON. Use `--json` when saving results for a
|
||||
JSON consumer. Valued output flags (`--format`, `--filter-output`,
|
||||
`--token-limit`, `--token-offset`) accept `--flag value` or `--flag=value`; a
|
||||
missing value fails with `MISSING_OUTPUT_VALUE` before the command runs. `--json` is indented on a terminal and compact when piped or
|
||||
missing value fails with `MISSING_OUTPUT_VALUE` before the command runs.
|
||||
Malformed format values and token windows fail with `INVALID_OUTPUT_VALUE` in the
|
||||
selected structured format. Token limits and offsets must be nonnegative safe
|
||||
integers; zero selects an empty window. These checks run before command execution. `--json` is indented on a terminal and compact when piped or
|
||||
redirected, which keeps large results smaller for agents and scripts; pipe
|
||||
through `jq .` for indented files. Output selection and formatting do not change
|
||||
operation status.
|
||||
|
||||
`--format md` escapes backslashes and pipes in table headers and cells, and
|
||||
renders cell line breaks as `<br>`. These are presentation changes; retained
|
||||
Evidence and JSON output keep the original values. Nested objects and arrays
|
||||
use JSON inside their owning Markdown cells, retaining the field names and
|
||||
values instead of replacing them with an object label. Table widths are measured
|
||||
iteratively, so an admitted row page is not limited by the JavaScript function
|
||||
argument count.
|
||||
|
||||
`--filter-output` selects fields from successful command data. Classified failures
|
||||
retain their complete typed diagnostic projection even when the selector matches
|
||||
a field such as `code` or `message`, or names a missing field. `--full-output`
|
||||
controls whether the outer `{ok, error, meta}` envelope is included.
|
||||
Selected own JSON members, including `__proto__`, remain ordinary output data.
|
||||
|
||||
Sibling array slices that select different fields are combined by original row
|
||||
index and emitted in source order, regardless of selector order. Selecting a
|
||||
whole parent row keeps that row intact and takes precedence over narrower fields
|
||||
selected from the same row. For example, `rows[0,2].name,rows[1,3].version`
|
||||
combines both fields for overlapping rows, while `rows[0,2],rows[1,3].version`
|
||||
retains rows 0 and 1 whole.
|
||||
|
||||
For `cutter-command`, `--json-output` requests command-specific JSON from
|
||||
Cutter when available. This is separate from `--format json`, which selects the
|
||||
@@ -532,11 +915,20 @@ Refusals before binding (unreadable files, non-Evidence JSON, capture Evidence
|
||||
in a format other than HAR, a manifest or state whose parent is not the
|
||||
supplied evidence, an unbindable port) print a structured
|
||||
error and exit `1`, as does a listener that fails after binding.
|
||||
If shutdown fails, the command reports `cleanup_incomplete` with the retained
|
||||
server resources. It preserves a primary listener diagnostic, or a
|
||||
`shutdown_observation` naming the requested shutdown and its URL, environment,
|
||||
tool count and Evidence identifiers. The successful `stopped` document is
|
||||
returned only after cleanup is confirmed. The same in-process server owner
|
||||
retains failed stages for retry; the CLI exposes no separate retry command.
|
||||
`rea environment-export` is one-shot: it prints the written files and exits
|
||||
`0`, or prints a structured `invalid_request` error and exits `1` for the same
|
||||
pair refusals, a non-empty or file-occupied directory, a missing package
|
||||
tarball or a name that is not a kebab-case label or is a Windows device name,
|
||||
writing nothing in each case.
|
||||
writing nothing in each case. A file occupying the destination is reported as
|
||||
requiring a directory; identity-change diagnostics are reserved for admitted
|
||||
paths that fail later identity checks. Host permission failures identify whether
|
||||
input reading or bundle writing was denied and retain the reported path.
|
||||
A write that fails after the first file (a permission, disk-space or
|
||||
pathname-length failure on a later path) propagates as the host's failure,
|
||||
naming its cause and, when it failed on one bundle file or the package
|
||||
@@ -557,7 +949,9 @@ browser, web and Electron commands (including `analyze-web-bundle`,
|
||||
for Android/ADB, Apktool/JEB/Flutter, firmware, PCAP and signal analysis,
|
||||
WASM/EVM, Go/Rust and binary diagnostics, Frida instrumentation, the Cutter
|
||||
command, objdump, Rizin and GDB, and the agent environment server
|
||||
(`environment-serve`). In these paths, a returned structured
|
||||
(`environment-serve`). The handler also wraps `doctor`,
|
||||
`inspect-sqlite-database`, `recover-javascript-sources`,
|
||||
`inspect-il2cpp-metadata`, and `inspect-desktop-application`. In these paths, a returned structured
|
||||
`cancelled` result can be printed before the wrapper assigns the
|
||||
signal-derived status. This describes the current source routing; it is not a
|
||||
claim that every command or provider runtime has been independently exercised.
|
||||
@@ -575,9 +969,9 @@ CLI stops writing, prints no error and exits `141` (`128 + 13`, the status of a
|
||||
SIGPIPE-terminated writer). The operation itself may have completed; only its
|
||||
output was declined.
|
||||
|
||||
`inspect-artifact` also returns a structured cancelled result, but its direct
|
||||
analysis cancellation path does not set a signal-derived exit status, so it
|
||||
exits `1`. Setup has its own contract: cancellation exits `0`. Treat the
|
||||
`inspect-artifact` also returns a structured cancelled result. Its logging
|
||||
wrapper sits outside the cancellation handler and replaces the signal-derived
|
||||
status with the analysis failure status, so it exits `1`. Setup has its own contract: cancellation exits `0`. Treat the
|
||||
structured result and process status as separate signals when scripting
|
||||
cancellation.
|
||||
|
||||
@@ -593,3 +987,13 @@ REA's failure status:
|
||||
set -o pipefail
|
||||
rea inspect-artifact ./app.asar --json | jq . > inspection.json
|
||||
```
|
||||
|
||||
`--token-count` reports a tokenx estimate for the selected emitted document,
|
||||
including a requested full-output envelope and metadata. JSON uses the same
|
||||
terminal indentation or compact piped representation as normal output; JSONL
|
||||
uses its emitted line representation. The final newline is included (tokenx
|
||||
assigns whitespace no tokens). This is an estimate, not a model tokenizer count.
|
||||
Counting retains the existing bounded formatted-string allocation after capacity
|
||||
admission; omit the flag to stream large JSON documents without materializing
|
||||
the complete formatted text. Exhausted token windows display an empty range at
|
||||
the total token count and have no next offset.
|
||||
+188
-13
@@ -6,6 +6,23 @@ for the reviewed setup transaction and [client IDs](installation.md#supported-ag
|
||||
The versioned verification notes below describe their stated runs; live service
|
||||
acceptance is verified only where explicitly recorded.
|
||||
|
||||
## Client tool names and discovery
|
||||
|
||||
REA advertises canonical names such as `binary_session`. The MCP host decides
|
||||
how to expose them to its model: a name such as `mcp__rea__binary_session` is a
|
||||
client convention, not a prefix REA can enforce. Use the active client's tool
|
||||
listing and server association to find the operation. A valid registration or
|
||||
installed skill does not prove that those tools reached the model. Doctor reports
|
||||
live-server identity as unknown; query `binary_session` through the active client
|
||||
to observe the running server's identity.
|
||||
|
||||
If a client or fork exposes several servers under the same prefix, a wildcard
|
||||
for that prefix does not isolate REA permissions. Inspect that client's actual
|
||||
tool names and approval rules before selecting a permission scope. REA setup
|
||||
does not repair a client's naming behavior or infer permission drift from an
|
||||
unfamiliar prefix. The unavailable fork reported in
|
||||
[#5512](https://github.com/morluto/rea/issues/5512) has not been reverified.
|
||||
|
||||
## Fiddler Everywhere alongside REA
|
||||
|
||||
Connect Fiddler Everywhere's MCP server separately for HTTP traffic debugging,
|
||||
@@ -14,6 +31,12 @@ preserving your REA client entry. See the
|
||||
|
||||
## GitHub Copilot CLI
|
||||
|
||||
Setup preserves an existing `tools` selection in the REA registration, including
|
||||
an empty list. New registrations default to `["*"]`. A restricted selection is
|
||||
aligned configuration; doctor does not widen it to expose every REA tool. See
|
||||
[Copilot's MCP configuration](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers)
|
||||
for tool-selection settings.
|
||||
|
||||
For GitHub Copilot CLI 1.0.95, `gpt-4.1` model metadata can block ordinary
|
||||
chat with REA's full input-schema profile before sending a model request
|
||||
(`compaction_static_context_blocked`; [#1554](https://github.com/morluto/rea/issues/1554)).
|
||||
@@ -73,12 +96,17 @@ to rerun it.
|
||||
A process that the MSIX app started, such as a terminal in Claude Desktop's
|
||||
Code tab, sees the package's private files at their `%APPDATA%` paths even
|
||||
though it reports no package identity. Setup compares one of those files at
|
||||
both paths. When it finds that redirected view, the app is running, so setup
|
||||
and uninstall refuse to change Claude Desktop's configuration; rerun them from
|
||||
a terminal that Claude Desktop did not start. If the package has no private
|
||||
file to compare, setup and uninstall cannot rule that out and also refuse; open
|
||||
Claude Desktop once if it has never run, quit it fully, then rerun. Doctor
|
||||
reads the same file from either kind of process.
|
||||
both paths: first one from the package's private `Claude` folder, and when
|
||||
that folder has none (some installations keep writing the real
|
||||
`%APPDATA%\Claude`), one from the package's other private roaming folders,
|
||||
which the package redirects in the same way. When it finds that redirected
|
||||
view, the app is running, so setup and uninstall refuse to change Claude
|
||||
Desktop's configuration; rerun them from a terminal that Claude Desktop did not
|
||||
start. If the package keeps no private roaming file at all, setup and
|
||||
uninstall cannot rule that out and also refuse. With Claude Desktop fully
|
||||
quit, add REA's entry by hand to the file setup names, which is the one the
|
||||
app reads, or remove it from both the private copy and the `%APPDATA%` file.
|
||||
Doctor reads the same file from either kind of process.
|
||||
|
||||
The same process also writes everything under `%APPDATA%` and
|
||||
`%LOCALAPPDATA%` into the package's private storage, where other apps never
|
||||
@@ -154,8 +182,16 @@ startup; an explicit Ghidra startup timeout of 2700000 ms produces a 2820000 ms
|
||||
tool timeout. The startup allowance follows an explicitly selected Ghidra or
|
||||
Hopper provider, and otherwise uses the larger known Ghidra/Hopper deadline.
|
||||
An explicit `REA_JEB_MCP_TIMEOUT_MS` deadline, shared by catalog discovery and
|
||||
each tools/call, also contributes to that maximum. For `ida` or `auto` selection, a valid IDA registration's parsed
|
||||
each tools/call, also contributes to that maximum. An explicit `REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS` is added to the Ghidra startup allowance, because a single-decompile call can import the target and then decompile up to that limit; setup forwards it into the registration. For `ida` or `auto` selection, a valid IDA registration's parsed
|
||||
`timeoutMs` also contributes to that maximum; its default is 300000 ms.
|
||||
The derived value is a single-decompile estimate, not a deadline for an
|
||||
unbounded batch. For example, startup 330 seconds plus two 600-second
|
||||
decompiles and 120 seconds of headroom needs an estimated 1650 seconds,
|
||||
before any additional listing or output work. Select the client tool deadline
|
||||
for the intended workload, split `batch_decompile`, use `decompile_all`
|
||||
limit/resume, or reduce `trace_native_values` `max_functions`. The trace can
|
||||
decompile several functions serially; its depth and graph budgets do not
|
||||
make the single-decompile allowance a whole-traversal deadline. Existing positive caller deadlines are preserved.
|
||||
Setup forwards these explicitly selected REA deadlines and the input schema
|
||||
profile into the registration; unrelated process values are omitted. An
|
||||
explicit `REA_JADX_OPERATION_TIMEOUT_MS` is forwarded with the JADX
|
||||
@@ -237,22 +273,82 @@ and the selected `trustedFolders.json` (including
|
||||
`GEMINI_CLI_TRUSTED_FOLDERS_PATH`). Settings in an untrusted workspace do not
|
||||
participate. A workspace or system `mcpServers.rea` replaces the entire user
|
||||
entry; setup stops if the winning definition conflicts with its planned entry.
|
||||
Setup checks user policy against the file snapshot it is inspecting or about to
|
||||
update, including confirmed absence; an earlier discovery snapshot does not veto
|
||||
a repaired user policy. Independent system and workspace sources are still checked.
|
||||
Review those files manually: setup never edits administrator/workspace settings
|
||||
or grants workspace trust. Trust supplied only by a running IDE is outside this
|
||||
standalone CLI check.
|
||||
|
||||
A model API's `400 INVALID_ARGUMENT` alone does not identify a rejected schema,
|
||||
tool-count limit or skill defect. Preserve the response's parameter path and
|
||||
diagnostic, the actual running REA version, Gemini CLI version and selected
|
||||
model. For schema rejection, review the [input presentation profiles](mcp-contracts.md#compact-input-schema-profile)
|
||||
and reconnect after changing REA's registration environment. Compact is the
|
||||
current default; an explicitly stored `full` choice is preserved. The generic
|
||||
error in [#4620](https://github.com/morluto/rea/issues/4620) does not establish
|
||||
which constraint failed, and local schema checks do not prove live API acceptance.
|
||||
|
||||
## Codex and Devin
|
||||
|
||||
REA manages its launcher and selected provider settings in the client
|
||||
registration. Codex's server and per-tool approval policies are user choices.
|
||||
Setup patches REA's connection fields in the selected file and retains other
|
||||
server settings, including `default_tools_approval_mode`, nested
|
||||
`[mcp_servers.rea.tools.<name>]` tables, tool allow/deny lists, and unrecognized
|
||||
client settings. Doctor accepts Codex's per-tool policy tables. Known malformed
|
||||
policy values block setup before it writes the configuration or a backup, and
|
||||
the diagnostic identifies the setting to repair. See the
|
||||
[Codex configuration reference](https://developers.openai.com/codex/config-reference/)
|
||||
for the supported policy values.
|
||||
|
||||
When direct launcher paths prove the same executable or Node entrypoint,
|
||||
setup retains the existing caller-selected spelling. An otherwise aligned
|
||||
registration therefore needs no rewrite or recovery backup merely because
|
||||
REA was invoked through another symlink. Setup also retains that spelling when
|
||||
another field, such as an invalid timeout, needs repair. Unresolved identity
|
||||
does not prove equivalence.
|
||||
|
||||
| Settings | Setup ownership |
|
||||
| ------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| Launcher, transport and activation | Refreshes the REA stdio connection and enables it. |
|
||||
| Explicit server environment | Preserves configured overrides and merges selected provider settings. |
|
||||
| Codex timeouts | Retains positive values; repairs missing or invalid values to recommended REA defaults. |
|
||||
| Codex approval policies and tool selections | Retains caller choices; refuses known malformed values. |
|
||||
| Unrelated or future server settings | Retains their parsed values. |
|
||||
|
||||
Dry-run applies the same policy validation and writes no files. A successful
|
||||
refresh keeps an exact recovery snapshot of the original configuration;
|
||||
repeating setup with the same settings performs no write. Uninstall removes
|
||||
the owned REA entry, including its policy, while retaining other servers and
|
||||
the recovery snapshot. A green registration check establishes known
|
||||
configuration alignment; it does not prove that a particular client or fork
|
||||
successfully exposes every REA tool.
|
||||
|
||||
Codex treats an unset or empty `CODEX_HOME` as `~/.codex`. Its MCP server
|
||||
registration supports `startup_timeout_sec` for connection initialization and
|
||||
`tool_timeout_sec` for each tool call. New REA registrations use a 30-second
|
||||
startup timeout and a tool timeout derived from the known provider
|
||||
allowance plus 120 seconds of operation headroom. This yields 450 seconds for
|
||||
the default Ghidra allowance; an explicit Ghidra startup timeout of 2700
|
||||
seconds yields 2820 seconds. Setup preserves positive caller-selected timeout
|
||||
seconds yields 2820 seconds. That 30-second Codex figure is client guidance.
|
||||
REA's own phase budgets are `REA_MCP_INITIALIZE_BUDGET_MS` (default 15000),
|
||||
`REA_MCP_TOOLS_LIST_BUDGET_MS` (default 5000),
|
||||
`REA_MCP_FIRST_CATALOG_BUDGET_MS` (default 20000), and
|
||||
`REA_MCP_DOCTOR_DEADLINE_MS` (default 30000). Each is a positive decimal
|
||||
integer of milliseconds from 1 to 2147483647. Unset keeps the default.
|
||||
Invalid values fail configuration and name the setting. Setup copies a
|
||||
caller-selected value into the registration, and a change is read from the
|
||||
environment that starts REA, so it needs a restart. Doctor and packaged
|
||||
startup probes name the effective milliseconds, whether they came from the
|
||||
default or the setting, the default, the floor, and the maximum. REA does
|
||||
not enforce Codex's 30 seconds as the doctor deadline. The 120000 ms
|
||||
tool-operation headroom and the 64 KiB doctor stderr cap stay fixed. Setup preserves positive caller-selected timeout
|
||||
values in the REA entry, and doctor reports a non-blocking advisory when a tool
|
||||
timeout is shorter than the known provider allowance. An explicitly selected
|
||||
JEB request budget also contributes to the allowance. A configured IDA
|
||||
JEB request budget also contributes to the allowance, and an explicit
|
||||
`REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS` is added to the Ghidra startup
|
||||
allowance. A configured IDA
|
||||
registration's parsed `timeoutMs` contributes for `ida` or `auto` selection.
|
||||
An explicit `REA_JADX_OPERATION_TIMEOUT_MS` is forwarded with the JADX
|
||||
registration but does not contribute; raise `tool_timeout_sec` to cover a
|
||||
@@ -261,12 +357,44 @@ These values are client-side deadlines, so increase them in `~/.codex/config.tom
|
||||
workload needs more time. Devin honors `XDG_CONFIG_HOME` for its
|
||||
`devin/mcp_config.json` on non-Windows systems.
|
||||
|
||||
### Codex with Moonshot or Kimi models
|
||||
|
||||
An older REA input containing acyclic local references was rejected by a
|
||||
Responses-compatible Kimi gateway as “infinite recursion”; expanding those
|
||||
references succeeded. A separate full-profile input failed the provider's
|
||||
schema-size limit. These are distinct reported failures, not proof that the
|
||||
canonical schema is recursive. Current REA inputs are already self-contained;
|
||||
see [schema advertisement](mcp-contracts.md#schema-advertisement).
|
||||
|
||||
For this failure, inspect the active MCP launch command and refresh the REA
|
||||
installation that command actually runs. A configured package selector or the
|
||||
installed skill version alone does not identify a cached or still-running
|
||||
server. Follow [installation and update guidance](installation.md#diagnose-update-and-remove).
|
||||
Then merge this setting into the REA entry in `$CODEX_HOME/config.toml`
|
||||
(`~/.codex/config.toml` by default), preserving its launcher and other settings:
|
||||
|
||||
```toml
|
||||
[mcp_servers.rea.env]
|
||||
REA_MCP_INPUT_SCHEMA_PROFILE = "compact"
|
||||
```
|
||||
|
||||
If that table already exists, add or update the key there. Restart/reconnect
|
||||
Codex so REA reads the new environment. Compact is now REA's default, but an
|
||||
explicit `full` override remains effective until changed. It preserves the
|
||||
tool inventory and canonical server validation while reducing advertised input
|
||||
detail. The probes in [#5160](https://github.com/morluto/rea/issues/5160) covered
|
||||
individual tools through one gateway, not the entire current catalog or a
|
||||
model-driven analysis workflow.
|
||||
|
||||
## OMP
|
||||
|
||||
For OMP, setup writes a `type: "stdio"` entry to the user-level
|
||||
`~/.omp/agent/mcp.json`. It follows `PI_CONFIG_DIR` and
|
||||
`PI_CODING_AGENT_DIR` (relative to the setup working directory), and the profile selected by `OMP_PROFILE` or
|
||||
`PI_PROFILE` (`~/.omp/profiles/<name>/agent/mcp.json`). Setup also removes
|
||||
`~/.omp/agent/mcp.json`. A relative `PI_CONFIG_DIR` selects a configuration root
|
||||
under the home directory. For the default profile, `PI_CODING_AGENT_DIR`
|
||||
overrides the agent directory; a relative value uses the setup working
|
||||
directory. A named profile selected by `OMP_PROFILE` or `PI_PROFILE` instead
|
||||
uses `<configuration-root>/profiles/<name>/agent/mcp.json` and ignores
|
||||
`PI_CODING_AGENT_DIR`. Setup also removes
|
||||
`rea` from that file's `disabledServers` list, which would otherwise hide the
|
||||
registration. Doctor treats an `enabled: false` entry as active when
|
||||
`enabledServers` lists `rea`, as OMP does, unless `disabledServers` also lists
|
||||
@@ -285,7 +413,11 @@ Setup, doctor and uninstall follow the root's `active_profile` selection, as
|
||||
plain Hermes CLI commands do. An explicit `HERMES_HOME` pointing to
|
||||
`profiles/<name>` selects that named profile directly. An invalid, missing or
|
||||
deleted sticky profile is reported instead of writing into the default home;
|
||||
repair the profile or run `hermes profile use default` before retrying.
|
||||
it also remains an unresolved skill selection, so setup cannot report a
|
||||
successful empty installation. Overrides nested beneath the native Hermes home
|
||||
use that native home's sticky selection and profile root together, matching
|
||||
[Hermes's root rules](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_constants.py#L83).
|
||||
Repair the profile or run `hermes profile use default` before retrying.
|
||||
REA follows `HERMES_HOME` whitespace trimming, environment-variable expansion,
|
||||
and home expansion before resolving that profile. Setup previews the resolved
|
||||
configuration and skill destinations; doctor and uninstall use those same paths.
|
||||
@@ -446,3 +578,46 @@ npx -y rea-agents@<version> mcp
|
||||
|
||||
Do not put credentials in that command or its arguments. `rea doctor --client grok_bot`
|
||||
reports this manual step. `rea uninstall` does not remove the account connector.
|
||||
|
||||
## Skill selection and maintenance
|
||||
|
||||
OpenCode's selected configuration layers determine the registration dialect.
|
||||
If any loaded layer uses native `mcp.servers`, setup writes a native REA entry
|
||||
in the selected upper file even when that file is absent or has no MCP table.
|
||||
Legacy activation is normalized before layer merging, so an explicit enable
|
||||
can override an inherited disable. Lower files and other server entries remain
|
||||
unchanged; a legacy server named `servers` that conflicts with the native table
|
||||
requires manual repair before setup can write.
|
||||
|
||||
When Hermes aliases refer to an anchored REA entry, refreshing or removing REA
|
||||
retains the aliases' original resolved values. Setup does not redirect another
|
||||
server's alias to REA's new launcher or policy.
|
||||
|
||||
With no client selected, the interactive skill choice targets the shared
|
||||
`~/.agents/skills` copy. Selecting a client targets its personal destination
|
||||
when supported. Use `rea setup --skill-client shared --skill` for the shared
|
||||
copy, or `--skill-client claude_code --skill` for that client's copy. Setup
|
||||
resolves the final selection before deciding which files need installation.
|
||||
Doctor retains each selected stale or missing path and names the matching
|
||||
repair command; a current copy elsewhere does not establish freshness.
|
||||
|
||||
Unresolved profile or directory selections remain diagnostics and never become
|
||||
write targets. Update maintenance considers only known enabled, owned REA
|
||||
registrations and owned skills. If identity or a selected skill root is
|
||||
unresolved, it reports maintenance as unavailable with the known scope and
|
||||
remaining diagnostics. An empty writable scope alone does not prove current.
|
||||
|
||||
When configuration publication fails, setup retains the affected path, operation
|
||||
and available filesystem code in the client result. Owner/group and mode
|
||||
preservation failures identify that operation rather than implying that the
|
||||
configuration bytes or parent directory are unwritable. Setup preserves the
|
||||
original and reports its recovery backup; review local ownership and group
|
||||
membership before retrying.
|
||||
|
||||
For an installed POSIX CLI, setup prefers an executable absolute `rea` alias
|
||||
from its selected `PATH` when filesystem identity proves that it names the
|
||||
running REA entrypoint. This keeps package-manager aliases intact instead of
|
||||
persisting an internal versioned script path. Unresolved aliases and wrappers
|
||||
that are different files do not establish that proof; setup retains its selected
|
||||
entrypoint. A manager-specific upgrade remains subject to that manager's alias
|
||||
policy.
|
||||
@@ -149,6 +149,12 @@ REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm r
|
||||
REA_VERIFY_COPILOT_MODEL=gpt-4.1 REA_VERIFY_COPILOT_SCHEMA_PROFILE=compact npm run verify:copilot-client -- call
|
||||
```
|
||||
|
||||
The verifier explicitly writes either selected profile into its disposable
|
||||
registration and compares the input schemas forwarded by Copilot with that
|
||||
profile's advertised schemas. A run must not infer `full` from an unset setting:
|
||||
REA's runtime default is now `compact`. The recorded client results above remain
|
||||
scoped to their executed revisions; this verifier correction is not a new client run.
|
||||
|
||||
Both native workflows passed with the complete tool inventory. The verifier selects the
|
||||
profile only in its disposable Copilot registration and records it in the
|
||||
receipt. Compact advertisements can reduce nested validation structure; the
|
||||
@@ -452,6 +458,11 @@ real local Codex CLI with:
|
||||
npm run verify:agent
|
||||
```
|
||||
|
||||
The verifier's scenarios, transcript scoring, fixture rubrics, and report gates
|
||||
are development verification code under `tests/evaluation/model-evals/`. The
|
||||
source-owned evaluator runs alongside the compiled REA tools; it is excluded
|
||||
from the shipped runtime package.
|
||||
|
||||
The lane runs scenarios sequentially in standalone Codex (`--no-daemon`) with a
|
||||
disposable HOME and CODEX_HOME under `~/.cache/rea-agent-evaluations/`. Set
|
||||
`REA_AGENT_EVAL_ROOT` to choose another parent directory outside the OS temporary
|
||||
@@ -475,10 +486,16 @@ is removed after confirmed process cleanup even with
|
||||
`REA_AGENT_EVAL_KEEP_FIXTURES=true`. No user configuration,
|
||||
rules, plugins or history is copied; shell snapshots and additional agents are
|
||||
disabled. Existing API-key environment authentication remains available.
|
||||
The native missing-provider scenario preapproves only `open_binary` and
|
||||
The native provider scenario preapproves only `open_binary` and
|
||||
`close_binary` in the disposable client so it reaches REA's actual provider
|
||||
boundary instead of stopping at Codex approval. This does not validate a real
|
||||
native engine or authorize arbitrary runtime execution.
|
||||
boundary instead of stopping at Codex approval. Its scenario contract accepts
|
||||
one `provider_unavailable` open refusal without a close, or one successful open
|
||||
followed by a successful close. Other errors, an unclosed session, and a close
|
||||
without acquisition fail. These outcomes and the prompt are owned together in
|
||||
`tests/evaluation/model-evals/AgentEvaluationScenarios.ts`; generic successful-tool sequences
|
||||
still apply to scenarios that require them. The provider scenario checks routing,
|
||||
reported limitations, and cleanup; free-form answer correctness remains
|
||||
unassessed. It does not validate a real native engine or authorize runtime execution.
|
||||
|
||||
Select a smaller run with `REA_AGENT_EVAL_SCENARIOS`, a comma-separated list of
|
||||
scenario IDs. `asar-zh` exercises the packaged desktop rubric from a Chinese
|
||||
@@ -491,8 +508,10 @@ limit for the whole process tree when testing on a constrained host; a Node
|
||||
heap limit alone does not bound client and child-process memory.
|
||||
|
||||
`missing-target` leaves desktop fixtures visible without selecting one in the
|
||||
request. It requires zero REA calls and a structured request for an app name or
|
||||
artifact path. Its `expectedFirstTool` is `null`, and
|
||||
request. It requires zero REA calls and a JSON-only request for an app name or
|
||||
artifact path, with no preceding prose or Markdown fences. Its prompt and
|
||||
whole-answer validator are defined together in `AgentEvaluationScenarios.ts`.
|
||||
Its `expectedFirstTool` is `null`, and
|
||||
`targetClarificationPassed` grades that limited routing outcome independently
|
||||
of analysis prose heuristics. Analyzing a nearby example cannot pass this case.
|
||||
|
||||
@@ -554,7 +573,10 @@ tool results and final answers for review.
|
||||
Report schema version 4 adds negative routing scenarios with nullable
|
||||
`expectedFirstTool` and a `targetClarificationPassed` outcome. Consumers must
|
||||
allow an expected absence of REA calls; this does not make an analysis scenario
|
||||
pass without its required tools. Version 3 changed the top-level
|
||||
pass without its required tools. Native provider records additionally report
|
||||
`scenarioOutcome` (`provider-unavailable`, `opened-and-closed`, or `failed`) and
|
||||
`scenarioOutcomePassed`; a failed outcome fails the gate independently of prose
|
||||
heuristics. Version 3 changed the top-level
|
||||
`factualCorrectness` from a constant
|
||||
string to an assessment summary with status, scope, and assessed/passed/failed/
|
||||
not-assessed scenario counts. Scenario records include the factual assessment and
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# Admission limits and recovery
|
||||
|
||||
How to add a REA-owned admission ceiling, budget, or deadline, and what a caller
|
||||
does when one is hit. This is a design rule, not an inventory. Ceilings that
|
||||
should become settings are tracked in
|
||||
[#6176](https://github.com/morluto/rea/issues/6176). Naming honesty for limit
|
||||
errors is [#5910](https://github.com/morluto/rea/issues/5910).
|
||||
|
||||
## Name a default, then a setting when operators need headroom
|
||||
|
||||
Put the number in a named constant with a unit. Do not leave it as a magic
|
||||
number at the call site.
|
||||
|
||||
When a workload may need more room, add a setting whose unset value is that
|
||||
constant. One module under
|
||||
[`src/config/`](https://github.com/morluto/rea/blob/main/src/config) owns the
|
||||
default, any floor, the maximum, the schema, the parser, and the recovery
|
||||
text. Register that schema on `environmentSchema` in
|
||||
[`environment.ts`](https://github.com/morluto/rea/blob/main/src/config/environment.ts).
|
||||
`parseConfig` calls `parseEnvironment`. Doctor uses that same parser through
|
||||
`nativeAnalysisConfigurationError`, so startup and `rea doctor` accept the
|
||||
same values. Add the name to `SELECTED_REGISTRATION_SETTINGS` so `rea setup`
|
||||
copies it into the client registration. Unset keeps today's behavior. An
|
||||
invalid value is a `ConfigurationError` and projects as `configuration_invalid`.
|
||||
Do not fall back to the default.
|
||||
|
||||
[`REA_REGEX_MATCHING_DEADLINE_MS`](https://github.com/morluto/rea/blob/main/src/config/regexMatchingDeadline.ts)
|
||||
([#6267](https://github.com/morluto/rea/pull/6267)) is the reference. Default
|
||||
5000 ms, floor 1 ms, maximum 2147483647 ms, because Node schedules a longer
|
||||
`setTimeout` delay as 1 ms. Setup copies the setting. A hit says:
|
||||
|
||||
> The effective REA_REGEX_MATCHING_DEADLINE_MS value is 5000 ms (source default; default 5000 ms, floor 1 ms, maximum 2147483647 ms). Set REA_REGEX_MATCHING_DEADLINE_MS in the environment that starts REA to a positive decimal integer from 1 to 2147483647, then restart REA. The worker old-generation heap does not raise this deadline.
|
||||
|
||||
[`REA_MCP_MAX_RESPONSE_BYTES`](https://github.com/morluto/rea/blob/main/src/config/mcpResponseBudget.ts)
|
||||
is the byte-budget shape: a decimal safe integer with a 10 MiB floor
|
||||
(`MINIMUM_RESPONSE_BYTES`), the same shared schema, and no silent fallback. A
|
||||
change needs a REA restart and a matching client receive buffer. It does not
|
||||
raise REA's reserved string limit or Node's string ceiling. The setup
|
||||
allowlist does not copy this setting. A new headroom setting should, following
|
||||
the regex deadline.
|
||||
|
||||
A per-call budget is not an environment setting. JavaScript `max_heap_mb`
|
||||
(CLI `--max-heap-mb`) is an integer from 128 to 16384 MiB, default 1024. It
|
||||
applies on the next analysis, which starts a new worker, and does not restart
|
||||
REA. It does not raise the parent result budget, transfer ceilings, or fixed
|
||||
parser limits.
|
||||
|
||||
Host memory and `NODE_OPTIONS` do not raise a REA admission unless that
|
||||
admission's recovery says so. The JavaScript parent result budget is one that
|
||||
does: its recovery says to set `NODE_OPTIONS` (for example
|
||||
`--max-old-space-size=<MiB>`) on the process that starts REA, then restart
|
||||
that process. `max_heap_mb` does not raise it.
|
||||
|
||||
## Keep a fixed ceiling honest
|
||||
|
||||
If the ceiling must stay fixed, say so in the failure. Name the limit and the
|
||||
effective value, say this release has no runtime override, and name what does
|
||||
not raise it. Do not invent a setting.
|
||||
|
||||
JSON values stop at `MAX_JSON_DEPTH` (1000) in
|
||||
[`jsonValue.ts`](https://github.com/morluto/rea/blob/main/src/domain/jsonValue.ts):
|
||||
"JSON value exceeds maximum nesting depth of 1000. This release has no runtime
|
||||
override."
|
||||
|
||||
## Recovery text
|
||||
|
||||
The string the caller sees includes:
|
||||
|
||||
- the limit or setting name
|
||||
- the effective value and unit
|
||||
- the source (`default` or `setting`)
|
||||
- the default, the maximum, and the floor when the setting has one
|
||||
- the next action, including where to set it ("in the environment that starts REA")
|
||||
- whether a restart is required
|
||||
- what the change does not raise
|
||||
|
||||
Invalid settings never take this path. They are `configuration_invalid`.
|
||||
Doctor reports `runtime-configuration:<setting>` and says to correct it in the
|
||||
shell or the MCP registration, then rerun.
|
||||
|
||||
## What agents do
|
||||
|
||||
Follow `remediation.action`. Change a request field or a `REA_*` setting only
|
||||
when that text recommends the change. Restart only when it says the value is
|
||||
read at startup. A sentence that a control does not raise the limit is not a
|
||||
request to change it.
|
||||
|
||||
- Dropped MCP connection: retry the call once only when it is safe to repeat
|
||||
or known not to have started. If it may already have changed the target, or
|
||||
it fails again, tell the user and ask them to restart the MCP client. Do not
|
||||
start with `rea doctor`
|
||||
([#6353](https://github.com/morluto/rea/pull/6353)).
|
||||
- REA could not start its connection: retry once. If it fails again, tell the
|
||||
user and ask them to restart the MCP client, then run `rea doctor`. Do not
|
||||
retry a startup that returns the same unchanged result: tell the user REA
|
||||
could not start, ask them to restart the MCP client, then run `rea doctor`.
|
||||
- Unexpected process-command failure: do not repeat the command. Tell the user
|
||||
to check the input files, then run `rea doctor`.
|
||||
- `cleanup_incomplete`: the operation is not retryable. Retry that exact
|
||||
cleanup owner, named in `details.resources`, and wait until it confirms
|
||||
release before admitting new work on those resources
|
||||
([#6357](https://github.com/morluto/rea/pull/6357)).
|
||||
- `resource_constraint` for capacity, when `retryable` is true: wait for the
|
||||
active admission to finish, then retry within the reported limits. Do not
|
||||
restart REA to free the slot.
|
||||
- `configuration_invalid`: correct the named setting in the environment that
|
||||
starts REA, then start REA again. Run `rea doctor` for the
|
||||
`runtime-configuration:` check. Re-run `rea setup` for that client when the
|
||||
setting is on the registration allowlist, then reconnect so the new process
|
||||
sees it.
|
||||
- `invalid_request`: correct the reported fields from the current schema or
|
||||
CLI help. Do not repeat the unchanged call.
|
||||
|
||||
A restart is required for an environment setting and for a client still
|
||||
running the previous registration. It is not the first step for a transient
|
||||
MCP drop, a capacity wait, or retained cleanup.
|
||||
|
||||
## Tests
|
||||
|
||||
Lock the unset default, one accepted override, rejected floor, maximum, and
|
||||
syntax, and the recovery sentence through `parseEnvironment` and `parseConfig`.
|
||||
[`regexMatchingDeadline.test.ts`](https://github.com/morluto/rea/blob/main/src/config/regexMatchingDeadline.test.ts)
|
||||
is the pattern. Project the failure and assert `configuration_invalid` for a
|
||||
bad value and the recovery wording for a hit.
|
||||
@@ -97,17 +97,18 @@ same bundle. Export writes only the caller-selected output file.
|
||||
| Unity Mono | A managed assembly below `*_Data/Managed` or `Contents/Resources/Data/Managed` has an exact UnityEngine assembly reference. |
|
||||
| Unity IL2CPP | A recognized IL2CPP metadata magic (with the raw version when complete) coexists with a conventionally named native `GameAssembly.dll`, `GameAssembly.so`, `GameAssembly.dylib` or `libil2cpp.so` image. Build correspondence remains unknown. |
|
||||
| Godot | A `GDPC` header at file start or a validated executable trailer offset. |
|
||||
| GameMaker | A bounded `FORM` container starts with a `GEN8` chunk. |
|
||||
| GameMaker | A bounded `FORM` container starts with a `GEN8` chunk. The scan probes conventional `data.win`, `game.unx`, and `game.ios` data files for that header. |
|
||||
| Unreal | `Content/Paks` path conventions or an IoStore TOC signature; arbitrary `.pak` filenames are insufficient. |
|
||||
| Electron | A `resources/app.asar` or `resources/default_app.asar` path convention; archive validity is unverified. |
|
||||
|
||||
When the four-byte metadata magic is present but version bytes are incomplete,
|
||||
the fact retains `raw_version: null`; it does not offer an IL2CPP metadata
|
||||
inspection probe. A complete magic/version prefix with supported raw version 29,
|
||||
31 or 39 offers an
|
||||
`inspect_il2cpp_metadata` follow-up at its admitted file path, including when no
|
||||
native library was found. This probe does not establish full metadata validity
|
||||
or correspondence to a native binary. See [IL2CPP metadata](il2cpp-metadata.md).
|
||||
inspection probe. A complete magic/version prefix with supported raw version
|
||||
24, 27, 29, 31 or 39 offers an `inspect_il2cpp_metadata` follow-up at its
|
||||
admitted file path, including when no native library was found. This probe does
|
||||
not establish full metadata validity or correspondence to a native binary;
|
||||
version 24 inspection still applies only to supported header layouts. See
|
||||
[IL2CPP metadata](il2cpp-metadata.md).
|
||||
|
||||
Candidates are derived or inferred from these facts and do not prove loading or
|
||||
execution. BepInEx, MelonLoader, Easy Anti-Cheat and BattlEye labels identify
|
||||
|
||||
@@ -29,15 +29,20 @@ files, including pages a packaged application serves from inside `app.asar`.
|
||||
For those, the first `.asar` path component that is a regular file is the
|
||||
archive, the remainder must name a regular file in its header, and the reported
|
||||
path is the canonical archive path joined with that member. Remote hosts,
|
||||
encoded path separators, ASAR directories or links, and nonexistent paths are
|
||||
encoded separators in the pathname, ASAR directories or links, and nonexistent paths are
|
||||
rejected. The endpoint exposes every eligible target and its local path
|
||||
metadata.
|
||||
metadata. Query strings and fragments do not participate in filesystem path
|
||||
admission, including their encoded separator text. Listing and inspection
|
||||
Evidence both retain the observed browser product/revision; host platform and
|
||||
architecture remain explicitly unknown.
|
||||
|
||||
Cancellation stops further file authorization after an already-started filesystem
|
||||
operation settles. ASAR descriptor-close failures remain typed incomplete cleanup
|
||||
with an exact retryable owner; they are not treated as a missing or unauthorized
|
||||
page. A later authorization and provider shutdown retry retained descriptors
|
||||
before reporting their release.
|
||||
before reporting their release. Failed listing retains its already-authorized
|
||||
target prefix and explicit remaining-target uncertainty through cleanup errors
|
||||
and exact-owner retries.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -70,10 +75,40 @@ DOM node indices retain their snapshot positions. Invalid rows are omitted with
|
||||
completeness diagnostics, so `total_nodes` can exceed the retained node count
|
||||
and a parent index can refer to an omitted source node.
|
||||
|
||||
Script metadata retains its execution-context frame ID when CDP supplies one. The capture also
|
||||
Script metadata retains its execution-context frame ID when CDP supplies one and
|
||||
records admission separately in `frame_scope`. An `authorized` scope refers to
|
||||
the observed document generation; `unknown` retains the known frame ID without
|
||||
claiming authorization and makes script coverage attach-limited. Historical
|
||||
captures may omit this field, which leaves that admission fact unknown.
|
||||
Scripts from document generations whose frames were explicitly excluded by
|
||||
local-file policy are omitted before script-source reads. Frame identity can
|
||||
come from the script event's own auxiliary data or its observed execution
|
||||
context; contradictory identities are not aligned.
|
||||
`frame_claims.script_event_frame_id` and
|
||||
`frame_claims.execution_context_frame_id` preserve the actual observed claims
|
||||
from those two sources. Both remain available when their disagreement leaves
|
||||
the resolved `frame_id` null and `frame_scope` unknown. An omitted claim is
|
||||
unobserved, not proof that a frame was absent.
|
||||
Frames describe the current inventory; script observations can retain earlier
|
||||
authorized frames that have since detached.
|
||||
|
||||
`has_source_url` preserves CDP's sourceURL declaration flag as true, false, or
|
||||
unknown (`null`, or omitted in historical captures). A script's canonical
|
||||
`file_path` is the admitted local-file URL or supplied sourceURL label. It does
|
||||
not verify that the runtime bytes came from that disk file; captured source
|
||||
bytes have their own digest. `reported_url` retains the original URL or label
|
||||
before canonical path admission. Labels remain distinct from filesystem provenance.
|
||||
The capture also
|
||||
inventories worker, service-worker, and shared-worker targets with
|
||||
validated opener-target and parent-frame IDs. Worker discovery uses passive
|
||||
target metadata; REA does not attach to or execute code in those targets.
|
||||
Script metadata uses the shared 8 MiB JSON-byte allowance for each retained
|
||||
representation: incoming drafts and normalized capture metadata. Admission
|
||||
counts escaped strings, frame claims, and provenance fields before retaining
|
||||
them or serializing metadata for a digest. Exhaustion preserves earlier records
|
||||
and reports counted `resource_budget_exhausted` script omissions; a later
|
||||
smaller record may still fit. This allowance adds no script-count cap and is
|
||||
separate from script-source bytes.
|
||||
Script-source collection retains at most 32 MiB of UTF-8 text across scripts
|
||||
and shares a 20-second collection deadline. Omitted sources retain their script
|
||||
metadata and an explicit reason; later smaller sources may still fit after an
|
||||
@@ -84,6 +119,11 @@ target, frame, script, and resource, a worker URL must resolve to a local
|
||||
file before it is retained. Relationship IDs improve
|
||||
attribution but do not prove which static module started a worker or that its
|
||||
work completed.
|
||||
Missing opener and parent relationships leave worker ownership unknown. Such
|
||||
targets are withheld with an `unattributed_target` exclusion and unavailable
|
||||
`workers` coverage, even when their URL names a local file. Explicit foreign
|
||||
relationships remain `out_of_target_scope`; malformed supplied relationship IDs
|
||||
are `invalid_protocol_value`.
|
||||
|
||||
Inspection does not retain DOM values, execute renderer code, navigate, click,
|
||||
invoke Electron IPC, close a target, or terminate the application.
|
||||
@@ -100,6 +140,8 @@ window-targeted renderer reload/crash actions, and synthetic `open-url` or
|
||||
WebContents identities, process metrics, and IPC channel and value-shape metadata.
|
||||
Payload values are not retained.
|
||||
|
||||
`window_index` selects from Playwright's current Page list at the start of each action. REA resolves that Page to its corresponding BrowserWindow once; the action's `target` uses the BrowserWindow's capture-scoped `window_id`, matching the window inventory. Reordering either enumeration after selection does not retarget the action. Handle cleanup failures retain the completed action records and remain owned for provider cleanup retry.
|
||||
|
||||
Run the real fixture verifier with an operator-provided Electron runtime:
|
||||
|
||||
```bash
|
||||
@@ -124,8 +166,10 @@ native-addon, process, and IPC timeline events. IPC channels and argument-shape
|
||||
metadata retain their observed lengths; payload values are never retained.
|
||||
Renderer crash/restart and deep-link actions are synthetic
|
||||
scenario controls; their attempted and observed outcomes remain in the
|
||||
timeline. The active hook blocks and records external shell/navigation,
|
||||
permission, download, popup, updater, and OS-integration effects. The timeline
|
||||
timeline. The active hook blocks and records the shell methods listed below
|
||||
and selected navigation, permission, download, popup, updater, and
|
||||
OS-integration APIs. These method patches do not contain direct filesystem,
|
||||
network, native-addon, or other unpatched target effects. The timeline
|
||||
is explicitly partial when attachment starts after application activity. The
|
||||
startup, including the initial state snapshot, has a 60-second deadline.
|
||||
Actions wait until completion or request cancellation; a click whose target
|
||||
@@ -134,11 +178,14 @@ window, remains pending until cancelled. After cancellation, the final state
|
||||
read has 5 seconds before REA keeps the initial snapshot instead; the
|
||||
cancellation keeps the caller's reason and records that expiry in its
|
||||
diagnostics.
|
||||
Blocked synchronous shell APIs keep their return contracts:
|
||||
The shell policy is owned by `blockedShellResults` in
|
||||
`scripts/electron-active-hook-boundaries.cjs`. Its synchronous blocks are:
|
||||
`showItemInFolder` returns `undefined` and `writeShortcutLink` returns `false`.
|
||||
Their `shell-attempt` events record the blocked phase and argument shapes;
|
||||
neither result means the requested host operation occurred. The existing
|
||||
`openExternal` and `openPath` blocks continue to reject their promises.
|
||||
`openExternal`, `openPath`, and `trashItem` blocks reject their promises.
|
||||
The `trashItem` block prevents that call from reaching the OS trash operation;
|
||||
it does not intercept other ways the application can remove files.
|
||||
CLI SIGINT/SIGTERM requests cancellation and awaits owned-process cleanup.
|
||||
Window IDs, URLs, and cached titles come from one main-process Electron snapshot,
|
||||
so a crashed renderer does not require a successful renderer-side title query.
|
||||
@@ -152,6 +199,15 @@ If cleanup cannot be verified, the error retains collected observations in
|
||||
`details.partial_observation` and marks their application cleanup `unverified`.
|
||||
Cleanup uncertainty does not authorize signaling unrelated processes.
|
||||
|
||||
On Linux, Chromium-based processes can overwrite the initial environment memory
|
||||
exposed through `/proc/<pid>/environ`. The environment may be readable while
|
||||
`REA_PROCESS_RUN_ID` is absent, even if the application still sees it through
|
||||
`process.env`. REA reports the missing marker and leaves lineage ownership and
|
||||
cleanup unverified; the PID, executable, or parent relationship cannot replace
|
||||
that ownership proof. This limitation can affect owned Electron captures on
|
||||
native Linux as well as WSL. It does not establish that all Electron versions or
|
||||
Linux environments behave the same way.
|
||||
|
||||
Main-process IPC recording keeps original-function removal for `on` and `once`
|
||||
registrations and records a fired `once` callback once. Native Node.js owns
|
||||
`once` registration, firing and callable dispatch as well as removal order and
|
||||
@@ -218,7 +274,9 @@ initialization, the hook leaves listener callbacks unwrapped and records a
|
||||
callback and receiver; callback execution remains unobserved. This comparison
|
||||
assumes Node's EventEmitter prototype has not itself been globally decorated
|
||||
before hook initialization. The provider loads the hook before the application
|
||||
and excludes `NODE_OPTIONS` from the child environment.
|
||||
and excludes `NODE_OPTIONS` from the child environment. It preserves selected
|
||||
`DISPLAY`, `XAUTHORITY` and `WAYLAND_DISPLAY` values, including the caller-selected
|
||||
X11 authorization-file path.
|
||||
|
||||
An opaque later listener or unsupported emitter state can prevent proof of
|
||||
removal order. The hook then delegates removal
|
||||
|
||||
+23
-5
@@ -24,8 +24,11 @@ requires an absolute filesystem path and preserves that exact selection:
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
`tables` lists each declared note program header or section, including the
|
||||
section name when a string table is present. Each `entries[]` item reports owner,
|
||||
type, descriptor SHA-256, and a hex preview for descriptors of 64 bytes or less.
|
||||
section name when a string table is present. Zlib-compressed section-name
|
||||
tables are decompressed within the selected file-size budget. An unsupported
|
||||
compression type leaves section names unknown and adds a limitation; note bytes
|
||||
remain independently examined. Each `entries[]` item reports owner, type,
|
||||
descriptor SHA-256, and a hex preview for descriptors of 64 bytes or less.
|
||||
|
||||
For GNU notes, `gnu.build_id_hex`, `gnu.abi_tag`, or `gnu.gold_version` are
|
||||
filled when the descriptor matches that type. Other owners keep `gnu` null.
|
||||
@@ -40,14 +43,29 @@ the image never carried notes in another container or overlay.
|
||||
|
||||
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
|
||||
32 note entries are examined by default across every table. Retained note
|
||||
names, section names, and GNU descriptor text share that file's byte budget,
|
||||
and repeated table ranges count again. Each descriptor range is hashed once,
|
||||
names, section names, and GNU descriptor text share a scan budget based on
|
||||
the source bytes plus any decoded section-name table, capped at
|
||||
`max_file_bytes`. The raw source and decoded table must also fit together
|
||||
within that ceiling. If admission fails, the error reports the required
|
||||
`max_file_bytes`; raising it can admit the decoded table up to the operation's
|
||||
512 MiB maximum. Once admitted, raising that setting does not increase the
|
||||
scan budget for the same source and decoded names. Repeated table ranges count again. Each descriptor range is hashed once,
|
||||
and each GNU version's trailing NUL run is scanned once; neither walk may
|
||||
exceed that byte ceiling. Malformed ELF headers, truncated header
|
||||
tables, declared note tables that extend past the file, and truncated
|
||||
note entries fail without partial success. ELF extended
|
||||
numbering (`PN_XNUM`, including a section count stored when `e_shnum` is 0) is
|
||||
unsupported.
|
||||
unsupported and returns an unsupported-target outcome rather than invalid-input
|
||||
recovery.
|
||||
|
||||
`SHF_COMPRESSED` note sections return `unsupported_target` before their payload
|
||||
is interpreted or decompressed. Their compression header is storage metadata,
|
||||
not a note header. The current result requires physical file offsets for each
|
||||
note and descriptor, which decoded compressed notes cannot provide. This refusal
|
||||
does not label compressed note storage as malformed or allocate its declared
|
||||
decoded size. Compressed section-name tables remain supported within the limits
|
||||
above. Inspect a separately decompressed copy when these notes are needed,
|
||||
retaining the original artifact for provenance.
|
||||
|
||||
This workflow does not execute the image or verify GNU build-id uniqueness
|
||||
against package catalogs.
|
||||
@@ -0,0 +1,126 @@
|
||||
# Erlang BEAM files
|
||||
|
||||
`inspect_erlang_beam` / `inspect-erlang-beam` answers whether one selected
|
||||
local file is a `FOR1`/`BEAM` IFF container, which chunks that form stores, and
|
||||
which atom, export, import, lambda, and Code-header fields those chunks carry.
|
||||
It reads the file without executing bytecode and does not require an Erlang
|
||||
installation or BEAM emulator.
|
||||
|
||||
```bash
|
||||
rea inspect-erlang-beam /absolute/path/to/app.beam --json
|
||||
```
|
||||
|
||||
The CLI resolves relative paths against the caller's working directory. MCP
|
||||
requires an absolute filesystem path and preserves that exact selection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_erlang_beam",
|
||||
"arguments": { "path": "/artifacts/app.beam" }
|
||||
}
|
||||
```
|
||||
|
||||
## Observations
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
`chunks` lists every IFF chunk name, file range, and payload SHA-256.
|
||||
Repeated names retain separate chunk occurrences. Decoded tables and the Code
|
||||
header use the last occurrence of each name, matching OTP's loader; the chunk
|
||||
inventory still exposes earlier payloads and their source ranges.
|
||||
The BEAM form size bounds the parsed chunks. Bytes after the declared form are
|
||||
ignored by the parser but remain part of the selected artifact's byte length
|
||||
and SHA-256.
|
||||
|
||||
When a selected `AtU8` or `Atom` table is decoded, `atoms` is the 1-based name table and
|
||||
`module` is atom 1. Both the pre-OTP 28 table and the OTP 28+ compact table
|
||||
(negated count, compact-encoded name lengths) are read. `exports`, `imports`, and `lambdas` resolve atom indices to
|
||||
those names when the table is present; unresolved indices keep `function` or
|
||||
`module` null.
|
||||
|
||||
`code` reports the Code chunk instruction-set, opcode maximum, label count,
|
||||
function count, and the instruction stream's file range. Missing optional
|
||||
tables are null, not empty-everywhere.
|
||||
|
||||
A zero-byte `AtU8` falls back to an available legacy `Atom` table, following
|
||||
[OTP 20.3's atom selection](https://github.com/erlang/otp/blob/a113f6117fd696ea6f84ed754055b4ec97a7ccb2/erts/emulator/beam/beam_load.c).
|
||||
A nonempty `AtU8` retains UTF-8 precedence, including an explicit zero atom
|
||||
count; malformed nonempty data fails instead of falling back. An empty atom
|
||||
chunk without an alternate table still fails its required count header.
|
||||
Absent and zero-byte optional `FunT` chunks produce `lambdas: null`, while a
|
||||
four-byte zero row count produces `lambdas: []`. This follows
|
||||
[OTP 29's positive-size lambda parsing rule](https://github.com/erlang/otp/blob/550d7b7898706c7822362c42e7b93120c1d1f29a/erts/emulator/beam/beam_file.c).
|
||||
Empty payloads retain their ranges and SHA-256 in the chunk inventory. These
|
||||
rules apply to the last occurrence of each chunk name; an empty final occurrence
|
||||
does not revive an earlier payload of that name. Nonempty truncated tables remain
|
||||
format failures.
|
||||
|
||||
## Select retained facts
|
||||
|
||||
Complete output remains the default. Request a summary with a `beam` view to
|
||||
read a few facts in the first call while retaining the complete inspection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_erlang_beam",
|
||||
"arguments": {
|
||||
"path": "/artifacts/app.beam",
|
||||
"detail": "summary",
|
||||
"view": {
|
||||
"kind": "beam",
|
||||
"collection": "exports",
|
||||
"names": ["module_info"],
|
||||
"fields": ["function_index", "function", "arity", "label"],
|
||||
"limit": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`normalized_result.page.items` contains the matching rows. `source_index` is
|
||||
zero-based in the original collection and scoped to `parent_evidence_id` and
|
||||
that collection. It does not replace the original one-based atom `index` or a
|
||||
lambda's own `index`. Every matching function name survives, including different
|
||||
arities. Names are exact retained display strings; raw indexes remain available
|
||||
when names are unresolved or displayed names collide.
|
||||
|
||||
Collections are `atoms`, `exports`, `imports`, `lambdas`, and `chunks`. `names`
|
||||
selects atom/chunk names or function names; imports additionally support exact
|
||||
`modules`. Filters intersect. `indices` selects original collection indexes;
|
||||
`fields` selects that collection's own fields. Empty selector arrays match
|
||||
nothing, and `fields: []` returns only `source_index`. Offset/limit page matching
|
||||
rows in original order (defaults 0/64). Unfiltered pages slice the retained
|
||||
collection; queries do not decode the file again.
|
||||
|
||||
`beam` keeps the module, form/Code bounds, original source coverage, and table
|
||||
counts. A null count means no table was decoded; zero means a decoded empty
|
||||
table. Missing tables and unresolved atom names remain explicit, and an empty
|
||||
page does not establish runtime absence. Page coverage is separate from the
|
||||
source's examined chunks and parser budgets.
|
||||
|
||||
CLI summary delivery requires a destination for the complete Evidence bundle:
|
||||
|
||||
```sh
|
||||
rea inspect-erlang-beam /artifacts/app.beam --detail summary \
|
||||
--view '{"kind":"beam","collection":"exports","names":["module_info"],"fields":["function","arity"]}' \
|
||||
--evidence_output /reports/app-beam.evidence.json --json
|
||||
rea inspect-analysis-view '{"source":{"kind":"evidence-file","path":"/reports/app-beam.evidence.json"},"view":{"kind":"beam","collection":"imports","modules":["erlang"]}}' --json
|
||||
```
|
||||
|
||||
The bundle retains the complete report. Subsequent MCP queries may use
|
||||
`source: {kind: "retained-evidence", evidence_id: parent_evidence_id}` or an
|
||||
Evidence file. Retained and offline views still work after the original BEAM
|
||||
file is deleted. Supplying `view` requires `detail: "summary"`; omitting it
|
||||
returns only the structural summary. Selection changes delivery, not parsing or
|
||||
coverage, and executes no bytecode.
|
||||
|
||||
## Limits and failures
|
||||
|
||||
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
|
||||
64 chunks and 4096 atoms are examined by default, and at most 65536 export,
|
||||
import, and lambda rows together. Malformed `FOR1`/`BEAM` headers, truncated
|
||||
chunks, and `AtU8` names that are not valid UTF-8 fail without partial success.
|
||||
|
||||
This workflow does not execute bytecode, inflate `LitT` literals, or decode
|
||||
`Attr`/`CInf` external-term payloads. Complete coverage describes this static
|
||||
chunk and table report; it does not establish that an Erlang runtime can load
|
||||
or execute the module.
|
||||
+16
-7
@@ -19,8 +19,10 @@ rea inspect-evm-interface ./runtime.bin raw --json
|
||||
|
||||
Initial real verification covers flat legacy bytecode on Linux x64 and unchanged
|
||||
bundled EVMole 0.9.3. EOF-style EF00 containers return `unsupported_target`,
|
||||
with the selected path and format reason. Provider/configuration failures retain
|
||||
their distinct unavailable-provider or capability classification.
|
||||
with the selected path and format reason. Selected-path resolution errors
|
||||
`ENOENT`, `ENOTDIR`, `ELOOP`, and `ENAMETOOLONG` are `invalid_request` path
|
||||
issues. Permission, resource, cancellation, provider, and cleanup failures retain
|
||||
their distinct classifications.
|
||||
The host must already provide util-linux `prlimit`, normally `/usr/bin/prlimit`;
|
||||
`REA_EVM_PRLIMIT_COMMAND` can select another absolute executable. REA installs
|
||||
no system tools and changes no user configuration. [Upstream provenance and
|
||||
@@ -43,7 +45,8 @@ license](https://github.com/morluto/rea/blob/main/third_party/evmole/README.md)
|
||||
map from the selected bytes. Integers use decimal strings; byte strings use hex,
|
||||
and scalar keys retain their types. The recorded offset/length addresses the
|
||||
original bytecode, so summary delivery does not copy an opaque CBOR payload.
|
||||
Nested containers have partial scalar coverage; unsupported or malformed
|
||||
Nested containers have partial scalar coverage; map keys are still checked for
|
||||
duplicates throughout nested maps and arrays. Unsupported or malformed
|
||||
candidates report `not-decoded` with a diagnostic. Decoding is bounded to 64
|
||||
container levels and the terminal payload's encoded size.
|
||||
- An unavailable trailer does not prove metadata absence or Solidity origin.
|
||||
@@ -105,13 +108,19 @@ CLI keeps its existing complete result.
|
||||
## Resource and lifecycle limits
|
||||
|
||||
Inputs are limited to 4 MiB, complete replies to 16 MiB and combined diagnostic
|
||||
output to 1 MiB. An owned worker has a 256 MiB JavaScript heap, 3 GiB virtual
|
||||
address-space limit and 30 CPU seconds. The worker command deadline is
|
||||
output to 1 MiB. An owned worker has a 256 MiB JavaScript heap; its profile
|
||||
maxima are 3 GiB virtual address space and 30 CPU seconds, with tighter inherited
|
||||
limits retained. The worker command deadline is
|
||||
`timeout_ms` (default 30000 ms, integer 1 through 2147483647) on the next call,
|
||||
or `--timeout-ms` on `rea inspect-evm-interface`. Omission keeps 30000 ms.
|
||||
Those byte, address-space, and CPU ceilings have no runtime override, and
|
||||
`timeout_ms` does not raise them. Virtual
|
||||
address space differs from resident memory. The worker retains inherited tighter
|
||||
`timeout_ms` does not raise them. An oversized selected carrier returns a
|
||||
non-retryable `resource_constraint` with resource `file-size` and its fixed
|
||||
input allowance in `reported_limits.maximum_input_bytes`.
|
||||
Complete-reply allowance refusals use the same non-retryable resource family,
|
||||
with `reported_limits.maximum_reply_bytes`; malformed JSON remains unreadable
|
||||
output. This refusal leaves no complete report to retrieve through another
|
||||
delivery channel. Virtual address space differs from resident memory. The worker retains inherited tighter
|
||||
soft/hard limits, reports its configured soft limits inline and leaves hard limits
|
||||
unchanged. WASM trap reservations are disabled
|
||||
so the engine can run within that virtual limit; actual memory is not equated to
|
||||
|
||||
@@ -27,7 +27,10 @@ checks the tool's `--version` banner when executing a request. Binwalk must
|
||||
print `binwalk <version>` and Unblob must print the version alone. Inspection and
|
||||
extraction have independent prerequisites; one does not require the other.
|
||||
Both require util-linux `prlimit`, normally `/usr/bin/prlimit`; override its
|
||||
absolute path with `REA_FIRMWARE_PRLIMIT_COMMAND` if needed.
|
||||
absolute path with `REA_FIRMWARE_PRLIMIT_COMMAND` if needed. `rea doctor` does
|
||||
not currently inspect firmware readiness. Engine selection/version checks run
|
||||
when the firmware operation is requested; successful general doctor checks do
|
||||
not establish firmware tool or external-extractor availability.
|
||||
|
||||
REA retains at most 8 MiB of combined stdout and stderr per command. Exceeding
|
||||
that diagnostic budget stops analysis with an explicit truncated-output error;
|
||||
@@ -50,6 +53,36 @@ git submodule update --init third_party/binwalk third_party/unblob
|
||||
Normal REA builds and npm packages do not include or build these sources or
|
||||
engine binaries. See [upstream provenance](https://github.com/morluto/rea/blob/main/third_party/README.md).
|
||||
|
||||
### Windows submodule checkout
|
||||
|
||||
On Windows, the Unblob checkout stops with `error: invalid path`. Several
|
||||
upstream test fixture names under `tests/integration/` contain characters,
|
||||
trailing dots, or trailing spaces that NTFS does not allow; REA does not use
|
||||
those fixtures. Keep the clone, exclude that directory, and retry from the
|
||||
repository root in PowerShell:
|
||||
|
||||
```powershell
|
||||
git -C third_party/unblob config core.protectNTFS false
|
||||
git -C third_party/unblob sparse-checkout set --no-cone '/*' '!/tests/integration/'
|
||||
git submodule update third_party/unblob
|
||||
```
|
||||
|
||||
Both settings are required. While `core.protectNTFS` is enabled, Git for
|
||||
Windows also rejects index paths that the sparse pattern excludes; the pattern
|
||||
keeps the invalid names out of the worktree. The setting applies only to the
|
||||
Unblob submodule. In Git Bash, prefix the `sparse-checkout` command with
|
||||
`MSYS_NO_PATHCONV=1`; otherwise `!/tests/integration/` is rewritten as a
|
||||
Windows path.
|
||||
|
||||
The extraction destination must be absent with an existing writable parent.
|
||||
REA checks that selection before starting an engine and still acquires it
|
||||
exclusively during publication. Invalid sources and destinations identify the
|
||||
caller argument; failures inside an acquired workspace keep their own cause.
|
||||
Published file `relative_path` preserves producer spelling for lineage; `path`
|
||||
uses the writer's actual normalized filename. An extracted filename that cannot
|
||||
be represented losslessly as UTF-8 fails publication explicitly with its raw
|
||||
name bytes in hexadecimal instead of silently omitting the entry.
|
||||
|
||||
## CLI and MCP
|
||||
|
||||
```sh
|
||||
@@ -102,14 +135,19 @@ build's source revision, so that field remains unknown.
|
||||
selected input have a known `root_file_range`, adjusted by the selected offset.
|
||||
- Decompressed children have unknown original byte ranges and runtime addresses.
|
||||
Task lineage does not establish a byte-for-byte mapping through decompression.
|
||||
- `partial` marks unknown/encrypted chunks, reported extraction problems,
|
||||
- `partial` marks unknown/encrypted chunks, selected input bytes outside all
|
||||
reported chunk intervals, reported extraction problems,
|
||||
unpublished links/special files or reached depth limits. `complete` means the
|
||||
engine finished its configured extraction without those reported conditions;
|
||||
selected input is covered by reported chunk intervals and the engine finished
|
||||
its configured extraction without those reported conditions;
|
||||
default skip rules and format coverage still apply. It does not establish
|
||||
complete firmware reconstruction.
|
||||
- `extraction_gaps` makes those conditions recoverable: a row identifies an
|
||||
unextracted region or a failed task/stage. A chunk retains its identity and
|
||||
byte range, plus its original-file range when it belongs to the selected input.
|
||||
`unclassified-range` rows identify selected input intervals with no reported
|
||||
chunk; their provider identity and handler stay null. An empty chunk inventory
|
||||
therefore cannot establish complete extraction of a nonempty selected input.
|
||||
A multi-file stage retains its reported ID and handler with unknown byte ranges;
|
||||
a standalone task failure keeps unavailable stage identity null. Rows retain exact dependency names from
|
||||
the producer's missing-dependency reports; an accepted report with no names
|
||||
@@ -118,6 +156,11 @@ build's source revision, so that field remains unknown.
|
||||
share recovery prose without losing any region identity. Instructions use
|
||||
already configured capabilities where one exists. Every extraction rerun
|
||||
requires a fresh absent `output_directory`, preserving the prior evidence.
|
||||
Unblob's declared `ERROR` severity establishes an extraction failure,
|
||||
including standalone `UnknownError` task exceptions. Warnings and resolved
|
||||
extraction problems remain diagnostics with partial coverage; they do not
|
||||
establish failed extractor stages. Standalone errors do not acquire a nearby
|
||||
chunk's handler or byte range.
|
||||
Nested unknown chunks use `inspect_firmware_regions` on the actual published
|
||||
child rather than repeating the same extraction with unchanged handlers.
|
||||
Inspection requires separately configured Binwalk (`REA_BINWALK_COMMAND`);
|
||||
@@ -189,10 +232,15 @@ tree semantics, vulnerability scanning, emulation or hardware runtime capture.
|
||||
Diagnostic output is bounded with exact captured byte counts.
|
||||
- Default staging budget: **256 MiB** and **10,000 entries**, configurable using
|
||||
`max_output_bytes` / `max_output_files` (CLI `--max-output-bytes` /
|
||||
`--max-output-files`). Depth defaults to 3, allowed range 1–10.
|
||||
`--max-output-files`). Exceeding either selected staging budget returns a
|
||||
nonretryable resource constraint naming the selected limit. Depth defaults to
|
||||
3, allowed range 1–10.
|
||||
|
||||
Staging budgets are polled and rechecked after exit; short-lived overshoot is
|
||||
possible. The inherited file-size limit bounds each file, not total storage;
|
||||
possible. The per-file process limit is the larger of the selected payload budget
|
||||
and the fixed report/diagnostic file allowances, so a small payload selection
|
||||
still permits the mandatory report. A tighter inherited host limit still applies.
|
||||
The inherited file-size limit bounds each file, not total storage;
|
||||
address-space limits are per process, not an aggregate memory quota. Worker
|
||||
count is not CPU affinity for every external extractor. These limits do not
|
||||
claim cgroup/container containment. Cancellation cleans the owned process group
|
||||
|
||||
@@ -62,6 +62,12 @@ server; older servers keep the complete request after schema discovery.
|
||||
[Dart 2.14 layout](https://github.com/dart-lang/sdk/blob/2.14.0/runtime/vm/snapshot.h).
|
||||
Kind numbers vary across Dart releases; other values are preserved with
|
||||
partial coverage, without guessing their interpretation.
|
||||
Unified `_kDartSnapshotData` / `_kDartSnapshotText` exports are a different
|
||||
unsupported profile. The [upstream VM-isolate removal](https://github.com/dart-lang/sdk/commit/d36adbacafee01d9852cf8e960cf2d8654d91e7e)
|
||||
changes both the source layout and the FullAOT kind value. REA reports the
|
||||
observed export names and library identity with a typed unsupported-profile
|
||||
refusal when only unified exports are available; it does not alias one header
|
||||
into two independent sources.
|
||||
Address resolution examines every overlapping `PT_LOAD` interpretation,
|
||||
including partial overlaps. Equivalent mappings are coalesced; conflicting
|
||||
mappings leave affected symbols unresolved and qualify coverage. Dynamic
|
||||
@@ -94,6 +100,8 @@ server; older servers keep the complete request after schema discovery.
|
||||
operation, and when they **disagree** the distinct values are
|
||||
kept in `snapshot_hash_candidates` with `snapshot_hash: null` and
|
||||
`coverage: "partial"` rather than silently picking one.
|
||||
Identification can retain a single byte-scanned hash from a unified payload;
|
||||
its partial status does not establish truncation or support for that profile.
|
||||
- **`elf_header_valid`** checks the supported little-endian ELF32/ELF64
|
||||
header, table entry widths, and full table extents against the library's
|
||||
actual byte length. Truncated, malformed, or unsupported headers report
|
||||
@@ -108,7 +116,12 @@ server; older servers keep the complete request after schema discovery.
|
||||
`build_id_candidates` preserves distinct IDs. `build_id` is populated only
|
||||
when all declared note ranges were examined and one ID was found;
|
||||
conflicting IDs or malformed/bounded note metadata make coverage partial.
|
||||
Nonempty GNU build-ID descriptors up to 64 bytes are retained as lowercase
|
||||
hexadecimal, including IDs shorter than four bytes.
|
||||
Matching bytes outside declared note ranges cannot supply a build ID.
|
||||
Compressed `SHT_NOTE` sections are skipped with an unsupported-compression
|
||||
limitation and partial coverage; IDs from other note ranges remain candidates.
|
||||
Encoded note storage is never interpreted as note headers or inflated.
|
||||
- Labeled strings must fit their complete grammar. `dart_version_capped`
|
||||
and `toolchain_capped` independently report labels omitted by grammar or
|
||||
projection bounds; either makes identification coverage partial. Values
|
||||
@@ -134,8 +147,9 @@ server; older servers keep the complete request after schema discovery.
|
||||
- **Bounded reads.** APK snapshots are bounded at 2 GiB, including while
|
||||
copying. Identification permits 768 MiB of cumulative library extraction;
|
||||
declared sizes are checked before extraction. Library reads are bounded at 256 MiB each and ZIP
|
||||
metadata at 8 MiB; the AOT image buffer is allocated at exactly the
|
||||
declared size before reading. Exceeded budgets report
|
||||
metadata at 8 MiB. Before reading, the AOT image buffer reserves the
|
||||
admitted declared size plus one byte; the reported byte count is the number
|
||||
actually read. Exceeded budgets report
|
||||
`resource_constraint`.
|
||||
- **Engine notes.** Engine libraries are spooled into the owned root. ELF
|
||||
headers, note tables and note payloads share an 8 MiB metadata budget;
|
||||
@@ -152,9 +166,16 @@ server; older servers keep the complete request after schema discovery.
|
||||
- **Selected ABI.** AOT inspection applies the library budget to the selected
|
||||
ABI after selection; an oversized unselected library does not block it.
|
||||
Undefined dynamic-symbol imports are not treated as snapshot exports.
|
||||
A retained section directory without `SHT_DYNSYM` uses the same bounded
|
||||
`PT_DYNAMIC` symbol description as a section-stripped image. Malformed
|
||||
present symbol-table sections remain failures.
|
||||
Section-stripped images require a bounded symbol count from `DT_HASH` or
|
||||
`DT_GNU_HASH`; layouts without either are refused. Symbol names and their
|
||||
terminators must fit their linked string table.
|
||||
terminators must fit their linked string table. Only dynamic entries used for
|
||||
symbol discovery are interpreted as numeric bounds. ABI-ignored payloads,
|
||||
including [`DT_BIND_NOW`](https://gabi.xinuos.com/elf/08-dynamic.html#dynamic-section),
|
||||
do not change discovery; their stored bytes still contribute to the selected
|
||||
artifact's digest.
|
||||
- REA performs **no hash-to-SDK lookup**: mapping the snapshot hash to a
|
||||
Dart SDK release requires an external dataset and is deliberately out of
|
||||
scope for this family.
|
||||
|
||||
@@ -6,6 +6,8 @@ For remote devices, pass a host with an optional port (`host[:port]`; bracket IP
|
||||
|
||||
MCP exposes `list_frida_devices`, `list_frida_processes`, `start_frida_session`, `load_frida_script`, `resume_frida_session`, `unload_frida_script`, `frida_session_status`, and `close_frida_session`. A spawned process starts paused so scripts can load first. Closing unloads session scripts and detaches. If a spawn remains paused, REA resumes it only after script cleanup or session detach confirms the instrumentation is released; if both cleanup paths fail, the target stays paused and the cleanup error is retained. REA never kills the target. MCP sessions end with the server process. The one-shot CLI command `instrument-with-frida` accepts one inline script or script file, observes messages for a bounded duration, and returns Evidence with the source hash and captured messages.
|
||||
|
||||
`instrument-with-frida --json` returns the Evidence record directly, matching MCP. Consumers of the earlier `{evidence, cleanup_error}` CLI wrapper should read the record at the JSON root and the session cleanup observation at `normalized_result.cleanup_error`. If final provider cleanup fails or cancellation arrives during it, the typed error retains completed Evidence in `details.partial_observation`. Discovery commands retain their device or process report in the same field.
|
||||
|
||||
For state and capture-coverage checks, pass `{"session_id":"SESSION_ID","detail":"summary"}` to `frida_session_status`. Each call reads the observed session state and retains the complete observation. The reply is an `inspect_analysis_view` Evidence summary with the session identity, state, scripts, `messages_retained`, and `messages_truncated`; message payloads remain in its `parent_evidence_id`. The count covers retained queued messages, not every target event. Omitted `detail` or `detail=complete` preserves the existing complete reply.
|
||||
|
||||
A summary's selected fields look like this (excerpt):
|
||||
|
||||
+12
-6
@@ -23,13 +23,19 @@ not name its entry function `entry`. MCP uses `open_binary`, `list_procedures`,
|
||||
`procedure_info`, `read_function_instructions`, and `analyze_function` with the
|
||||
same admitted target and provider semantics.
|
||||
|
||||
The classifier validates MZ page counts, initialized module length, header and
|
||||
The DOS MZ parser validates page counts, initialized module length, header and
|
||||
relocation-table bounds, relocation destinations, and the initialized entry
|
||||
point. It distinguishes DOS load-module and relocation bytes from the Windows
|
||||
new-header field. PE remains PE; NE, LE, and LX declarations are explicitly
|
||||
unsupported. A damaged declared Windows header does not silently become DOS.
|
||||
Inventory classification returns `unknown` when its bounded prefix cannot
|
||||
establish the format; this is distinct from the complete target admission check.
|
||||
point using the available structural bytes and actual file length. It reports
|
||||
those DOS facts separately from Windows-header evidence. A recognized PE
|
||||
signature remains PE; recognized NE, LE, and LX headers are explicitly
|
||||
unsupported. A nonzero Windows-header pointer without a recognized signature
|
||||
remains an unresolved candidate, even when the DOS structure validates:
|
||||
automatic target selection fails closed and reports both observations.
|
||||
REA does not infer DOS intent from padding or infer a damaged Windows header
|
||||
from the pointer alone. Inventory classification is a bounded-prefix format
|
||||
hint; it returns `unknown` for that candidate or when its prefix cannot
|
||||
establish the format. That result is distinct from complete DOS validation and
|
||||
does not decide target admission.
|
||||
|
||||
Ghidra imports a private target snapshot with `MzLoader`, language
|
||||
`x86:LE:16:Real Mode`, and compiler specification `default`. These settings,
|
||||
|
||||
@@ -109,7 +109,7 @@ bytes live only in a parser-owned overlay, identified by virtual address, size,
|
||||
and SHA-256; the caller's source Buffer remains unchanged. Limits derive from
|
||||
captured section extents and format field bounds. Separate resource policies
|
||||
admit up to 128 MiB of captured PE bytes, 64 MiB-equivalent parser work, 64 MiB
|
||||
of exact parser-owned overlay/index Buffers, and 4 MiB of serialized NativeAOT
|
||||
of exact parser-owned overlay/index storage, and 4 MiB of serialized NativeAOT
|
||||
report JSON. These are local parser limits, independent of MCP delivery; the
|
||||
buffer counter does not claim to cap V8 heap or total process memory. Rows that
|
||||
do not fit the report budget are omitted with explicit partial status and
|
||||
@@ -120,6 +120,26 @@ before and after parsing; the synchronous parser call itself is bounded but
|
||||
cannot be interrupted mid-call. Parser output never claims that a Ghidra
|
||||
database type was defined.
|
||||
|
||||
RTR signature omission counts include every eligible section before the work
|
||||
budget is spent, including later sections that were never visited. MethodTable
|
||||
closure indexes each validated table's related pointer once and visits a work
|
||||
queue for inheritance and interface reachability. Its temporary index and queue
|
||||
consume the existing working-memory budget; the parser does not increase its
|
||||
work limit. Candidate headers/counts are parsed once; unmapped table bodies are
|
||||
rejected before charging table entries, and validation charges only each slot or
|
||||
interface pointer actually examined. Declared counts and unvisited tails do not
|
||||
become examined coverage. Discovery still must finish before claiming a unique System.Object
|
||||
root, so a discovery budget stop can return zero recovered tables without
|
||||
establishing their absence. Performance on the large bflat report has not been
|
||||
measured. Indexed closure and candidate validation avoid separate repeated or
|
||||
unperformed work, but do not establish that the fixed discovery budget admits
|
||||
that artifact. Budget stops remain partial results with unknown type absence.
|
||||
|
||||
If captured snapshot bytes no longer match their admitted digest, the parser
|
||||
retains both digests and the Ghidra owner reports `artifact_changed`, with the
|
||||
captured byte count and admitted identity still available. Reopen acquisition
|
||||
against a stable artifact; this is distinct from corrupted retained Evidence.
|
||||
|
||||
The optional adapter performs its annotations in an ephemeral Program
|
||||
transaction. That workflow may change analysis-memory bytes, never the original
|
||||
executable file. It is unavailable on Windows P0, which has no database
|
||||
|
||||
+21
-2
@@ -15,7 +15,20 @@ snapshot parser and a separate optional annotation extension. Check the
|
||||
[release boundary](installation.md#released-package-and-main) when using npm.
|
||||
|
||||
See [import language and analysis seeds](installation.md#ghidra-import-language-and-analysis-seeds)
|
||||
to override the header-detected language or seed analysis.
|
||||
to override the header-detected language or seed analysis. An override that conflicts with a fixed raw, DOS, PSP, MIPS or RISC-V import
|
||||
is a configuration error naming the conflicting settings. It does not make an
|
||||
otherwise admitted target unsupported; REA preserves the requested settings.
|
||||
An inaccessible language
|
||||
catalog remains a read failure; it does not prove that a configured language is
|
||||
undefined. Installation `application.properties` has a fixed 1 MiB read allowance
|
||||
with no runtime override. The failure retains that allowance and directs recovery
|
||||
at the selected installation metadata.
|
||||
|
||||
Retained process diagnostics can report termination by `SIGKILL`; the signal alone
|
||||
cannot establish an OOM kill. REA gives neutral recovery guidance and preserves the
|
||||
signal. Confirm resource causes from process output or host/container records;
|
||||
operation-scoped JVM `OutOfMemoryError` observations retain their separate resource
|
||||
classification. Automatic kernel or cgroup OOM attribution is not implemented.
|
||||
|
||||
Provider selection and profile identity follow
|
||||
[MCP contracts](mcp-contracts.md#identity-and-discovery). One deep
|
||||
@@ -162,7 +175,7 @@ profile stages before retrying their retained descriptors.
|
||||
|
||||
| Concern | Ghidra contract |
|
||||
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Decompiler lifetime | One persistent `DecompInterface` is opened for the imported Program and disposed during bridge shutdown. Native decompilation has no fixed per-function deadline. External functions or functions without bodies return `null`; cancellation and native failure remain distinct. |
|
||||
| Decompiler lifetime | One persistent `DecompInterface` is opened for the imported Program and disposed during bridge shutdown. Native decompilation has no fixed per-function deadline unless `REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS` sets one; a decompile that reaches it fails only that request with `remote_code` `decompile_timed_out`, the Program and session stay open, and the next decompile uses a fresh decompiler process. External functions or functions without bodies return `null`; cancellation and native failure remain distinct. |
|
||||
| Serialization | A FIFO sends one Program request at a time. Caller cancellation removes queued work promptly and is passed to active socket waits. This is an adapter safety commitment, not a claim that every Ghidra API is thread-safe. |
|
||||
| Function identity | Every function result carries the entry address and Ghidra FunctionManager classification for external, thunk, and resolved thunk target. These are observations, not proof that unresolved targetless calls have been recovered. |
|
||||
| Assembly and pseudocode | Assembly is complete Ghidra Listing text; pseudocode is Ghidra decompiler output. Neither is original source, and cross-provider comparison never treats Hopper and Ghidra text as equal or unequal semantic facts. |
|
||||
@@ -257,3 +270,9 @@ next match. Coverage can overlap between pages; do not sum it as unique memory.
|
||||
page. These blocks are never searched. Cancellation returns a typed error,
|
||||
discards the active Ghidra database, and does not return a successful partial page.
|
||||
Hopper byte search remains unavailable until its implementation is verified.
|
||||
|
||||
MIPS16 shared-image admission requires examined dynamic REL declarations.
|
||||
RELR dynamic tags or nonempty `SHT_RELR` sections leave relocation coverage
|
||||
unavailable and the profile unsupported; REA does not infer an empty relocation
|
||||
set from the absence of REL declarations. RELR loader semantics have not been
|
||||
verified for this lane.
|
||||
+47
-8
@@ -5,6 +5,12 @@ modules, replacements and build settings are embedded in one selected local
|
||||
executable or library. It reads the file without running it and requires no
|
||||
Go installation, active binary target, Hopper, Ghidra or IDA.
|
||||
|
||||
If a stable read completes but descriptor finalization fails or receives
|
||||
cancellation, the error retains the selected path, SHA-256 and byte length as
|
||||
a `file-snapshot-hash` partial observation. It does not claim that Go metadata
|
||||
was decoded. Failed cleanup stays incomplete until its exact owner confirms
|
||||
release; cancellation keeps the caller's reason.
|
||||
|
||||
```bash
|
||||
rea inspect-go-binary /absolute/path/to/application --json
|
||||
```
|
||||
@@ -61,7 +67,7 @@ rea inspect-go-binary ./application --detail summary --evidence_output ./go-evid
|
||||
`--evidence_output` is required for CLI summary delivery and never overwrites an
|
||||
existing file. The default CLI/MCP reply remains complete.
|
||||
|
||||
[Measured workflows](../experiments/analysis-efficiency/go-build-views.md) include
|
||||
[Measured workflows](https://github.com/morluto/rea/blob/42e1a17d6e6c0487f17be0bae248553c4caba7f7/experiments/analysis-efficiency/go-build-views.md) include
|
||||
catalog repetition and extra calls. Identity/count questions benefit from a
|
||||
single summary. In the tested Codex full-catalog configuration, a compound
|
||||
question about identity and one dependency uses fewer tokens in captured request JSON with
|
||||
@@ -112,8 +118,28 @@ module record and no dependency records has an observed empty dependency list.
|
||||
The built-in TypeScript reader supports ELF, PE and thin Mach-O images, and one
|
||||
selected slice of a universal Mach-O, with Go's inline or historical pointer
|
||||
build-info encoding. Source ranges refer to the original stable file snapshot,
|
||||
including pointer-backed strings. It does not scan arbitrary byte strings as
|
||||
compiler metadata.
|
||||
including pointer-backed strings. The shared native container owns address
|
||||
mapping, including file-backed PE headers. A descriptor or string can cross
|
||||
adjacent mappings when their virtual addresses map to physically contiguous
|
||||
file bytes with the same translation. Gaps, conflicting aliases, zero-fill
|
||||
aliases and noncontiguous string source bytes cannot establish a single source
|
||||
range and are refused. Native export C strings use the same mapping checks but
|
||||
can join multiple source chunks because their result has no single-span source
|
||||
contract. Legacy pointer-backed fields are decoded at
|
||||
the width declared by Go's build-info record, independently of the image's ELF
|
||||
class. It does not scan arbitrary byte strings as compiler metadata.
|
||||
|
||||
Legacy string descriptors and contents may use the declared file-backed PE
|
||||
headers. A descriptor or string may also cross adjacent loader mappings when
|
||||
their virtual ranges cover every requested byte and identify one contiguous
|
||||
file range. Gaps, conflicting file aliases and nonempty zero-fill overlap remain
|
||||
invalid. Discovery policy and the aggregate 1 MiB string-content limit are
|
||||
unchanged. These are static reads over REA's native mapping facts; they do not
|
||||
establish Windows/Linux loading or parity with Go's standard reader, whose
|
||||
[Go 1.24.0 ELF/PE readers](https://github.com/golang/go/blob/go1.24.0/src/debug/buildinfo/buildinfo.go#L445)
|
||||
read within individual segments or sections. The historical two-word string
|
||||
descriptor follows the
|
||||
[Go 1.17.13 reader](https://github.com/golang/go/blob/go1.17.13/src/cmd/go/internal/version/version.go#L212).
|
||||
|
||||
A universal (FAT) Mach-O needs `architecture` (`x86`, `x86_64`, `arm`
|
||||
or `arm64`; CLI `--architecture`). REA reads its own FAT table, without `lipo`,
|
||||
@@ -129,7 +155,13 @@ For ELF images without a named build-info section, discovery checks each
|
||||
nonempty writable, non-executable load segment in program-header order. This
|
||||
includes sectionless PIE images with metadata in a later data segment. Named
|
||||
sections retain priority, and alignment, file mapping and record validation
|
||||
remain required.
|
||||
remain required. When ELF section names are unreadable, limitations report that
|
||||
coverage gap; the fallback search policy is unchanged.
|
||||
|
||||
A named ELF or Mach-O build-info section selects the record's discovery range.
|
||||
For inline encoding, the following version and module strings may continue past
|
||||
that section into its file-backed load mapping; their bytes are still checked
|
||||
against the mapping and the existing aggregate string limit.
|
||||
|
||||
Historical moduleless Go binaries can store their empty module-string header in
|
||||
declared zero-initialized virtual memory rather than file-backed bytes. The reader
|
||||
@@ -156,10 +188,12 @@ and `reported_limits.maximum_input_bytes`. Select a suitable input within that
|
||||
fixed allowance; changing an output-write or transport budget does not admit it.
|
||||
|
||||
Resource guards bound the selected file and cumulative header searches to 256 MiB
|
||||
each, aggregate embedded strings
|
||||
to 1 MiB, structural table decoding to 16 MiB and the complete normalized metadata
|
||||
report to 8 MiB. Identical search spans with the same virtual alignment are
|
||||
searched once; partially overlapping spans consume the cumulative search budget
|
||||
each, aggregate embedded string contents to 1 MiB (varint length prefixes are
|
||||
framing and do not count toward this allowance), structural table decoding to
|
||||
16 MiB and the complete normalized metadata report to 8 MiB. Inline strings must
|
||||
also fit within their actual mapped source range. Identical search spans with
|
||||
the same virtual alignment are searched once; partially overlapping spans
|
||||
consume the cumulative search budget
|
||||
and fail with a CPU resource constraint when it is exhausted. Oversized results
|
||||
fail without partial success. MCP also applies
|
||||
its [response transport budget](mcp-contracts.md), which counts both text and
|
||||
@@ -178,3 +212,8 @@ those questions require a disassembler.
|
||||
`npm run verify:go:binary` exercises compiler-built fixtures through the public
|
||||
CLI and MCP, comparing their metadata with Go's standard `debug/buildinfo`
|
||||
reader. The verification lane requires Go; normal inspection does not.
|
||||
|
||||
Reader architecture names remain verbatim in the report. Evidence subjects use
|
||||
the recognized canonical family: `386` maps to `x86`, `amd64` to `x86_64`, and
|
||||
`ppc` to `powerpc`. Unknown families such as `ppc64` remain unknown rather than
|
||||
being inferred from a related spelling.
|
||||
@@ -34,6 +34,11 @@ The same CLI commands locate a PCK at the beginning of a file or through its
|
||||
payloads or discover packs behind additional trailing signatures. The root
|
||||
content SHA-256 covers the whole carrier; its content classification remains
|
||||
native, while the inventory records the PCK role and exact pack bounds.
|
||||
An EOF marker is only a candidate: an invalid size or a pointed-to location
|
||||
without a `GDPC` header leaves native classification in place. Once the
|
||||
trailer points to a pack header, malformed or unsupported PCK content remains
|
||||
an error. This recognition boundary follows the Godot loader at pinned source
|
||||
revision [06d1e7a](https://github.com/godotengine/godot/blob/06d1e7a34ccde4029eed53d018cfde77dfe771d5/core/io/file_access_pack.cpp#L2099-L2146).
|
||||
Ordinary native `open_binary` detection remains unchanged.
|
||||
|
||||
## MCP and selected facts
|
||||
|
||||
@@ -27,11 +27,38 @@ selected fields.
|
||||
Conflicting base architectures retain both values, mark object/report/target
|
||||
coverage partial and annotate the affected kernels. CUBIN flags establish a base
|
||||
architecture: `base-only` explicitly leaves feature-profile agreement unknown,
|
||||
including `a` variants. A standalone declaration is `single-source`, and
|
||||
including `a` and `f` variants. A standalone declaration is `single-source`, and
|
||||
unexamined payloads remain `unexamined`. An exact AMDGPU target is normalized at
|
||||
its note owner for comparison with a Clang bundle declaration; raw values remain
|
||||
intact. Unresolved feature comparisons stay `unknown`.
|
||||
|
||||
A HIP target ID identifies a toolchain target, not its payload representation.
|
||||
Recognized raw or wrapped LLVM bitcode stays `unsupported` for kernel metadata
|
||||
inspection, with the bundle ID, range and selected payload digest retained.
|
||||
Bitcode validity and inner targets remain unexamined; malformed recognized ELF
|
||||
still reports `malformed`. Standalone bitcode is outside GPU container coverage.
|
||||
|
||||
HIP bundle and metadata note recognition accept both the legacy `amdgcn` triple
|
||||
and LLVM's `amdgpu` subarchitecture triples, including declared generic processors
|
||||
such as `gfx11-generic` and `gfx9-4-generic`. Notes with the empty or explicit
|
||||
`unknown` environment use the same processor and feature comparison while
|
||||
retaining their raw triple spelling. CUDA's legacy fatbinary compression flag is
|
||||
retained as an unsupported codec with its encoded and decoded extents;
|
||||
compressed bytes are not treated as PTX text. CCOB is recognized by the exact
|
||||
four signature bytes `CCOB`; high-bit-corrupted bytes are not decoded as ASCII
|
||||
and accepted as a bundle. CCOB v1 has no stored total size: the completed zlib
|
||||
or Zstd stream determines its encoded extent. Embedded `CCOB` text is ordinary
|
||||
compressed data; zero-byte alignment after the stream belongs to neither
|
||||
payload. If decoding fails or its codec is unsupported, a later `CCOB` marker
|
||||
is only an uncertain recovery candidate. The affected row retains that
|
||||
limitation and discovery remains partial.
|
||||
|
||||
The declared decoded length bounds decompression. An understated length can
|
||||
stop decoding before codec completion, so any subsequent recovery extent stays
|
||||
unknown; the decoder does not expand its allowance to prove the bad declaration.
|
||||
Interrupted observations omit unfinished v1 rows. A terminal capacity row
|
||||
qualifies its retained range as the containing tail and does not infer siblings.
|
||||
|
||||
`cuda_target_header` retains the OS ABI, normalized raw `e_flags` and exact
|
||||
header location. CUDA OS ABI `0x33` and the observed `0x41` flags layout use
|
||||
different architecture fields; they are decoded at one ABI-aware owner.
|
||||
@@ -43,8 +70,8 @@ target remains unexamined; an empty filtered result does not establish that
|
||||
no enclosed code declares that target. Discovering every inner declaration
|
||||
requires decoding those payloads.
|
||||
|
||||
HIP IDs with one or two separators before the architecture retain the declared
|
||||
target. If a recognized AMDGPU bundle ID does not expose a target, the same
|
||||
HIP bundle IDs retain the declared processor and v2-v3 `+feature` suffixes for
|
||||
exact selection. If a recognized AMDGPU bundle ID does not expose a target, the same
|
||||
bounded note-discovery owner used for standalone AMDGPU files establishes it
|
||||
before filtering. Those directory/note reads share the inspection work budget;
|
||||
successive queries can select the target returned by inventory.
|
||||
@@ -81,15 +108,42 @@ Kernel representations have different meanings:
|
||||
- PTX reports parameter declarations and virtual registers. Shared bytes cover
|
||||
supported static declarations inside that entry; they do not establish module
|
||||
shared memory, dynamic allocation or physical device resources.
|
||||
PTX 1.0–1.3 declares entry parameters in the body; PTX 1.4 and later uses the
|
||||
entry parameter list. Entry-scoped `.pragma` statements before the body do
|
||||
not end the declaration or suppress its body resources. A `.target
|
||||
compute_xx` spelling is retained as written;
|
||||
NVIDIA PTX defines it as a synonym for `.target sm_xx` when comparing its
|
||||
target identity with the fatbin declaration. `.callprototype` formal
|
||||
parameters are excluded from entry virtual-register counts. Parameter names
|
||||
remain available when pointer qualifiers are understood or an unsupported
|
||||
qualifier prevents layout decoding. A pointer's `.align` describes the
|
||||
pointed-to memory and does not change parameter layout alignment. Spaced,
|
||||
joined and partly joined pointer qualifiers have the same meaning. Parameter
|
||||
alignment before or after the scalar type retains its layout and name.
|
||||
Parameter names and following declarations are retained when an array
|
||||
extent uses a parenthesized expression or when dimensions are
|
||||
multidimensional, even though those forms do not currently yield a layout.
|
||||
PTX target selection uses the highest-numbered module-scope `.target`
|
||||
directive, including directives after entry declarations; unsupported or
|
||||
conflicting target profiles leave the effective target unknown.
|
||||
Scalar register declarations count each bare or percent-prefixed name and
|
||||
each parameterized range. Unsupported declaration forms or overflowing totals
|
||||
each parameterized range, including compact keywords such as `.reg.b32`.
|
||||
Unsupported declaration forms or overflowing totals
|
||||
retain an unknown count and partial decoding coverage. Unsupported parameter
|
||||
layouts and unknown shared-memory declarations likewise retain their known
|
||||
facts, explicit limitations and partial coverage through retained pages.
|
||||
- CUBIN identifies CUDA entry symbols and reads numeric `nv.info` records and
|
||||
native section declarations. Global register declarations also cover compiler
|
||||
- CUBIN identifies CUDA entry symbols and reads numeric `nv.info` records,
|
||||
including V2 kernel-parameter sizes, and native section declarations. Global
|
||||
register declarations also cover compiler
|
||||
versions that leave the older text-section register field empty.
|
||||
- AMDGPU reads v3+ MessagePack notes. Argument counts include hidden ABI arguments
|
||||
Conflicting register declarations keep the physical count unknown, preserve the
|
||||
kernel's other facts, and mark object/report/target decoding coverage partial.
|
||||
- AMDGPU reads v3+ MessagePack notes. V3 processor identity comes from the
|
||||
`EF_AMDGPU_MACH` field in ELF `e_flags`; known processor values are selectable
|
||||
even when the note omits `amdhsa.target`. This inventory reports the processor
|
||||
only; it does not interpret version-specific feature flags. Unknown processor values remain unknown. A known
|
||||
processor conflict between ELF flags and note metadata leaves the processor
|
||||
unresolved. Argument counts include hidden ABI arguments
|
||||
when present; SGPR/VGPR counts and fixed group/private segment sizes retain
|
||||
their declared meanings. Legacy YAML metadata remains unsupported.
|
||||
|
||||
@@ -119,7 +173,9 @@ A location with `container_id: null` uses original file offsets. Other locations
|
||||
use that inventoried object's decoded byte coordinates. Follow its parent,
|
||||
encoded location and digest to trace decompressed metadata back to a native
|
||||
section and the original file. Decoded offsets are never presented as physical
|
||||
file offsets.
|
||||
file offsets. Reads from an ELF or PE GPU section stay within that declared
|
||||
section extent, so neighboring native section bytes cannot complete a
|
||||
truncated embedded bundle header.
|
||||
|
||||
The CLI uses the same service and contracts:
|
||||
|
||||
@@ -164,3 +220,5 @@ failures preserve earlier successfully parsed declarations and the original
|
||||
failure reason. Standalone PTX recognition tries a small header first, then
|
||||
continues within the existing 16 MiB text budget when leading comments or
|
||||
whitespace obscure its directives; admitted full-file probe bytes are reused.
|
||||
After those comments and whitespace, `.version` must be the first PTX module
|
||||
token; later PTX-looking text does not identify an arbitrary binary as PTX.
|
||||
@@ -36,8 +36,9 @@ The MCP tool `project_harmony_application_graph` takes the same
|
||||
|
||||
- exact component paths, artifact IDs, and hashes (manifests, Ark bytecode,
|
||||
resources, native libraries, JavaScript, signing)
|
||||
- a packaging-model hint: Stage is claimed only from a `module.json` entry,
|
||||
FA only from `config.json`, otherwise `unknown`
|
||||
- a packaging-model hint: Stage is claimed only from a `module.json` entry
|
||||
outside a classified resource path, FA only from `config.json` outside a
|
||||
classified resource path, otherwise `unknown`
|
||||
- FA resources under `assets/<module>/resources/` as well as Stage `resources/`
|
||||
- unavailable component paths with unknown IDs and hashes, including encrypted entries; these make projection coverage partial
|
||||
- App Pack child package paths without recursive inventory
|
||||
@@ -83,3 +84,12 @@ not been independently established. This lane does not complete the pinned
|
||||
toolchain acceptance in #1431 and does not verify Ark decoding or signatures.
|
||||
Set `REA_HARMONY_ENTRYPOINT` to an installed package's `scripts/rea.mjs` to run
|
||||
the same checks against that package; the default lane uses the repository build.
|
||||
|
||||
Packaging-model and App Pack child presence use inventoried file/slice paths even
|
||||
when encrypted content cannot be authenticated. Unavailable App Pack children
|
||||
retain their path, format, encryption and limitations with null artifact ID and
|
||||
digest; they do not establish decoded package semantics. Missing source nodes
|
||||
outside supplied pages retain the inventory limitation and partial coverage.
|
||||
The signing list includes inventoried META-INF RSA, DSA and EC certificate-block
|
||||
paths alongside the existing suffixes; opaque content does not establish a valid
|
||||
signature or certificate.
|
||||
+43
-2
@@ -108,7 +108,14 @@ Headless REA and the configured supervisor must share the host filesystem and na
|
||||
|
||||
REA copies the admitted binary into a private temporary directory, checks its digest, and requests an owned worker with `force_headless`. POSIX workspaces are created atomically with owner-only permissions, respecting the process umask; Windows uses the matching package's native private-DACL boundary on a local NTFS temporary directory. It injects the returned database ID into analysis requests. It verifies the active owned worker and input path rather than adopting a user database. On close or failed startup, it releases its session with `save: false`, verifies that it is absent from upstream session discovery, and removes its private input and sidecars. An unverified release retains the workspace and reports incomplete cleanup.
|
||||
|
||||
One REA session owns one database. Stdio registrations default `IDA_MCP_MAX_WORKERS` to `1`; the explicit upstream `--max-workers 1` also bounds a supervisor. Database size and engine memory depend on the target and upstream; REA does not impose an engine memory limit. The original binary is never executed or overwritten. Optional `workspaceRoot` selects an existing absolute temporary-directory parent on the REA host. Ensure it has space for the input copy and IDA working files.
|
||||
One REA session owns one database. Stdio registrations default `IDA_MCP_MAX_WORKERS` to `1`; the explicit upstream `--max-workers 1` also bounds a supervisor. Database size and engine memory depend on the target and upstream; REA does not impose an engine memory limit. The original binary is never executed or overwritten. Optional `workspaceRoot` selects an existing absolute temporary-directory parent on the REA host. The selected parent is resolved by the filesystem before deriving its canonical identity, including POSIX symlink/parent components. Ensure it has space for the input copy and IDA working files.
|
||||
|
||||
Readiness and allocation check the selected spelling with native
|
||||
[`stat`](https://github.com/nodejs/node/blob/v24.22.1/doc/api/fs.md#fspromisesstatpath-options)
|
||||
before canonicalizing it. This retains missing and non-directory intermediate
|
||||
lookup failures: [macOS `realpath`](https://github.com/apple-oss-distributions/Libc/blob/71bbe350ab79eef58113991d817ccc6165061a64/stdlib/FreeBSD/realpath.c)
|
||||
alone can collapse `file/..`. Diagnostics retain the selected spelling; private
|
||||
input paths use the canonical parent for upstream identity checks.
|
||||
|
||||
When a completed `idb_open` response reports success or is otherwise ambiguous, but no active owned private worker can be verified, REA retains the private workspace even if `idb_list` appears empty. A later close can retry verification if the original owned worker becomes discoverable. A completed explicit `idb_open` rejection retains the existing no-worker cleanup path. This is a local admission/ownership safeguard and **does not** provide a process-termination receipt or change the legacy reaped-worker protocol.
|
||||
|
||||
@@ -133,7 +140,7 @@ Add the registration path to the **REA** MCP entry's environment, keeping the up
|
||||
}
|
||||
```
|
||||
|
||||
Use a package version that actually includes this adapter; repository main can be ahead of the npm release. After reconnecting, call:
|
||||
Use a package version that actually includes this adapter; repository main can be ahead of the npm release. After reconnecting, call the sequence below; the default `analyze_function` reply is a summary that retains the dossier, so add `"detail":"complete"` when the dossier itself must come back inline:
|
||||
|
||||
```json
|
||||
{"name":"open_binary","arguments":{"path":"/absolute/path/to/program","provider_id":"ida"}}
|
||||
@@ -169,3 +176,37 @@ The verifier requires the resolved procedure in both inventory and procedure sea
|
||||
The initial real workflows use stdio registrations for the legacy Windows GUI profile and a Windows x64 headless supervisor with IDA 9.3. The headless workflow has also passed on macOS arm64 with IDA Professional 9.1 (build 9.1.250226) against the same upstream commit `c133c3853faa111a9b00ee615c013b720d0c4acd`, using a source-built arm64 Mach-O fixture; Mach-O C symbols carry a leading underscore, so the procedure name is `_rea_fixture_add`. Streamable HTTP is tested against a loopback MCP fixture with the pinned client SDK; a real IDA HTTP-supervisor workflow remains unverified. Linux headless engine operation, other IDA releases, architectures, and modern attached GUI tools require their corresponding real workflows before support is claimed.
|
||||
|
||||
Native Windows verification uses IDA Pro 9.3 with the public two-function fixture and Windows `whoami.exe`, using upstream `c9d15d98e8a143fc74f6e14037c48f63889b6d6c` with a loopback-listener change. It covers production CLI/MCP parity, distinct-function switching, identity and connection cleanup. See the [correctness and performance measurements](https://github.com/morluto/rea/blob/main/experiments/analysis-efficiency/ida-native-windows.md). This follow-up did not run Windows IDA Free or native macOS.
|
||||
|
||||
Headless readiness distinguishes an explicit `auto_analysis_ready: false` from
|
||||
unknown readiness (`null`). The former returns a retryable capability refusal
|
||||
with wait-and-retry guidance; the latter asks for upstream readiness inspection
|
||||
without claiming that analysis is running. Neither is malformed protocol output.
|
||||
An unusable `workspaceRoot` reports its directory constraint as a configuration
|
||||
error before opening an IDA worker. Native JSON metadata resources accept omitted
|
||||
media types and normal case/parameter variants of `application/json`; their
|
||||
resource URI, JSON structure and recorded target identity remain checked.
|
||||
|
||||
The public profile's registration digest excludes HTTP authentication header
|
||||
values (`Authorization`, `Proxy-Authorization`, and `Cookie`). Mark any other
|
||||
credential fields explicitly with `"sensitive": { "headers": ["X-Api-Key"],
|
||||
"env": ["PRIVATE_VALUE"] }` in the registration. Header names match without
|
||||
case; environment names match exactly. These values are also redacted from IDA
|
||||
diagnostics. Ordinary fields such as `IDADIR` and noncredential headers remain
|
||||
part of analysis identity. Credential rotation does not change that public
|
||||
identity; each newly resolved client factory still captures its own complete
|
||||
transport registration and creates a fresh connection.
|
||||
|
||||
Caller cancellation is classified at the MCP request boundary using the installed
|
||||
SDK's exact abort outcome. A later abort during error unwinding does not replace
|
||||
an established provider failure. Cancellation retains the caller's original reason
|
||||
and collected diagnostics; metadata timeouts name the requested analysis operation
|
||||
while retaining the resource-read failure as their cause. This request cancellation
|
||||
does not authorize saving or closing an attached GUI database.
|
||||
|
||||
Registration refusals retain schema-authored constraints, including the
|
||||
native/attached lifecycle requirement, local credential-free endpoint requirement
|
||||
and absolute workspace root. Configuration diagnostics omit registration values.
|
||||
Target and private-worker identity refusals retain the failed check, its reason
|
||||
and recovery guidance in the public error before any analysis replies exist.
|
||||
These diagnostics do not relax identity admission or authorize closing an attached
|
||||
GUI database.
|
||||
+190
-43
@@ -58,15 +58,22 @@ the original rows without reopening or decoding the file:
|
||||
|
||||
Collections are `sections`, `images`, `types`, `methods`, `fields`,
|
||||
`properties`, `events`, `parameters`, `string_literals`, `method_addresses`,
|
||||
`return_types`, `field_types`, `parameter_types` and `code_gen_modules`. Every returned row includes
|
||||
`return_types`, `field_types`, `parameter_types`, `code_gen_modules`,
|
||||
`generic_method_instances` and `generic_method_issues`. Every returned row includes
|
||||
its source `index`, scoped by artifact SHA-256 and collection. All collections
|
||||
accept batch `indices` and `fields`; collections whose rows have a `name` also
|
||||
accept exact `names`. Types also accept `image_indices`, `namespaces` and
|
||||
accept batch `indices` and `fields`. Exact `names` filtering is supported for
|
||||
`sections`, `images`, `types`, `methods`, `fields`, `properties`, `events`,
|
||||
`parameters`, `return_types`, `field_types`, `parameter_types` and
|
||||
`generic_method_instances`. Native type names match the retained rendered display value exactly; every same-name row
|
||||
keeps its source index, and an unavailable (`null`) name never matches a string.
|
||||
Types also accept `image_indices`, `namespaces` and
|
||||
`declaring_types`; methods, fields, properties and events accept
|
||||
`declaring_types`. Parameters accept exact `method_indices`, so a batch of
|
||||
method indices retrieves their parameters without reading the entire parameter
|
||||
table. `parameter_types` accepts the same `method_indices`, resolved through the
|
||||
retained `parameters` owners; Evidence without `parameters` refuses that filter. Filters intersect, duplicate indices are returned once, and
|
||||
retained `parameters` owners; Evidence without `parameters` refuses that filter. Generic method instances
|
||||
accept `method_indices` (definition indices) and `states`; generic method
|
||||
issues accept `rows` (the instance rows they leave unknown). Filters intersect, duplicate indices are returned once, and
|
||||
rows retain metadata order. An empty filter selects no rows; `fields: []`
|
||||
returns indices only. Omitted fields return the complete selected rows.
|
||||
|
||||
@@ -110,7 +117,9 @@ Historical Evidence that predates these member collections remains queryable.
|
||||
A missing collection has an unknown (`null`) summary count, and requesting it
|
||||
reports that it was not collected; it is never presented as an empty inventory. The same holds for Evidence that predates binary linking: its summary
|
||||
omits `binary`, and `method_addresses`, `method_address_states`,
|
||||
`return_types`, `field_types` and `parameter_types` counts are `null`.
|
||||
`return_types`, `field_types` and `parameter_types` counts are `null`. A link retained before
|
||||
generic instances existed omits `binary.generic_methods`, and the
|
||||
`generic_method_instances` and `generic_method_issues` counts are `null`.
|
||||
Existing empty collections retain a zero count. Full Evidence is preserved.
|
||||
|
||||
`offset` counts matching rows, not source indices. `limit` defaults to 64;
|
||||
@@ -143,8 +152,18 @@ in [CLI and Evidence](cli.md#run-a-command). Compact delivery requires
|
||||
`--evidence_output`; existing files are never overwritten. The full export
|
||||
preserves every decoded row for subsequent queries without the original file.
|
||||
|
||||
`--binary PATH` links method addresses and `--architecture arm64|amd64` selects
|
||||
the slice of a universal Mach-O.
|
||||
`--binary PATH` links method addresses, `--architecture arm64|amd64` selects
|
||||
exactly one slice of a universal Mach-O and `--generic-instances` adds generic
|
||||
method instances. The report retains the native reader's
|
||||
`amd64` spelling for x86-64; loader CPU checks use the corresponding `x86_64`
|
||||
identity. Multiple slices with the same architecture label are refused as
|
||||
`ambiguous-slice-selection`, including
|
||||
distinct ARM subtypes that both map to `arm64`. On macOS, use `lipo -info`
|
||||
to enumerate exact subtypes, then extract the intended subtype into a separate
|
||||
file, for example `lipo universal -thin arm64e -output thin-arm64e`. Inspect
|
||||
that thin file without `--architecture`. An exact `arm64e` slice is not
|
||||
selected by `lipo -thin arm64`. Thinning resolves only selection ambiguity;
|
||||
the metadata must still match and unsupported pointer encodings remain unsupported.
|
||||
|
||||
## Observations
|
||||
|
||||
@@ -204,9 +223,22 @@ assembly image. Each module record starts with its module name, its method
|
||||
pointer count and its method pointer array. The reader finds, for every metadata
|
||||
image, the single record whose name pointer reaches that image's name string and
|
||||
whose count equals the image's method count. It then requires exactly one
|
||||
`codeGenModulesCount`/`codeGenModules` pair that lists exactly those records. No
|
||||
symbols are needed, so stripped builds work. A method's token row id selects its
|
||||
slot in the module's method pointer array.
|
||||
`codeGenModulesCount`/`codeGenModules` pair that lists exactly those records.
|
||||
`codeGenModulesCount` and module method counts own four bytes, followed by
|
||||
alignment padding before their eight-byte pointers. Unresolved loader writes
|
||||
overlapping those four bytes prevent the registration from being established;
|
||||
padding outside the field is neither required nor interpreted as part of its
|
||||
count. No symbols are needed, so stripped builds work. A method's token row id
|
||||
selects its slot in the module's method pointer array. Within one image, each method must
|
||||
claim a distinct MethodDef token row; duplicate rows are refused before any
|
||||
linked method table or seed output is published. Row ids may repeat across
|
||||
different images, and distinct rows may still resolve to the same address when
|
||||
the binary uses identical-code folding.
|
||||
|
||||
For ELF, a repeated load mapping that resolves a complete module record to the
|
||||
same virtual address and file bytes contributes one record. Distinct virtual
|
||||
record addresses remain distinct candidates, and conflicting file mappings
|
||||
remain malformed.
|
||||
|
||||
Module records establish names through their declared NUL-terminated string pointers. NUL/0xFF padding, spaces, Unicode and suffix-shared storage are supported. A pointer to a longer string does not match just its suffix; a pointer directly into a shared suffix may name it. Exact reversed UTF-8 matching avoids rescanning the file for every name and retains duplicate-name owners until module records resolve them. A contributor investigated a Linux
|
||||
`GameAssembly.so` from Unity 2023.2.13f1 whose static registration was not located. When no static pair is resolved on x86-64, the reader searches executable ranges for bytes consistent with RIP-relative LEA/MOV references to module-array candidates. Exactly one referenced array candidate must remain. `code_gen_modules_field` is then `null`, and Evidence is **inferred**: instruction boundaries, the absence of a static registration elsewhere, and runtime use were not verified. The module records and slots themselves remain file observations. Static-table associations are **derived**. Otherwise the result is `code-registration-not-found`.
|
||||
@@ -239,7 +271,9 @@ The result adds:
|
||||
`rva` (address minus `image_base`), both hexadecimal strings. `state` is
|
||||
`code` when the module slot holds an unambiguously mapped executable address, `no-body` when an observed slot is zero,
|
||||
as for abstract, extern and most generic methods, and `unknown` when no type
|
||||
range in any image holds the method or a symbol relocation leaves its slot unresolved.
|
||||
range in any image holds the method, a symbol relocation leaves its slot
|
||||
unresolved, or dyld will bind a Mach-O slot. The raw word in a dyld-bound
|
||||
slot is not treated as the resolved address.
|
||||
`address` and `rva` are `null` unless `state` is `code`. The summary counts
|
||||
each state in `counts.method_address_states`. Ghidra seeds are written only
|
||||
for `code` rows.
|
||||
@@ -248,19 +282,123 @@ The result adds:
|
||||
field and parameter index with `type`, the index into the binary's
|
||||
`Il2CppType` table, and `name`, rendered from that table: C# keywords for
|
||||
primitives (`int`, `string`, `object`), `Namespace.Outer+Inner` for type
|
||||
definitions, `T[]`, `T[,]` and `T*`, generic instances as
|
||||
definitions, `T[]` for vectors, `T[*]` for rank-one multidimensional arrays,
|
||||
`T[,]` for rank-two arrays and `T*`, generic instances as
|
||||
`System.Collections.Generic.List<string>` and generic parameters by their
|
||||
metadata name (`TKey`). A trailing `&` marks a by-reference type. `name` is
|
||||
`null` only for type index `-1`.
|
||||
metadata name (`TKey`). Nested generic arguments stay with the component
|
||||
that declares them, for example `Game.Outer<int>+Inner<string>` or
|
||||
`Game.Outer<int>+Leaf` when the nested type adds no parameters. A trailing
|
||||
`&` marks a by-reference type. `name` is `null` for type index `-1`, metadata
|
||||
version 27 as described below, or an unresolved type-table slot. A dyld bind
|
||||
or unresolved ELF relocation retains the type index without interpreting its
|
||||
stored word as a resolved pointer. If an unresolved loader value occurs
|
||||
inside a type graph, linking is refused with `unsupported-pointer-encoding`;
|
||||
a concrete name is not synthesized from that placeholder.
|
||||
|
||||
`binary` also records `types_field` and `types_count`: the address of
|
||||
`Il2CppMetadataRegistration.typesCount` and its value. That record is found as
|
||||
the single place whose `fieldOffsetsCount` and `typeDefinitionsSizesCount` both
|
||||
equal the metadata type-definition count and whose type table is file mapped.
|
||||
Counts use their declared 32-bit width independently of alignment padding.
|
||||
The complete 128-byte registration must have unambiguous loader coverage. Its
|
||||
used fields through byte 112 must be file-backed; a trailing declared zero-fill
|
||||
range can cover the final unused fields. An unmapped or conflicting tail is
|
||||
refused. These layouts follow the versioned [Unity 2022.3.8f1 producer header](https://github.com/MlgmXyysd/libil2cpp/blob/master/libil2cpp/Unity_2022.3/2022.3.8f1/il2cpp-class-internals.h#L596-L637)
|
||||
(redistributed source) and the [AArch64 aggregate alignment rules](https://github.com/ARM-software/abi-aa/blob/2025Q1/aapcs64/aapcs64.rst#aggregates).
|
||||
Dyld pointer ownership comes from the parsed bind stream, as in Apple's
|
||||
[dyld analyzer](https://github.com/apple-oss-distributions/dyld/blob/dyld-1165.3/common/MachOAnalyzer.cpp#L1508-L1511), rather than the slot's placeholder word.
|
||||
The padding, zero-fill-tail and unresolved-type cases have authored regression
|
||||
fixtures; they do not add real Unity-build verification to the checks above.
|
||||
|
||||
Without `binary_path`, `binary` is `null` and the four binary collections are
|
||||
empty in the complete producer report to indicate unrequested observations. Their summary counts, including state counts, are `null`, and native page requests report unavailable observations. This does not establish an empty native inventory. Missing native fields in historical Evidence remain unknown too.
|
||||
|
||||
### Generic method instances
|
||||
|
||||
`generic_instances: true` (CLI `--generic-instances`, needs `binary_path`) also
|
||||
links the instances in `Il2CppMetadataRegistration.genericMethodTable`, as
|
||||
Il2CppDumper does: each row's `genericMethodIndex` selects an `Il2CppMethodSpec`
|
||||
(generic method definition plus class and method `Il2CppGenericInst`), and its
|
||||
`methodIndex` selects a `genericMethodPointers` slot. Without the option both
|
||||
fields below are `null` (not linked, so unknown), as are the summary counts
|
||||
`generic_method_instances` and `generic_method_instance_states` and the seed
|
||||
counts. The result adds:
|
||||
|
||||
- `binary.generic_methods`: `generic_method_pointers_field` (the address of
|
||||
`genericMethodPointersCount`), the pointer, table, method spec and generic
|
||||
instance counts, `generic_method_table_stride` and `issues` (each with the
|
||||
`row` it leaves unknown, the `address` of the contradicting row, method spec
|
||||
or pointer slot record, and a `message`);
|
||||
- `generic_method_instances`: one row per table entry (row position is the
|
||||
table index) with `method_spec`, `method` (the recorded definition index,
|
||||
including invalid values, or `null` when the index cannot be read),
|
||||
`name`, and `state`, `address` and `rva` as in `method_addresses`: `code`,
|
||||
`no-body` for a zero pointer slot, or `unknown` exactly when an issue names
|
||||
the row.
|
||||
|
||||
Names follow Il2CppDumper's `GetMethodSpecName`, for example
|
||||
`System.Collections.Generic.Dictionary<int, string>$$TryGetValue` or
|
||||
`System.HashCode$$Add<int>`: namespaces only on the declaring type, nested types
|
||||
joined with `.`, the backtick arity cut, `IntPtr`/`UIntPtr`/`TypedReference`
|
||||
and no byref mark. These names do not depend on the byref bit, so version 27 is
|
||||
covered.
|
||||
|
||||
Header versions do not separate the CodeRegistration layouts (27.0, 27.1 to
|
||||
29 and 29.1/31 differ in the fields before `codeGenModulesCount`). Every layout for the header version is tried, and
|
||||
exactly one must have a `genericMethodPointers` count and an invoker count that
|
||||
bound every table row; the layout also fixes the row size (12 bytes, or 16 with
|
||||
an adjustor thunk index). No layout agreeing fails as
|
||||
`generic-method-registration-not-found`, more than one as
|
||||
`ambiguous-generic-method-registration`. An empty table selects no layout:
|
||||
the result has no rows, and `generic_method_pointers_field`,
|
||||
`generic_method_pointers_count` and `generic_method_table_stride` are `null`.
|
||||
A table of a few rows can still leave two layouts agreeing, which fails as
|
||||
ambiguous rather than picking one. Metadata versions 24 and 39 and builds
|
||||
without a static CodeRegistration (`code_gen_modules_field` `null`) fail as
|
||||
`generic-methods-unsupported`: for version 24 the type-definition generic
|
||||
container offset and the generic class layout have not been checked against a
|
||||
real build, so it is refused rather than guessed.
|
||||
|
||||
After the layout is fixed, rows that contradict another table do not fail the
|
||||
whole link: a method spec naming a method or generic instance out of range,
|
||||
class or method arguments whose count differs from the generic container the
|
||||
owner declares (none for a non-generic type or method), an owner whose generic
|
||||
container index is out of range, a name that cannot be rendered, or a pointer slot that is unmapped, has an
|
||||
unresolved relocation or points outside unambiguous executable file data adds
|
||||
an `issues` entry, and that row is `unknown` with no address. A method spec
|
||||
issue also leaves `name` `null`, even when the pointer slot is valid: the slot
|
||||
would otherwise be reported under a definition nothing confirms. Capacity
|
||||
refusals are never turned into issues. Rows that share one method spec
|
||||
(Il2CppDumper refuses such a table) each get an issue at their row address that
|
||||
names one other sharing row, and all of them lose `name` and become `unknown`.
|
||||
A zero pointer slot is a `no-body` row and has no issue. Before any row is
|
||||
resolved, the smallest row for every table entry is checked against the
|
||||
remaining report budget; each row and issue is then admitted as it is retained.
|
||||
Compact summaries omit `binary.generic_methods.issues` and keep its count in
|
||||
`counts.generic_method_issues`; query `collection: "generic_method_issues"`
|
||||
with `rows`, `indices`, `fields` and pagination. Select only the instance and
|
||||
issue facts needed for the question. For a small report where both collections
|
||||
are needed, `detail: "complete"` can cost less than a summary followed by two
|
||||
pages. Paging saves irrelevant row text but adds calls and schema overhead; it
|
||||
does not imply lower total latency or memory. See the
|
||||
[completed-query measurements](verification/il2cpp-generic-instances-2026-10-24.md).
|
||||
Like method addresses, these rows are derived from static tables; layout agreement
|
||||
is structural evidence, not proof of runtime use.
|
||||
|
||||
Contributor-reported verification against Il2CppDumper's `script.json` (same
|
||||
`Address` and `Name`):
|
||||
macOS arm64 and x86_64 slices from Unity 2020.3.49f1, 2021.1.28f1 (version 27),
|
||||
2021.3.45f2, 2022.3.30f1 (29), 2022.3.62f3 and 6000.0.84f1 (31); Android arm64
|
||||
from 2022.3.62f3; and the Windows x64 build from 2023.2.13f1 named above
|
||||
(`GameAssembly.dll` SHA-256
|
||||
`a3ae7f6ecd1f671297b7d98d12713ef024062d93cd7a4205298102656c93b67a`). On every
|
||||
build each linked instance matches an Il2CppDumper entry by address and name,
|
||||
and Il2CppDumper lists no address-name pair that REA leaves out (Windows: 63,750
|
||||
linked rows, 142 without code; Android: 5,152 and 44). This does not establish
|
||||
names for versions 24 or 39, for type arguments that are open generic type
|
||||
definitions, or anything about instances created at runtime. The Linux build
|
||||
from 2023.2.13f1 keeps no static CodeRegistration and is refused.
|
||||
|
||||
For metadata version 27, type names are `null` and only type indices are
|
||||
reported: versions 27.0 to 27.2 share that header number but place the
|
||||
Il2CppType byref flag in different bits, so a rendered name could gain or lose
|
||||
@@ -268,29 +406,35 @@ Il2CppType byref flag in different bits, so a rendered name could gain or lose
|
||||
|
||||
The builds checked are listed after the module search rules above.
|
||||
|
||||
| Reason | Category | Meaning |
|
||||
| --------------------------------- | ----------- | ------------------------------------------------------------------------------------------------- |
|
||||
| `unsupported-container` | unsupported | Not a 64-bit little-endian Mach-O, PE or ELF image (ELF: AArch64 or x86-64 with dynamic section). |
|
||||
| `slice-selection-required` | unsupported | A universal Mach-O was given without `binary_architecture`. |
|
||||
| `slice-not-found` | unsupported | The requested slice is absent, or a thin binary was given an architecture. |
|
||||
| `unsupported-pointer-encoding` | unsupported | Mach-O chained fixups or Android packed (APS2) ELF relocations; pointers not decoded. |
|
||||
| `malformed-container` | input error | The container headers are damaged. |
|
||||
| `code-gen-modules-not-found` | input error | An image has no module record; often a binary from a different build. |
|
||||
| `ambiguous-code-gen-module` | input error | More than one module record matches an image. |
|
||||
| `code-registration-not-found` | input error | No unique module table or executable-byte array candidate was resolved. |
|
||||
| `invalid-method-token` | input error | A method token is not a method row inside its module's pointer count. |
|
||||
| `invalid-method-pointer` | input error | A slot is unavailable or its nonzero address lacks unambiguous executable file coverage. |
|
||||
| `metadata-registration-not-found` | input error | No single MetadataRegistration matches the type-definition count. |
|
||||
| `invalid-type` | input error | A type index or `Il2CppType` record is out of range, unmapped or malformed. |
|
||||
| `unsupported-type-kind` | unsupported | A valid type kind has no implemented rendering. |
|
||||
| Reason | Category | Meaning |
|
||||
| --------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `unsupported-container` | unsupported | Not a 64-bit little-endian Mach-O, PE or ELF image (ELF: AArch64 or x86-64 with dynamic section). |
|
||||
| `slice-selection-required` | unsupported | A universal Mach-O was given without `binary_architecture`. |
|
||||
| `ambiguous-slice-selection` | unsupported | More than one universal slice matches the selected architecture label; select an exact subtype externally. |
|
||||
| `slice-not-found` | unsupported | The requested slice is absent, or a thin binary was given an architecture. |
|
||||
| `unsupported-pointer-encoding` | unsupported | Mach-O chained fixups or Android packed (APS2) ELF relocations; pointers not decoded. |
|
||||
| `malformed-container` | input error | The container headers are damaged. |
|
||||
| `code-gen-modules-not-found` | input error | An image has no module record; often a binary from a different build. |
|
||||
| `ambiguous-code-gen-module` | input error | More than one module record matches an image. |
|
||||
| `code-registration-not-found` | input error | No unique module table or executable-byte array candidate was resolved. |
|
||||
| `invalid-method-token` | input error | A token is outside its module's method-pointer rows, or a MethodDef row is reused within one image. |
|
||||
| `invalid-method-pointer` | input error | A slot is unavailable or its nonzero address lacks unambiguous executable file coverage. |
|
||||
| `metadata-registration-not-found` | input error | No single MetadataRegistration matches the type-definition count. |
|
||||
| `invalid-type` | input error | A type index or `Il2CppType` record is out of range, unmapped or malformed. |
|
||||
| `unsupported-type-kind` | unsupported | A valid type kind has no implemented rendering. |
|
||||
| `generic-methods-unsupported` | unsupported | Generic instances were requested for metadata version 24 or 39 or a build without a static CodeRegistration. |
|
||||
| `generic-method-registration-not-found` | input error | No CodeRegistration layout bounds every genericMethodTable row, or the generic tables are unreadable. |
|
||||
| `ambiguous-generic-method-registration` | input error | More than one CodeRegistration layout bounds every genericMethodTable row. |
|
||||
|
||||
Format and selection failures name `binary_path`; capacity refusals remain typed resource errors. Failed linking preserves the metadata source identity, validated counts, selected binary path/architecture and its SHA-256 when observed, plus prior producer observations. It never publishes an incomplete native table as complete. The binary input is bounded to1GiB.
|
||||
Format and selection failures name `binary_path`; capacity refusals remain typed resource errors. Failed linking preserves the metadata source identity, validated counts, selected binary path/architecture and its SHA-256 when observed, plus prior producer observations. It never publishes an incomplete native table as complete. The binary input is bounded to 1 GiB.
|
||||
|
||||
Scans yield cooperatively in bounded batches. Name-address retention and reverse-trie construction are each limited to65,536 entries/nodes and fallback module-array candidates to64 (`native-index-capacity`); exceeding capacity does not prove absence. Rendered type text has a64MiB UTF-8 cache-workspace guard, separate from the64MiB encoded-report admission, and64 nested rendering frames (`native-type-text-capacity` / `type-nesting-capacity`). Genuine cycles are malformed; unsupported type kinds are explicit coverage failures. Native row/text admission runs before allocation or retention. Unused generic parameter names are read on demand from the same stable metadata snapshot. There is no cross-inspection file cache; validated immutable Evidence is reused for session views.
|
||||
Scans yield cooperatively in bounded batches. Name-address retention and reverse-trie construction are each limited to 65,536 entries/nodes and fallback module-array candidates to 64 (`native-index-capacity`); exceeding capacity does not prove absence. Rendered type text has a 64 MiB UTF-8 cache-workspace guard, separate from the 64 MiB encoded-report admission, and 64 nested rendering frames (`native-type-text-capacity` / `type-nesting-capacity`). Genuine cycles are malformed; unsupported type kinds are explicit coverage failures. Native row/text admission runs before allocation or retention. Unused generic parameter names are read on demand from the same stable metadata snapshot. There is no cross-inspection file cache; validated immutable Evidence is reused for session views.
|
||||
|
||||
ELF RELATIVE writes into zero-fill are kept as a private virtual-pointer overlay. Unresolved supported-width symbol relocations, including RELA PLT entries, remain unknown; stored zeros never establish no-body. COPY, TLS descriptor, REL, packed APS2 and other unsupported relocation encodings fail closed. Input bytes are unchanged.
|
||||
ELF RELATIVE writes into zero-fill are kept as a private virtual-pointer overlay. Unresolved supported-width symbol relocations, including RELA PLT entries, remain unknown only for the bytes their ELF ABI relocation type covers; an adjacent 32-bit relocation does not make a separate 64-bit method slot unknown. Stored zeros never establish no-body. COPY, TLS descriptor, REL, packed APS2 and other unsupported relocation encodings fail closed. Input bytes are unchanged.
|
||||
|
||||
Native address queries batch exact64-bit hexadecimal `addresses` and `states` (`code`, `no-body`, `unknown`), intersecting indices and pagination. Numerically equal address spellings match; original row indices and every shared-body alternative survive:
|
||||
ELF relocation tables and file-backed destinations may span adjacent load ranges when every range agrees on the file translation. Any overlapping zero-fill declaration makes those bytes ambiguous; REA refuses the table or relocation destination instead of choosing the stored file bytes. Missing file bytes remain distinct from conflicting owners: supported unresolved relocations outside file-backed memory retain their unknown overlay, while ambiguous destinations refuse linking. Registration coverage uses the same file/zero-fill ownership checks as pointer reads.
|
||||
|
||||
Native address queries batch exact 64-bit hexadecimal `addresses` and `states` (`code`, `no-body`, `unknown`), intersecting indices and pagination; `generic_method_instances` accepts the same filters plus `method_indices` (generic definition indices). Numerically equal address spellings match; original row indices and every shared-body alternative survive:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -329,8 +473,11 @@ loaders place the binary: `0x100000` for an ELF linked at 0, and the link base
|
||||
for Mach-O and PE; `--seed-base 0x...` overrides it for a different import. When
|
||||
identical code folding gives several methods one address, the first method names
|
||||
the function and a `# folded <address> <kept> also <names>` comment line after
|
||||
the seeds lists the others, so the kept name is visibly one of several. Ghidra still names a seeded function that is a thunk after its
|
||||
target. The file is atomically published with exclusive creation; an existing destination is never replaced. Failed staging retains its cleanup owner for retry. Source digest and link/seed-base comments make the mapping inspectable. Unknown slots are skipped separately from observed no-body slots. `--seed-base` requires `--ghidra-seeds`; overflowing rebased addresses and names/outputs beyond the existing Ghidra protocol limits (2,000 name characters,32MiB file,1,000,000 entries) are refused before publication. The CLI and reusable service use one formatter and owned output workflow; ordinary MCP inspection remains read-only.
|
||||
the seeds lists the others, so the kept name is visibly one of several. With
|
||||
`--generic-instances`, linked generic instances are seeded after the method
|
||||
definitions under their Il2CppDumper-style names; an instance sharing an
|
||||
address with a definition or another instance is folded the same way. Ghidra still names a seeded function that is a thunk after its
|
||||
target. The file is atomically published with exclusive creation; an existing destination is never replaced. Failed staging retains its cleanup owner for retry. Source digest and link/seed-base comments make the mapping inspectable. Unknown slots are skipped separately from observed no-body slots. `--seed-base` requires `--ghidra-seeds`; overflowing rebased addresses and names/outputs beyond the existing Ghidra protocol limits (2,000 name characters, 32 MiB file, 1,000,000 entries) are refused before publication. The CLI and reusable service use one formatter and owned output workflow; ordinary MCP inspection remains read-only.
|
||||
|
||||
## Failures
|
||||
|
||||
@@ -354,16 +501,15 @@ message names one reason:
|
||||
| `nested-type-conflict` | A type is listed as nested in two types, in itself, or in a cycle of declaring types. |
|
||||
| `declaring-type-mismatch` | A method inside a type's method range names a different type, or a method lies outside its type's range. |
|
||||
| `image-range-overlap` | Two images claim the same type definitions. |
|
||||
| `invalid-string` | A name index does not start a string, or the name is unterminated or not valid UTF-8. |
|
||||
| `invalid-string` | A name index is outside the strings section, its string is unterminated, or it is not valid UTF-8. |
|
||||
|
||||
A version field that disagrees with the file's layout, for example a version
|
||||
39 file relabeled as 29, usually surfaces as `unexpected-header-size` or
|
||||
`record-size-mismatch`.
|
||||
|
||||
Two checks are stricter than the IL2CPP runtime: a name index must start a
|
||||
string, and an empty range's start must lie between -1 and the collection size.
|
||||
Every Unity-written file checked so far satisfies both; metadata rewritten by
|
||||
other tools may be rejected.
|
||||
An empty range's start must lie between -1 and the collection size, which is
|
||||
stricter than some runtime paths. String indices are byte offsets; a valid
|
||||
offset may select a suffix of another NUL-terminated name.
|
||||
|
||||
An incomplete inventory is never reported as complete. Failures after a verified
|
||||
snapshot retain partial source Evidence with its SHA-256, byte length and any
|
||||
@@ -383,10 +529,11 @@ report limit can still require the summary and paging path.
|
||||
|
||||
- Decrypting or deobfuscating protected metadata.
|
||||
- Linking Mach-O binaries that use chained fixups, ELF images with Android
|
||||
packed (APS2) relocations, and 32-bit images; generic method instances,
|
||||
adjustor thunks and invokers.
|
||||
packed (APS2) relocations, and 32-bit images; adjustor thunks and invokers;
|
||||
generic method instances for metadata versions 24 and 39 or builds without a static
|
||||
CodeRegistration.
|
||||
- Full method signatures beyond the rendered field, parameter and return type
|
||||
names, generic method instances, and type names for metadata version 27.
|
||||
names, and type names for metadata version 27.
|
||||
- Running Ghidra or another disassembler. `--ghidra-seeds` only writes a seed
|
||||
file for [`REA_GHIDRA_SEED_FILE`](installation.md); use the
|
||||
[native workflow](native-investigation.md) for native code.
|
||||
|
||||
+15
-14
@@ -47,22 +47,23 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
|
||||
|
||||
## Choose a guide
|
||||
|
||||
| Task | Guides |
|
||||
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [ELF notes](elf-notes.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
|
||||
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
|
||||
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
|
||||
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
|
||||
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
|
||||
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
|
||||
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
|
||||
| Capture and compare a command | [Process capture](process-capture.md) |
|
||||
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
|
||||
| Task | Guides |
|
||||
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [ELF notes](elf-notes.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
|
||||
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
|
||||
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
|
||||
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [APK signatures](apk-signature.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
|
||||
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
|
||||
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
|
||||
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
|
||||
| Capture and compare a command | [Process capture](process-capture.md) |
|
||||
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
|
||||
|
||||
For development, use [testing](testing.md), [client verification](client-verification.md),
|
||||
[tool design](tool-design.md), and [releasing](releasing.md).
|
||||
[tool design](tool-design.md), [admission limits and recovery](design/limits-and-recovery.md),
|
||||
and [releasing](releasing.md).
|
||||
[Reconstruction readiness](reconstruction-readiness.md) describes a development
|
||||
conformance verifier. [The 6.2 migration](migration-6.2.md) records that release's
|
||||
compatibility changes; [the roadmap](roadmap.md) tracks future work.
|
||||
|
||||
+274
-32
@@ -60,7 +60,10 @@ Before applying changes, REA checks your current configuration. The plan lists:
|
||||
|
||||
Malformed or unsafe existing configuration blocks the whole transaction before
|
||||
Hopper installation or any file write. Declining or pressing Ctrl-C makes no
|
||||
changes. Agent configuration writes preserve unrelated entries and comments,
|
||||
changes. Client configuration files and bundled skill files have separate
|
||||
publication and recovery paths; the guarantees in this paragraph apply to
|
||||
client configuration. See [skill destinations](#skill-destinations) for skill
|
||||
rollback limits. Agent configuration writes preserve unrelated entries and comments,
|
||||
create backups, stage changes in a private directory, and verify their result.
|
||||
Before publication, they refuse detected changes to the selected configuration.
|
||||
Replacement uses an explicit destination under the selected canonical parent;
|
||||
@@ -85,6 +88,11 @@ Setup also retains valid environment overrides in REA's existing server entry,
|
||||
such as `REA_MCP_INPUT_SCHEMA_PROFILE`, `REA_MCP_OUTPUT_SCHEMA_PROFILE`, and
|
||||
`REA_MCP_MAX_RESPONSE_BYTES`.
|
||||
Detected provider settings replace only matching environment keys.
|
||||
Setup manages REA's launcher, selected provider settings, and recommended timeout
|
||||
defaults. Codex approval policies, tool selections, and other caller-owned server
|
||||
settings are preserved. Known malformed policy values block setup before it
|
||||
writes the configuration or a backup; see
|
||||
[Codex settings preservation](client-configuration.md#codex-and-devin).
|
||||
Setup and doctor report a malformed REA environment map or invalid runtime
|
||||
setting before setup writes files. Repair the named setting in the client
|
||||
configuration; setup does not silently discard the rest of the map.
|
||||
@@ -104,9 +112,11 @@ your investigation. Failed steps and diagnostics remain in terminal history.
|
||||
|
||||
Select exact clients in scripts with repeatable `--client` flags. Each explicit
|
||||
client skips interactive selection. Use `--all-detected` only when you intend
|
||||
to configure every detected supported client. `--skill=false` omits the
|
||||
workflow, and `--dry-run` returns a read-only plan with status `planned` and
|
||||
exit code 0:
|
||||
to configure every detected supported client. Combining `--client` with
|
||||
`--all-detected` configures both the explicitly selected and detected clients,
|
||||
even when an explicit client is not detected. Each client is configured once.
|
||||
`--skill=false` omits the workflow, and `--dry-run` returns a read-only plan
|
||||
with status `planned` and exit code 0:
|
||||
|
||||
```bash
|
||||
rea setup --client codex --client cursor --skill=false --dry-run
|
||||
@@ -133,6 +143,15 @@ exits 1. Installing missing Hopper non-interactively additionally requires
|
||||
rea setup --yes --all-detected --install-hopper --json
|
||||
```
|
||||
|
||||
Client configuration writes detect changes to the file read by setup, including
|
||||
when a matching recovery backup already exists. A detected conflict reports
|
||||
`reason: "conflict"` and preserves the newer file; review it and rerun setup.
|
||||
Rollback likewise requires the file identity and content installed by that
|
||||
transaction. Requests for the same resolved path are serialized within one REA
|
||||
process. These checks do not provide an operating-system compare-and-swap or
|
||||
coordinate arbitrary external writers, so avoid editing client configuration
|
||||
while setup is applying it.
|
||||
|
||||
### Skill destinations
|
||||
|
||||
The bundled skill is installed in each selected client's personal skill directory.
|
||||
@@ -151,6 +170,26 @@ paths as `unmanaged`, names the affected paths, and provides those alternatives;
|
||||
an unreadable skill identity remains `unknown`. Uninstall retains and reports
|
||||
files it cannot safely remove, including their ownership manifest.
|
||||
|
||||
The skill installer preserves byte-exact backups of changed existing files,
|
||||
including invalid UTF-8, BOMs, and permission modes, and verifies each published
|
||||
file through its owned descriptor. Before publication it checks the selected
|
||||
file identity; first installation uses an exclusive hard link and refuses a
|
||||
concurrently created destination. Filesystems without hard-link support cannot
|
||||
publish a first-install skill file.
|
||||
|
||||
Rollback restores an existing file only while its published identity, state,
|
||||
and bytes still match that installation attempt. A recovery backup preserves
|
||||
the original bytes; it does not authorize overwriting a later user edit or
|
||||
replacement. Conflicts report the affected paths and recovery backups. If a
|
||||
newly created skill file cannot be verified, setup retains it and reports manual
|
||||
recovery rather than deleting by pathname. Staging cleanup failures remain
|
||||
owned and retryable, and setup does not report installation success while
|
||||
cleanup is unresolved.
|
||||
|
||||
These checks do not lock out another writer modifying the same inode or exclude
|
||||
pathname changes between the last check and publication. Avoid editing managed
|
||||
skill files or running another installer while setup applies them.
|
||||
|
||||
`rea setup --skill-client claude_code --yes --json` refreshes and verifies the
|
||||
selected skill destination independently of MCP registration selection. The
|
||||
final `doctor.scope.skill_clients` records which skill destinations were
|
||||
@@ -384,8 +423,13 @@ rea uninstall --purge-data
|
||||
```
|
||||
|
||||
Uninstall preserves Hopper, Node.js, Evidence files, captures, unrelated skills
|
||||
and other MCP servers. Purging removes only REA's cache and state under
|
||||
`~/.rea`. A client configuration that is malformed, unreadable, or at an unsafe
|
||||
and other MCP servers. Purging removes REA's legacy cache and state under
|
||||
`~/.rea`, the default Kuna cache (`~/Library/Caches/rea/kuna` on macOS or
|
||||
`${XDG_CACHE_HOME:-~/.cache}/rea/kuna` on Linux), and Linux Hopper release
|
||||
archives under `${XDG_CACHE_HOME:-~/.cache}/rea/hopper-releases`. Other cache
|
||||
namespaces remain intact. A caller-selected `REA_KUNA_CACHE_DIR` is retained
|
||||
and reported because REA cannot establish ownership of its entire contents.
|
||||
A client configuration that is malformed, unreadable, or at an unsafe
|
||||
path stops the operation before anything is removed, and a client that fails
|
||||
while being updated stops the remaining removals. Uninstall reports each
|
||||
candidate `configuration_path` with its outcome. A client with multiple files
|
||||
@@ -402,12 +446,65 @@ symbolic link is retained and reported rather than followed. Uninstall checks
|
||||
REA's managed shared and Claude Code personal skill locations. See the
|
||||
[CLI guide](cli.md#output-and-exit-status) for exit statuses.
|
||||
|
||||
### Missing dependencies in an npx installation
|
||||
|
||||
If REA exits with `ERR_MODULE_NOT_FOUND` before `doctor` can produce a report,
|
||||
inspect the missing path in the error. A path under npm's `_npx/<key>/node_modules`
|
||||
identifies a package-runner installation. In [#5259](https://github.com/morluto/rea/issues/5259),
|
||||
the missing file was `@modelcontextprotocol/client/dist/index.mjs`. That file is
|
||||
present in a clean Windows installation of REA 6.7.0; its absence alone does not
|
||||
establish an incompatible dependency release or explain how the file went missing.
|
||||
|
||||
`npm cache verify` checks npm's downloaded content cache; it does not restore
|
||||
missing files in an already extracted npx installation. Repeating the same npx
|
||||
command can reuse that incomplete installation. To reinstall only the affected
|
||||
entry:
|
||||
|
||||
1. Stop the REA processes and agent clients using that entry. In the same shell
|
||||
environment as the failing command, identify the cache and its entries:
|
||||
|
||||
```powershell
|
||||
npm.cmd config get cache
|
||||
npm.cmd cache npx ls
|
||||
npm.cmd cache npx info REA_CACHE_KEY
|
||||
```
|
||||
|
||||
Replace `REA_CACHE_KEY` with the key from the failing `_npx` path. Check that
|
||||
`info` reports that location and the expected `rea-agents` version. Its
|
||||
`valid` label describes the cache entry metadata, not the presence of every
|
||||
installed file. These subcommands require npm with `cache npx` support.
|
||||
|
||||
2. Remove that specific entry and rerun the affected version with registry
|
||||
access. For the version reported in #5259:
|
||||
|
||||
```powershell
|
||||
npm.cmd cache npx rm REA_CACHE_KEY
|
||||
npx.cmd --yes rea-agents@6.7.0 doctor --json
|
||||
```
|
||||
|
||||
Keep the key argument: this recovery does not require clearing all npm caches
|
||||
or deleting REA configuration, skills, or Evidence. On Linux/macOS, use `npm`
|
||||
and `npx` without `.cmd`. A JSON doctor report confirms startup recovered;
|
||||
it can still exit nonzero for missing analysis engines or registrations.
|
||||
Restart the affected agent after repairing its cache entry.
|
||||
|
||||
If a fresh installation still loses the file, retain the error, REA/Node/npm
|
||||
versions and installation details in the issue. Check installation logs and,
|
||||
on Windows, security software's protection history before attributing the cause.
|
||||
Do not rename `index.cjs` to `index.mjs` or disable protection to make startup pass.
|
||||
|
||||
### Update the owning installation
|
||||
|
||||
`rea update` installs the exact resolved
|
||||
release into the npm prefix or Bun global directory that owns the running
|
||||
package, then checks the new executable's version before reporting success. It
|
||||
does not reopen onboarding. For npm installations, release lookup and
|
||||
package, then checks the new executable's version before reporting success.
|
||||
That verification covers the CLI version; it does not establish readiness of
|
||||
optional native providers such as Frida. Updates keep dependency lifecycle
|
||||
scripts disabled, so a native binding that needs an install or build step may
|
||||
remain unavailable. A later provider command reports its own load failure;
|
||||
retain that diagnostic and the owning installation path before choosing a manual
|
||||
repair. The updater does not download or rebuild the binding. It does not reopen
|
||||
onboarding. For npm installations, release lookup and
|
||||
installation both use npm's configured registry. Bun global updates require
|
||||
Bun 1.2.17 or later, which introduced `bun info`. The updater checks `bun --version`
|
||||
first and stops with an instruction to upgrade Bun manually if the version is
|
||||
@@ -540,13 +637,18 @@ Hopper is separate commercial software with its own license. Its free demo has
|
||||
vendor-defined limits, and a paid license is optional. REA reuses any detected
|
||||
installation and preserves Hopper during uninstall.
|
||||
|
||||
The automated Hopper installer supports macOS 12+, Ubuntu 24.04+, Fedora 41+,
|
||||
Nobara 44+, 64-bit Arch Linux, CachyOS, or Omarchy. REA itself runs on any
|
||||
The automated Hopper installer supports macOS 12+ and x86_64 hosts running
|
||||
Ubuntu 24.04+, Fedora 41+, Nobara 44+, Arch Linux, CachyOS, or Omarchy. The
|
||||
official Linux packages are x86_64-only, so setup does not offer Hopper on other
|
||||
Linux architectures; configure [Ghidra](#ghidra) there instead. REA itself runs on any
|
||||
Linux distribution with a recognized apt, dnf, or pacman package family, where
|
||||
an existing Hopper launcher is selected through `HOPPER_LAUNCHER_PATH`. Ghidra
|
||||
and IDA have their own provider-specific host requirements; Windows Ghidra uses
|
||||
the [experimental P0 boundary](windows-ghidra-p0.md).
|
||||
|
||||
Hopper metadata and package downloads require HTTPS throughout the redirect
|
||||
chain; a redirect to HTTP is refused before that destination is requested.
|
||||
|
||||
On macOS, approved setup downloads the official DMG, checks its published size
|
||||
and digest, validates the application bundle, and atomically installs it to
|
||||
`~/Applications/Hopper Disassembler.app`. REA then opens Hopper so the
|
||||
@@ -636,10 +738,24 @@ manual dependency repair.
|
||||
|
||||
When the package-manager step fails, setup reports the failing command and its
|
||||
captured output, so a package conflict is distinguishable from an authorization
|
||||
failure. On CachyOS, an unversioned compatibility package such as
|
||||
`zlib-ng-compat` may conflict with Hopper's versioned `zlib` dependency. Resolve
|
||||
the package conflict yourself or install Hopper's official package manually,
|
||||
then rerun `rea setup`.
|
||||
failure. Hopper is optional, so setup still applies the approved client
|
||||
registrations and skill, then reports `needs_human` with the Hopper failure. On
|
||||
Arch Linux and CachyOS, the unversioned compatibility package `zlib-ng-compat`
|
||||
may conflict with Hopper's versioned `zlib` dependency. Resolve the package
|
||||
conflict yourself, install Hopper's official package manually, or use another
|
||||
provider such as Ghidra; rerun `rea setup` after installing Hopper.
|
||||
|
||||
If setup cannot finish releasing its resources, a normally returned structured
|
||||
result reports `needs_human` and includes `cleanup.reason` with the identified
|
||||
paths in `cleanup.resources`. A thrown typed failure reports the same retained
|
||||
paths in `details.resources`, preserving the primary failure separately. Reasons
|
||||
that do not identify a resource remain explicit without guessing paths from text.
|
||||
For in-process callers retaining REA's setup host, a successful retry of its close
|
||||
updates the original failure's cleanup state and removes released resource labels.
|
||||
Custom `SetupHost` implementations must bind returned or thrown cleanup errors
|
||||
to their resource owners to provide the same live recovery. An ownerless error
|
||||
remains an unconfirmed snapshot: `runSetup` does not observe later direct calls
|
||||
to the caller's port or replace that port's methods.
|
||||
|
||||
The supported Linux demo forwards additional launches into its existing
|
||||
application, even across private displays. REA therefore reserves one Linux
|
||||
@@ -673,8 +789,10 @@ Automatic discovery accepts executable regular files, including file symlinks,
|
||||
and skips directories.
|
||||
On Linux, it prefers executable `/opt/hopper/bin/Hopper`, then executable
|
||||
`~/.local/share/rea/hopper/bin/Hopper`, followed by `/opt/hopper-v4/bin/hopper`
|
||||
and `/usr/bin/hopper`; if none exists, the former remains the diagnostic
|
||||
fallback. `HOPPER_LAUNCHER_PATH` overrides those choices:
|
||||
and `/usr/bin/hopper`; if none exists, `/opt/hopper/bin/Hopper` remains the
|
||||
diagnostic fallback. `rea doctor` checks only the current platform's candidates, in the
|
||||
same order; other platforms have no automatic candidates.
|
||||
`HOPPER_LAUNCHER_PATH` overrides those choices:
|
||||
|
||||
```bash
|
||||
export HOPPER_LAUNCHER_PATH=/absolute/path/to/Hopper
|
||||
@@ -824,6 +942,11 @@ When Hopper cannot start in CI, run
|
||||
`rea doctor --provider hopper --json` in the failing runner.
|
||||
Structured failures distinguish private-display dependencies, an unsupported
|
||||
demo dialog or build, process-ownership conflicts, and an early lifecycle exit.
|
||||
If a private-display probe times out or is cancelled and its process cleanup
|
||||
fails, REA retains that exact probe owner and reports `cleanup_incomplete`.
|
||||
Session close retries the probe before releasing its cleanup marker; doctor
|
||||
uses its own retained resource scope for the same retry. A probe timeout whose
|
||||
process has already been released remains an ordinary startup diagnostic.
|
||||
Apply the reported remediation rather than exposing the runner's desktop,
|
||||
copying license secrets into logs, or killing unrelated Hopper processes.
|
||||
|
||||
@@ -857,6 +980,26 @@ dossiers, instructions, recovered data types, measured load mappings, loaded
|
||||
memory bytes, and observed file offsets. Independent load-image attestation
|
||||
supports DOS MZ, explicitly selected COM, and explicit raw-binary imports;
|
||||
PE returns measurements with an unsupported attestation status.
|
||||
ELF target opening checks the declared program and section header tables and
|
||||
their file-backed ranges before selecting a provider. A declaration beyond EOF
|
||||
is refused with its range and the actual file size, including a file cut one
|
||||
byte short. This also refuses carved or deliberately damaged copies that retain
|
||||
out-of-file declarations; they are not presented as complete smaller programs.
|
||||
Files declaring no tables remain valid inputs, subject to the provider's lane.
|
||||
Inactive headers, zero-length ranges and `SHT_NOBITS` memory-only sections do
|
||||
not require stored payload bytes. Extended counts come from section zero.
|
||||
The check streams header records in chunks of at most 64 KiB; it does not decode
|
||||
section names, copy payloads or impose the metadata decoder's structural budget.
|
||||
Passing it establishes physical range consistency, not complete function
|
||||
discovery, loader validity or runtime behavior. Existing ISA/ABI checks remain
|
||||
separate, including the bounded recordless MIPS lane.
|
||||
Target admission preserves recognized PE/NE/LE/LX signatures separately from
|
||||
reader layout support. A Windows header overlapping the first 64 DOS-header
|
||||
bytes remains an unsupported layout in this release; it is not reported as an
|
||||
invalid offset. Tiny PE files with short optional headers have additional
|
||||
unverified loader requirements. Unconfirmed nonzero DOS padding remains an
|
||||
unresolved Windows-header candidate, with independently validated DOS facts in
|
||||
the target error diagnostics; those facts do not prove a Windows header absent.
|
||||
On Linux and macOS, `annotate_native_function` also edits a function name and/or
|
||||
entry comments atomically and returns refreshed analysis, and `set_address_name`
|
||||
and `set_addresses_names` name a function entry or any other mapped address
|
||||
@@ -888,7 +1031,10 @@ selected formula's `libexec/openjdk.jdk/Contents/Home`, for example
|
||||
`/opt/homebrew/opt/openjdk/libexec/openjdk.jdk/Contents/Home` or
|
||||
`/usr/local/opt/openjdk/libexec/openjdk.jdk/Contents/Home`. Use a JDK inside the
|
||||
Java version bounds declared by your Ghidra installation; REA does not select
|
||||
another installed JDK automatically. Doctor distinguishes an absent Java
|
||||
another JDK when `JAVA_HOME` is explicit or the PATH JVM runs. On macOS,
|
||||
when the PATH launcher cannot start a JVM, it tries executable Homebrew
|
||||
`opt/openjdk` candidates, preferring the host architecture's prefix. Failed
|
||||
discovery probes remain available in installation diagnostics. Doctor distinguishes an absent Java
|
||||
command from a command that runs and fails, and retains bounded probe output
|
||||
and any parsed properties when the selected runtime cannot be admitted.
|
||||
|
||||
@@ -908,6 +1054,17 @@ installation remains unavailable on Windows. Setup never installs Ghidra,
|
||||
Java, or Python. It preserves valid detected Ghidra/JDK settings in agent
|
||||
registrations. See [Windows Ghidra P0](windows-ghidra-p0.md) for provider diagnostics.
|
||||
|
||||
The separate `rea mcp add` builtin currently uses Incur's package-runner child
|
||||
launcher, which can fail on Windows with `MCP_ADD_FAILED: spawn npx ENOENT`
|
||||
before registering a client. REA's setup path is unaffected. For a client supported
|
||||
by setup, review `rea setup --client opencode --skill=false --dry-run --json`
|
||||
(substitute its client ID), then use the same scoped command without `--dry-run`
|
||||
and approve the displayed plan. Non-interactive application additionally needs
|
||||
`--yes`. This configures setup's user-level client location; it does not replace
|
||||
`mcp add --no-global`, arbitrary `--command` registrations, or unsupported clients.
|
||||
The builtin launcher defect remains pending a dependency fix; a successful setup
|
||||
run does not verify that `mcp add` works on Windows.
|
||||
|
||||
When `GHIDRA_INSTALL_DIR` is unset, doctor and the Ghidra provider also search
|
||||
`PATH` for `ghidra` or `ghidraRun`, resolve the selected launcher through its
|
||||
real path, and accept it only when that location belongs to a Ghidra tree with
|
||||
@@ -923,9 +1080,23 @@ It also checks that the reported JVM `os.arch` and `os.name` map to the same nat
|
||||
directory validated for the host; unrecognized reported architectures or a
|
||||
non-host operating system are refused. LaunchSupport overrides of either
|
||||
property, including through helper-environment JVM option variables, are
|
||||
rejected at launch.
|
||||
rejected at launch. `JDK_JAVA_OPTIONS` argument files (`@file`) are expanded
|
||||
and inspected with that same check using the Java launcher's argfile quoting,
|
||||
comments and continuation rules. The reader admits regular UTF-8 files, retains
|
||||
failed descriptor cleanup, and bounds all admitted file contents and the prepared
|
||||
environment to 64 KiB per launch. Unreadable, oversized or nested `@` references
|
||||
are refused before the analysis JVM starts. Validated bytes are snapshotted under
|
||||
the owned runtime root, so later helpers and the analysis JVM do not reopen mutable
|
||||
caller files. Repeated references share that launch's snapshot; no live state is
|
||||
cached across launches. `JAVA_TOOL_OPTIONS`
|
||||
and `_JAVA_OPTIONS` do not expand `@file` tokens, so they remain
|
||||
literal-token checks.
|
||||
When Java is found through `PATH`, setup records its observed JDK home so GUI
|
||||
MCP clients do not depend on an incidental shell path. Setup shows every exact
|
||||
MCP clients do not depend on an incidental shell path. A caller-selected
|
||||
`JAVA_HOME` spelling is preserved when it resolves to that observed JDK. On
|
||||
macOS, a discovered Homebrew Cellar home uses the formula's stable `opt` alias
|
||||
only when both resolve to the same JDK. The Java check retains the JVM-reported
|
||||
home as evidence. Setup shows every exact
|
||||
environment entry in its plan, writes only after approval, and never downloads,
|
||||
installs, upgrades, or modifies Ghidra or Java.
|
||||
|
||||
@@ -1021,7 +1192,28 @@ One session contains exactly one imported Program; use `provider_id: "ghidra"`,
|
||||
One persistent decompiler is owned by the Program, and a serial queue keeps
|
||||
Ghidra API calls on the owning Program thread without a fixed queue length.
|
||||
Operations run until a result, caller cancellation, or provider shutdown; there
|
||||
is no fixed per-operation or response-size ceiling. Unresolved computed calls
|
||||
is no fixed per-operation or response-size ceiling. Cancelling an operation that
|
||||
is already running stops the Ghidra session, so one function whose
|
||||
decompilation runs past the MCP client's deadline costs the whole import.
|
||||
`REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS` (an integer from 1 to 2,147,483; absent by
|
||||
default, which leaves decompilation unbounded) bounds each native decompile
|
||||
instead. This maximum keeps Ghidra’s signed 32-bit seconds-to-milliseconds
|
||||
conversion from overflowing. The client must still support a deadline covering
|
||||
startup, that limit and operation headroom. Ghidra stops that one decompile and starts a fresh decompiler process
|
||||
for the next request. `analyze_function` still returns the function's assembly,
|
||||
references and basic blocks, with pseudocode, `native_api` and
|
||||
`native_value_flow` unavailable for the reason "exceeded the N-second limit",
|
||||
and that dossier is not saved to analysis snapshots. `procedure_pseudo_code`
|
||||
fails with `provider_timeout` and that message; other Ghidra tools stay
|
||||
available. If the caller also cancels while the timeout settles, the session
|
||||
still closes and the message says so. Session reuse concerns an active MCP or
|
||||
library session; a standalone CLI command still closes its own session when it
|
||||
finishes. The limit is read when a Ghidra session
|
||||
starts, and Codex/Kimi registrations derived by `rea setup` add it to the tool
|
||||
deadline as a single-decompile estimate. A bulk call can need that limit for
|
||||
every attempted function plus listing and file work; select the client deadline
|
||||
for the workload, split batches, use `decompile_all` limit/resume, or reduce
|
||||
`trace_native_values` `max_functions` to select fewer serial decompiles. Ghidra's own interactive default is 30 seconds. Unresolved computed calls
|
||||
remain unknown, reference-kind provenance is preserved, and provider-specific
|
||||
pseudocode is never treated as original source or Hopper-equivalent text.
|
||||
|
||||
@@ -1115,17 +1307,22 @@ override in `analyzer_preset` (for example
|
||||
open when its committed profile and the setting differ. A name starts with a
|
||||
letter or digit and contains only letters, digits, spaces, periods, underscores,
|
||||
and hyphens; at most 32 names are admitted, each at most 120 characters.
|
||||
Names must identify analyzers registered for the selected program. Unknown names
|
||||
or options that remain enabled abort the headless import before auto-analysis
|
||||
and the bridge, so an unsuccessful override cannot produce a session claiming
|
||||
that the analyzer was disabled. The setting applies to every import in the REA
|
||||
server. A format-specific analyzer may not be registered for another target, so
|
||||
the same override can prevent that target from opening. For mixed-target work,
|
||||
omit the override or use a separate REA server configured for the affected
|
||||
format. Changing the environment requires restarting the server; reopening a
|
||||
target alone keeps the current setting. For Windows PE resource analysis, the
|
||||
exact name is `WindowsResourceReference`. Disabling an analyzer skips the facts
|
||||
it would have produced.
|
||||
|
||||
The setting applies to every import in the REA server, but Ghidra registers
|
||||
format-specific analyzers, such as `Objective-C Message Analyzer` or
|
||||
`Demangler Swift`, only for programs they can analyze. A name that the Ghidra
|
||||
installation provides but did not register for the selected program is not
|
||||
applicable: the import continues, the profile still commits the configured
|
||||
names, and result limitations report the applied preset with that name marked,
|
||||
for example `ghidra-default+disabled:Objective-C Message Analyzer(n/a)`. A name
|
||||
that no installed analyzer provides, or an option that remains enabled, aborts
|
||||
the headless import before auto-analysis and the bridge with an error naming
|
||||
`REA_GHIDRA_DISABLED_ANALYZERS` and the analyzer, so a misspelled override
|
||||
cannot produce a session claiming that the analyzer was disabled. Changing the
|
||||
environment requires restarting the server; reopening a target alone keeps the
|
||||
current setting. For Windows PE resource analysis, the exact name is
|
||||
`WindowsResourceReference`. Disabling an analyzer skips the facts it would have
|
||||
produced.
|
||||
|
||||
### Ghidra heap and CPU controls
|
||||
|
||||
@@ -1292,7 +1489,7 @@ equivalent entry point. Setup run through `npx` registers
|
||||
`cmd.exe /d /c` so clients can start the runner's command shim. Neither
|
||||
registration points into the runner's temporary package cache.
|
||||
|
||||
REA supports Node.js 22.x (>=22.19), 24.x (>=24.11), and 26+. Node.js 23, 25, and prereleases are unsupported. It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
|
||||
REA supports Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), and 26.12.1+. Node.js 23, 25, earlier 26.x releases, and prereleases are unsupported. It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
|
||||
|
||||
When REA runs on Bun, the `rea doctor` `node` check reports the Bun version and
|
||||
the Node.js compatibility version Bun declares, and passes when that
|
||||
@@ -1327,6 +1524,26 @@ REA process and analysis state. Direct registrations follow the installed CLI;
|
||||
use `rea update` to update it. Running setup through `npx` again selects the pinned
|
||||
package-runner command. Do not register paths inside npm's temporary package cache.
|
||||
|
||||
### npm workspace errors before REA starts
|
||||
|
||||
An `npx` launch can inspect the caller's npm project before executing REA. For
|
||||
example, a direct dependency whose override conflicts with its declared spec
|
||||
can cause npm's `EOVERRIDE`, even when the requested REA package is already
|
||||
cached. This is a package-runner failure: REA has not started and cannot return
|
||||
an MCP error. [#3690](https://github.com/morluto/rea/issues/3690) reproduced it
|
||||
outside OpenCode as well as through that client's workspace-default launch.
|
||||
|
||||
Use the direct global-installation setup flow above to replace the affected
|
||||
package-runner registration, then restart the client. If REA is already installed
|
||||
globally, run that installed `rea setup --client <client-id> --dry-run`, review
|
||||
the direct launcher in the plan, and apply the approved scope. Installing a
|
||||
global copy alone does not change an existing registration. Keep the client's
|
||||
working directory: changing it to bypass npm inspection can change the meaning
|
||||
of relative target paths. A direct REA entrypoint preserves that directory
|
||||
without asking npm to resolve the workspace's dependency graph.
|
||||
|
||||
### Global installation wrapper
|
||||
|
||||
The optional curl wrapper installs only the global npm package:
|
||||
|
||||
```bash
|
||||
@@ -1342,4 +1559,29 @@ curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh |
|
||||
bash -s -- --dry-run
|
||||
```
|
||||
|
||||
To review a version-pinned installer before running it, select the release
|
||||
version, download its tagged script, and inspect the downloaded file:
|
||||
|
||||
```bash
|
||||
rea_version=6.7.0
|
||||
curl -fsSL "https://raw.githubusercontent.com/morluto/rea/rea-agents-${rea_version}/install.sh" -o rea-install.sh
|
||||
less rea-install.sh
|
||||
```
|
||||
|
||||
After reviewing it, install the same package version:
|
||||
|
||||
```bash
|
||||
bash rea-install.sh --version "$rea_version"
|
||||
```
|
||||
|
||||
The tag pins the selected script revision; `--version` selects the npm package
|
||||
version. Neither command verifies a signature on the downloaded script.
|
||||
|
||||
Supported options are `--version <semver>`, `--dry-run`, `--no-setup`, `--no-prompt`, and `--verbose`. Neither `--no-prompt` nor a non-interactive shell grants permission to install external dependencies.
|
||||
|
||||
Hopper setup distinguishes a confirmed occupied destination from a failed
|
||||
destination inspection. It also reports local archive staging and installed
|
||||
launcher inspection failures separately from download failure or launcher
|
||||
absence. Follow the reported operation, path, and available filesystem code; an
|
||||
unresolved inspection does not establish that a destination exists or a launcher
|
||||
is missing.
|
||||
@@ -76,12 +76,17 @@ identities even when their displayed values match. Partial identities also retai
|
||||
the source site: matching expression text at different sites does not establish
|
||||
the same runtime target. This records observed source syntax; it does not
|
||||
establish that the call ran or contacted that endpoint.
|
||||
Version comparison can align equal unresolved source sites and syntax across
|
||||
artifact changes, while target identity remains unknown. Equal unresolved text
|
||||
at different sites and partial versus exact values cannot establish a pair.
|
||||
Unresolved targets qualify endpoint absence only within the same endpoint kind;
|
||||
known metadata differences on an established pair remain visible.
|
||||
Graph coverage describes the analyzed artifact scope and resource limits, not
|
||||
runtime execution. A partial target alone does not imply omitted source.
|
||||
|
||||
The directed relation vocabulary is `contains`, `loads`, `imports`, `maps_to`,
|
||||
`exposes`, `sends`, `invokes`, `handles`, `calls`, `persists_to`, `observed_as`,
|
||||
and `changed_from`. Provider names are deliberately absent from both
|
||||
`exposes`, `sends`, `invokes`, `handles`, `calls`, `accesses`, `persists_to`,
|
||||
`observed_as`, and `changed_from`. Provider names are deliberately absent from both
|
||||
vocabularies.
|
||||
|
||||
For a `maps_to` edge targeting a source-map node, the edge carries
|
||||
@@ -158,6 +163,14 @@ module produced it. In particular:
|
||||
zero omissions;
|
||||
- complete coverage cannot be truncated or omit items.
|
||||
|
||||
Semantic graph validation enforces the same truncation coupling. Unknown
|
||||
omission extents remain `null`; positive omitted unknown occurrences can explain
|
||||
truncation even when omitted node and relation counts are both zero. Ordinary
|
||||
source unavailability, including invalid UTF-8, can make coverage partial without
|
||||
resource truncation. JavaScript summary `limitation_count` counts the full,
|
||||
deduplicated union of parent, application and semantic caveats, including messages
|
||||
whose delivery is omitted by the view's byte budget.
|
||||
|
||||
Unavailable values are represented explicitly rather than as missing fields,
|
||||
so absence of evidence cannot silently become evidence of absence.
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ artifact digest and structural graph ID. Semantic tracing requires the semantic
|
||||
relation graph and reports when it is unavailable rather than treating missing
|
||||
data as an empty graph.
|
||||
|
||||
Worker progress reports the source path and last observed parse, static,
|
||||
semantic, projection or transfer stage. These notifications do not measure
|
||||
stage CPU/time or establish the root cause of a slow or incomplete analysis.
|
||||
Semantic projection limits bound retained output, not upstream analysis work.
|
||||
|
||||
Explicit TypeScript `import type` and `export type` declarations remain source
|
||||
facts and do not create runtime import or export relationships. Type export names
|
||||
are reported separately from value export names. Inline `type` specifiers can
|
||||
@@ -40,6 +45,12 @@ combine its fields with the fact's location. Semantic trace results include the
|
||||
canonical context subset referenced by their returned facts, so those facts
|
||||
remain self-contained in the response.
|
||||
|
||||
Content-deduplicated module references match every retained observation path.
|
||||
A path-filtered reference reports the caller-selected path while keeping its
|
||||
shared node identity. Item selection also accepts any retained path when it
|
||||
identifies one module; if several module kinds share that path, select the
|
||||
reported `node_id`. Node-ID selection uses the stable representative path.
|
||||
|
||||
## Retained module facts
|
||||
|
||||
For canonical identities and retained source-range counts, query the existing
|
||||
@@ -149,7 +160,10 @@ matching seeds and the complete graph reachable in the selected direction.
|
||||
The result contains the matching basis, complete reachable subgraph, terminal
|
||||
paths, authority summaries, and native handoffs. A handoff binds
|
||||
the exact native artifact digest and requested exports from the application
|
||||
graph. Existing Hopper or Ghidra Evidence is linked only when its subject digest
|
||||
graph. Trace direction selects the reachable addons; requested
|
||||
exports for each selected addon come from the complete supplied graph, so an
|
||||
outgoing trace does not discard an incoming export association. Existing Hopper
|
||||
or Ghidra Evidence is linked only when its subject digest
|
||||
matches exactly. Otherwise the result recommends provider-neutral follow-up
|
||||
tools and reports `requires-provider-analysis`; it never starts or switches a
|
||||
provider implicitly.
|
||||
@@ -183,6 +197,19 @@ registrations/removals and dispatch, Node timers and cancellation handles, async
|
||||
`node:child_process` creation and ownership, configuration sources and direct
|
||||
defaults, request construction and response consumers, parse/coercion/
|
||||
validation boundaries, and built-in resource acquisition/release.
|
||||
Reassignments connect retained right-hand reads to the binding or property
|
||||
write. Compound assignments also retain the previous-value input; logical
|
||||
assignments keep conditional inputs as candidates. These edges describe
|
||||
influence without evaluating arithmetic or establishing execution order or the
|
||||
resulting value. Deferred callback bodies do not become inputs to the enclosing
|
||||
assignment. Computed method keys belong to the enclosing callable; parameter
|
||||
defaults and method bodies belong to the method. Uncertain later property reads
|
||||
remain opt-in through `include_ambiguous_dynamic_edges`.
|
||||
Global timer, `fetch`, `WebSocket`, `JSON.parse`, and coercion calls receive
|
||||
these built-in effects only when the file has no visible possible write to that
|
||||
global, including a computed write through a global object. The check does not
|
||||
evaluate order or reachability, so a later or guarded polyfill write also
|
||||
removes the resolved effect.
|
||||
|
||||
Local callback candidates do not establish a complete target set when a
|
||||
conditional write may leave a parameter, catch binding, or uninitialized
|
||||
@@ -284,6 +311,14 @@ change hashed event IDs and longer event/listener labels; query the preserved
|
||||
name or select IDs from the parent graph. Previously stored graphs keep their
|
||||
original IDs and remain valid trace inputs.
|
||||
|
||||
Empty string event names remain exact identities and can be selected with an
|
||||
event seed whose `name` is `""`. A property seed selects the named slots and
|
||||
retained accesses whose producer established that no slot was resolved. Those
|
||||
accesses keep their source locations and unknowns; a missing retained relation
|
||||
does not prove that an access is slotless. New access nodes record
|
||||
`property_slot_resolution` independently of whether capacity permitted retaining
|
||||
the resolved slot node or its relations.
|
||||
|
||||
## Version comparison
|
||||
|
||||
REA pairs entities only when a tier produces one unique candidate on each side.
|
||||
@@ -320,6 +355,11 @@ remain `changed` while retaining any separate relationship uncertainty.
|
||||
An unknown item with no observed content, property, or relationship difference
|
||||
does not emit a `changed_from` edge; its pairing and uncertainty remain in the item.
|
||||
|
||||
Within a valid `file_occurrence`, `container_sha256` records provenance and does
|
||||
not by itself count as a semantic property change. The graph retains the original
|
||||
occurrence; its path and source digest still participate in comparison.
|
||||
Unrecognized occurrence metadata remains part of property comparison.
|
||||
|
||||
One-sided absence is `added` or `removed` only when the opposite input graph has
|
||||
complete coverage. With partial, unavailable, or truncated input, the same
|
||||
condition is `unknown`. The comparison retains every classified item and
|
||||
@@ -342,9 +382,16 @@ files; multiple canonical-equivalent candidates remain unknown.
|
||||
|
||||
Each historical source file is classified as `unchanged`, `modified`,
|
||||
`removed`, `split`, `merged`, `duplicated`, or `unknown`. Multiple equally
|
||||
scored path mappings form a static split inference. Multiple current
|
||||
nodes with the exact historical digest are duplicated; multiple historical
|
||||
paths with exact identity to one current node are merged. `removed` requires
|
||||
scored path mappings form a static split inference. Multiple current file
|
||||
occurrences with the exact historical digest are duplicated; multiple historical
|
||||
paths with exact identity to one current occurrence are merged. Fresh file
|
||||
observations carry their container digest, inventory path, and content digest.
|
||||
Their asset, source-module, and export-binding representations form one candidate,
|
||||
which retains `current_file_occurrence` and `representation_node_ids`. Equal bytes
|
||||
at two inventory paths remain two occurrences even when they share an asset node.
|
||||
Logical bundle modules and older observations without occurrence facts retain
|
||||
their graph identities; matching does not infer common ownership from a label.
|
||||
`removed` requires
|
||||
complete historical and application inventories. The comparison analyzes and
|
||||
returns all source files, candidate nodes, and matching signals represented in
|
||||
the supplied graphs. Partial inventories remain unknown where absence cannot
|
||||
@@ -357,12 +404,36 @@ equivalence.
|
||||
name on each authenticated graph. Missing or duplicate exact selectors return
|
||||
candidate inventory and an unknown result; the tool never chooses a fuzzy
|
||||
match. An export is linked to a callable only through exact lexical/module
|
||||
relationships recovered by the AST analysis.
|
||||
relationships recovered by the AST analysis. A later named CommonJS deletion
|
||||
invalidates the earlier publication per exported field at the receiver owner;
|
||||
surviving fields from the same assignment stay available. Descriptor-defined
|
||||
fields retain unknown deletion success unless configurability is established.
|
||||
Conditional or
|
||||
dynamic deletions retain uncertainty. Unresolved invocation spreads remain
|
||||
unknown source sequences and cannot publish a phantom array index.
|
||||
|
||||
Direct return expressions, including expression-bodied arrows, are evaluated
|
||||
through an execution-free value lattice. Literal object fields and direct
|
||||
return sites are represented in the result. Calls, dynamic spreads, computed
|
||||
keys, and parser recovery remain partial or unknown.
|
||||
keys, and parser recovery remain partial or unknown. Known conditional and
|
||||
logical tests select only the established branch; uncertain tests retain
|
||||
alternatives. Object copies preserve known own primitive data fields at copy time while
|
||||
unknown sources retain incomplete coverage. Array holes do not overwrite own
|
||||
fields during a copy. Copied reference fields keep explicit uncertainty; shared
|
||||
reference projection remains responsible for their later member reads.
|
||||
Declaration initializers and later assignments share the binding-at-read owner;
|
||||
a declaration-only `var` read before its initializer does not inherit the future
|
||||
value. Earlier `break`/`continue` paths can leave a var initializer unexecuted.
|
||||
Legacy non-strict block-function copies are indexed on their outer variable
|
||||
owner, while strict/module and lexical-shadowing boundaries keep their values.
|
||||
A catch-shadowed var declaration keeps declaration ownership separate from its
|
||||
initializer's lexical assignment, including object and array destructuring slots.
|
||||
Projected initializer facts retain their source and alias identity on the lexical
|
||||
recipient, so a later mutation through that alias prevents reporting the original
|
||||
object field as an unchanged constant. Ordinary functions own an implicit
|
||||
arguments object; arrows retain lexical arguments lookup. Promise source identity uses
|
||||
the same read-position selection and requires an unmodified intrinsic factory.
|
||||
|
||||
Rebound values are evaluated at their read position: saving an incoming parameter
|
||||
before a later assignment preserves caller-dependent uncertainty, while a proven
|
||||
preceding assignment can supply a constant. Unresolved ordering remains unknown.
|
||||
@@ -401,8 +472,60 @@ when the object is an array element, nested inside mixed object/array
|
||||
containers, or assigned to a previously declared identifier. Saving the getter
|
||||
result in an alias does not make its old field values immutable. Known array
|
||||
indices include holes; an element after a spread has only a minimum possible
|
||||
index. Unrelated properties and known array prefixes remain precise, and a
|
||||
getter read without a write does not invalidate the receiver's fields.
|
||||
index. For these returned-reference writes, unrelated properties and known
|
||||
array prefixes remain precise. Accessor applicability uses the shared object
|
||||
reference owner for literal definition order, own-property shadowing, known
|
||||
array elements and explicit literal prototypes. Getter invocation effects
|
||||
project write and call operands through the same local alias and `this` owner
|
||||
used by returned references. Reads of another selected getter continue that
|
||||
effect walk. A receiver reached by these effects becomes uncertain on an
|
||||
ordinary or optional read, compound assignment or update. A simple assignment
|
||||
or deletion does not invoke the getter. Selected setters invalidate
|
||||
the receiver and its shared children on assignment, including through known
|
||||
array and prototype paths. Supported class descriptors use the same inherited
|
||||
field and own-property shadowing rules as class reference selection. Inert
|
||||
getters and ordinary data slots retain their supported precision. Exposing a
|
||||
constructed receiver also exposes its retained shared field references, using
|
||||
the same reference owner as selected getter effects.
|
||||
|
||||
Object spreads, rest copies and destructuring obtain their property reads from
|
||||
the shared pattern-source owner. Copies read own enumerable properties;
|
||||
ordinary destructuring can invoke an inherited getter. Operations requiring
|
||||
primitive conversion conservatively expose their conversion operands, including
|
||||
computed keys, arithmetic and untagged template interpolation. `using` and
|
||||
`await using` expose the retained resource at its lexical scope exit; earlier
|
||||
scalar reads retain their capture timing, including reads before a later scope's
|
||||
disposal. Repeated loop iterations can expose
|
||||
a previous disposal and therefore retain uncertainty. Private getter fields
|
||||
retain their parsed initializer references; `super` reads use the selected
|
||||
receiver conservatively, including superclass reads whose lookup and receiver
|
||||
authority differ. Custom `instanceof` hooks expose their receiver and argument.
|
||||
Known unmodified intrinsic Symbol keys cannot overwrite string properties or
|
||||
invoke primitive conversion; their omitted symbol-slot inventory stays partial.
|
||||
Shadowed or visibly replaced Symbol references retain unknown key effects.
|
||||
These are source-derived effects,
|
||||
not execution results or proofs of accessor purity. Reflectively installed
|
||||
descriptors remain opaque; their setup calls retain ordinary argument/receiver
|
||||
escape uncertainty. External prototype changes and unsupported runtime protocols
|
||||
remain unknown rather than supplying exact post-invocation values.
|
||||
Identifier references and closure captures share Babel syntax-role facts plus
|
||||
explicit binding/write roles; noncomputed declaration keys and import/meta names
|
||||
are not lexical reads. Provenance-only expression-depth omissions enter the same typed source coverage
|
||||
as value-evaluation bounds and remain visible in retained application Evidence.
|
||||
Immediate synchronous function results also share the loader reference owner.
|
||||
An expression arrow returning the retained CommonJS `require` can preserve that
|
||||
loader identity. Possible loader results with opaque alternatives or block
|
||||
bodies retain candidate uncertainty; async and generator results are their
|
||||
wrapper values.
|
||||
This does not interpret `typeof` or Proxy behavior in bundler shims. Historical
|
||||
source imports consume the same proven loader fact and keep their existing
|
||||
requirement for a known loader association.
|
||||
|
||||
Direct generator and async calls produce iterators and
|
||||
Promises; body returns do not become direct call-result flows. Their unresolved
|
||||
consumption remains an explicit frontier. Replaced emitter bindings select their
|
||||
initializer lifetime at each operation. A lexical `this` owner remains partial
|
||||
because it does not prove one runtime instance.
|
||||
Nested callable returns are not assigned to their parent callable. Projected
|
||||
graph observations carry source ranges but never source text.
|
||||
|
||||
@@ -416,7 +539,9 @@ Changes use JSON Pointer paths with `added`, `removed`, `changed`, or `unknown`
|
||||
status and a separate `presence` object (`present`, `absent`, or
|
||||
`unknown-coverage`) on each side. A name is `added` or `removed` when one paired
|
||||
shape proves presence and the other proves absence. Complete parent-property
|
||||
coverage can establish absence even when field values stay unresolved. Identical literals are
|
||||
coverage can establish absence even when field values stay unresolved. Proven
|
||||
absence of an ancestor also establishes absence of its descendants. An observed
|
||||
ancestor with unresolved contents keeps its descendants unknown. Identical literals are
|
||||
omitted. Unresolved values that remain on both sides stay `unknown` when the
|
||||
projections differ, and are omitted when presence is unchanged and the unknown
|
||||
projections match. Incomplete spreads and other partial parent coverage keep
|
||||
|
||||
@@ -60,6 +60,11 @@ The result retains the canonical local path, root artifact digest, artifact
|
||||
manifest and graph commitments, JavaScript Application Graph, static
|
||||
Electron summary, reconstruction statistics, and explicit limitations. It does
|
||||
not require a live Hopper, Ghidra, browser, or Electron process.
|
||||
Selected directories require read and search access. A denied root remains a
|
||||
typed host access failure; omission handling for denied descendants does not
|
||||
make an inaccessible selected root analyzable. Other host filesystem failures
|
||||
retain the selected input, reported errno and syscall/path when supplied, and
|
||||
the original diagnostic separately from artifact-format failures.
|
||||
|
||||
The application graph selects the shallowest `package.json` outside
|
||||
`node_modules` as its package root, breaking equal-depth ties by Unicode
|
||||
@@ -96,15 +101,44 @@ caller's request and first-occurrence priority, analyzing the file once.
|
||||
Omit the selection for the existing complete
|
||||
application workflow.
|
||||
|
||||
For a long selection, pass the paths as a JSON array with
|
||||
`--source-paths-json`, either inline or as a JSON file path. A file keeps the
|
||||
command below host command-line limits, such as the 8,191-character limit of
|
||||
the `cmd.exe` shim that `npx` uses on Windows. Its entries follow any
|
||||
`--source-path` values and stay artifact-relative; they are not resolved
|
||||
against the working directory:
|
||||
|
||||
```bash
|
||||
rea analyze-javascript-application /absolute/path/to/app.asar \
|
||||
--source-paths-json /absolute/path/to/paths.json --json
|
||||
```
|
||||
|
||||
The result and summary retain `source_selection.requested_paths` and
|
||||
`source_selection.unselected_files`. Every relevant file keeps its original
|
||||
inventory identity; unselected files report `text_status:
|
||||
`source_selection.unselected_files`. Selecting every relevant file leaves this
|
||||
count at zero and adds no unselected-source caveat. Every relevant file keeps its
|
||||
original inventory identity; unselected files report `text_status:
|
||||
"outside-analysis-scope"`. Their relationships remain unknown, and application
|
||||
coverage is partial while any file remains outside scope. Selection does not
|
||||
claim whole-application completeness or change integrity policy. Summary detail
|
||||
only reduces delivery; source selection reduces text acquisition and analysis.
|
||||
Explicit native provider or snapshot routing rejects `--source-path` rather
|
||||
than ignoring it.
|
||||
Explicit native provider or snapshot routing rejects `--source-path` and
|
||||
`--source-paths-json` rather than ignoring them.
|
||||
|
||||
For directory input, a permission-denied child file remains an unavailable
|
||||
inventory occurrence, and an unreadable child directory records an unknown
|
||||
descendant omission. Readable sources remain available; application coverage is
|
||||
partial and retains those inventory limitations. An unreadable selected root
|
||||
remains an error. Explicit readers that require selected child bytes keep their
|
||||
access errors rather than treating denied paths as absent.
|
||||
Unavailable source files also retain their applicable per-file processing gap
|
||||
in the application graph. Their container inventory identity anchors that gap;
|
||||
REA does not invent a content digest for bytes it could not read.
|
||||
Incomplete traversal also qualifies negative path and package-metadata lookups:
|
||||
an unobserved earlier loader candidate cannot be skipped in favor of a later
|
||||
observed candidate. An explicit source selection whose discovery is unknown
|
||||
returns an unavailable error with the retained inventory, rather than claiming
|
||||
the selected source does not exist. Observed exact file candidates keep their
|
||||
identity; unknown traversal cannot establish a unique application root.
|
||||
|
||||
ASAR header/index admission defaults to 16 MiB / 16777216 bytes. Set
|
||||
`REA_ASAR_MAX_HEADER_BYTES` in the environment that starts REA to a decimal
|
||||
@@ -177,9 +211,24 @@ An unpacked entry whose companion bytes were not supplied remains
|
||||
`unavailable`. REA continues analyzing embedded JavaScript and records the
|
||||
missing native/resource bytes as unknown. The occurrence keeps
|
||||
`hash_status: unavailable`; no content-addressed child artifact is created for
|
||||
those missing bytes. See [MCP integrity handling](mcp-contracts.md#integrity-record-and-continue)
|
||||
those missing bytes. New inventories preserve an ASAR companion declaration as
|
||||
`unpacked: true`; an omitted flag in an older inventory does not establish that
|
||||
unavailable bytes belonged to a companion. See [MCP integrity handling](mcp-contracts.md#integrity-record-and-continue)
|
||||
for the tool-result contract.
|
||||
|
||||
JavaScript whose syntax cannot be parsed retains unavailable semantic coverage
|
||||
with `incomplete-module` unknowns for each semantic family and unknown omitted
|
||||
node and relation extents. The source module is retained when node capacity
|
||||
permits. A parser failure alone does not imply resource truncation or a named
|
||||
resource limit. Decoding failures and deliberately
|
||||
unselected sources retain the same explicit semantic coverage gap.
|
||||
|
||||
HTML whose bytes are not valid UTF-8 retains its file observation and an unknown
|
||||
`parse-html-script-references` scope with artifact-byte authority. Reference
|
||||
discovery was unavailable; zero retained script links does not establish that
|
||||
the unreadable HTML declared none. Readable HTML with no references has no such
|
||||
gap, and readable external references retain their load edges.
|
||||
|
||||
## Large results
|
||||
|
||||
Use MCP `detail: "summary"` for a large application, then read selected views of
|
||||
@@ -219,6 +268,16 @@ waiting before receiving the reply, follow [MCP cancellation recovery](mcp-contr
|
||||
to find the record. CLI failure output contains the complete partial Evidence.
|
||||
Owned analyzer cleanup is verified before private files are released; uncertain
|
||||
cleanup retains ownership for retry and is reported separately.
|
||||
Typed cancellation or another primary interruption remains attached while
|
||||
cleanup is incomplete. Confirmed release restores that primary classification,
|
||||
including the caller's exact cancellation reason, while retaining partial
|
||||
application Evidence and historical cleanup diagnostics.
|
||||
|
||||
Private worker storage failures retain the source identity, worker phase and
|
||||
reported I/O message as execution failures; they are distinct from malformed
|
||||
worker output. A per-source temporary-file cleanup failure preserves static and
|
||||
module facts together with any parent application-projection limit. Cleanup
|
||||
failure does not authorize expanding relationships that parent admission declined.
|
||||
|
||||
## What is reconstructed
|
||||
|
||||
@@ -249,10 +308,15 @@ utility-process runtime paths retain the runtime filename selected by the caller
|
||||
Selected bounded text is then parsed as inert data to recover:
|
||||
|
||||
- `package.json` metadata and declared main or renderer entrypoints;
|
||||
- Electron preload paths and renderer files visible in static syntax;
|
||||
- Electron preload paths owned by proven BrowserWindow options and renderer
|
||||
files loaded through proven BrowserWindow instances;
|
||||
- local HTML script entrypoints;
|
||||
- Webpack and Rspack chunk registrations and module factories represented as
|
||||
AST literals;
|
||||
AST literals, plus esbuild `__commonJS` module tables; recovered esbuild
|
||||
CommonJS exports use its factory's `exports`-then-`module` parameter order.
|
||||
Export receivers and loader helpers retain those calling-convention roles only
|
||||
while the binding at the read still holds its original parameter value;
|
||||
replacing a parameter does not give the replacement object that role;
|
||||
- static imports, dynamic imports, CommonJS `require` calls, workers, and
|
||||
service workers (only `new Worker(new URL(path, import.meta.url))` resolves
|
||||
relative to the calling module; string worker and service-worker paths use
|
||||
@@ -296,9 +360,11 @@ RPC client syntax also contributes network endpoint candidates, in both
|
||||
initializer and no binding assignments; shadowed locals do not inherit client
|
||||
identity. `subscribe` additionally requires a tRPC observer
|
||||
argument (`onData`, `onError`, `onStarted`, `onStopped`, `onComplete`, or
|
||||
`onConnectionStateChange`). `this` roots, browser globals, and any path
|
||||
containing an RTK Query `endpoints` segment are never procedures; cache-key
|
||||
helpers such as `queryKey` issue no request and are excluded.
|
||||
`onConnectionStateChange`). `this` roots and unshadowed browser globals are
|
||||
excluded. A lexical client can be named `history`, and its router segments can
|
||||
use browser-global spellings. RTK Query `endpoints` chains are excluded only
|
||||
when the root has no recognized tRPC client binding. Cache-key helpers such as
|
||||
`queryKey` issue no request and are excluded.
|
||||
- `trpc:link:<link>` records the literal `url` option of `httpLink`,
|
||||
`httpBatchLink`, `httpBatchStreamLink`, and `httpSubscriptionLink` when the
|
||||
link's callee name survives bundling, including `(0, module.httpBatchLink)`
|
||||
@@ -329,6 +395,27 @@ recorded path keeps those leading carrier segments; the procedure path is its
|
||||
suffix. Utility calls such as `useUtils().invoice.list.fetch()`, `useQueries`,
|
||||
and links whose callee was renamed by a minifier are not recognized.
|
||||
|
||||
Bundler adapters own callback roles: Webpack supplies module, exports, then
|
||||
loader; esbuild CommonJS supplies exports, then module. Export extraction uses
|
||||
those binding identities for assignments and declarations, so renaming either
|
||||
parameter does not change the recovered names. Global property-definition
|
||||
intrinsics require lexical and visible-write proof. Factory export declarations
|
||||
also require the loader's `d` member to have no prior or unordered visible
|
||||
replacement, including writes through lexical aliases. Unrelated loader members
|
||||
do not invalidate declarations. Ordering describes declarations within one factory
|
||||
activation; helper integrity on entry, effects of earlier invocations, and
|
||||
arbitrary call side effects remain unproven.
|
||||
Computed writes through a standalone loader's exposed module/cache table use
|
||||
the shared value owner; an unknown key invalidates the original-slot proof
|
||||
rather than implying a fresh chunk installation. A function stored on a
|
||||
standalone loader member does not write the table merely by being defined:
|
||||
the bootstrap owner excludes its deferred writes only when that member is
|
||||
unread and the loader namespace stays confined. Calls, member aliases, dynamic
|
||||
member reads, namespace escapes, or
|
||||
receiver-based access retain uncertainty about those writes. Export names
|
||||
describe recognized declarations, not a proof
|
||||
of final runtime property lifetime or arbitrary helper behavior.
|
||||
|
||||
Each recovered bundle module retains the exact factory-source digest. A complete
|
||||
bounded AST also receives a `babel-ast-v3` structural fingerprint. It ignores
|
||||
ordinary identifier spellings while retaining literal and directive values,
|
||||
@@ -355,6 +442,22 @@ prefix is never presented as a complete stable fingerprint.
|
||||
|
||||
## Authority and unknowns
|
||||
|
||||
API identity is a derivation: a familiar method or property name does not
|
||||
establish a browser or Electron API. The shared lexical, ambient-write, and
|
||||
member-mutation owners must establish the receiver before static API facts become
|
||||
roles or storage observations. Path `join`, `resolve`, and `fileURLToPath` use
|
||||
proven Node imports; source-relative URL conversion additionally requires an
|
||||
unmodified global or proven Node `url.URL` constructor and `import.meta.url`.
|
||||
Native constructor aliases retain that identity; their local spelling does not
|
||||
determine URL semantics, and replaced exports or local constructors remain unknown.
|
||||
Relative results of native path operations retain filesystem-expression context;
|
||||
their spelling does not turn them into module specifiers. Other calls preserve
|
||||
their expressions. A property named `preload` outside a proven BrowserWindow
|
||||
configuration does not establish a script entrypoint. Renderer recovery currently
|
||||
covers direct BrowserWindow instances, stable local aliases, and their
|
||||
`webContents`; other receiver-producing APIs remain unmodeled. Complete AST
|
||||
traversal does not imply complete API-model coverage.
|
||||
|
||||
Artifact bytes and AST syntax are observations. Entrypoint resolution, imports,
|
||||
loads, calls, and persistence relationships are static inferences and explicitly
|
||||
say that syntax does not prove runtime execution. A malformed or unavailable
|
||||
@@ -392,6 +495,11 @@ observation, because callers outside the traced paths may pass other values.
|
||||
A wrapper call that is both its own entry and reached through another entry
|
||||
is recorded once when both paths find the same facts; calls that supply
|
||||
different handlers or channels stay distinct.
|
||||
A contextBridge exposure reached through wrapper calls records one API per key
|
||||
those invocations supply, each with its own bounded member paths. Invocations
|
||||
that supply the same key to one exposure call describe one API covering each
|
||||
alternative object. A traced invocation whose key, API object or world stays
|
||||
unresolved or guarded keeps the call's own unresolved observation.
|
||||
Unknown spreads, reassigned bindings, accessors,
|
||||
dynamic channels and unsupported syntax do not become exact implementations.
|
||||
This pass neither executes the bundle nor proves that runtime guards pass.
|
||||
@@ -483,6 +591,14 @@ candidate. Dynamic channels are never paired, and multiple compatible handlers
|
||||
are reported as ambiguous without adding a caller-to-handler edge. A matching
|
||||
channel still does not prove registration order, reachability, or runtime use.
|
||||
|
||||
Native-addon call bindings consume the same loader identity and normalized
|
||||
arguments as dependency analysis, including optional calls. Local methods or
|
||||
parameters named `require` establish no native binding; `require.resolve` records
|
||||
lookup rather than a native namespace. CommonJS re-export bindings require a
|
||||
selected export value and an applicable shared export receiver. Arithmetic and
|
||||
logical compound stores keep their dependency observations without asserting that
|
||||
the addon is the selected exported value.
|
||||
|
||||
For `.node` bindings, member names mean “requested by JavaScript syntax.” A
|
||||
resolved add-on path does not convert them into verified native exports. Native
|
||||
symbol verification remains a separate deep-analysis claim.
|
||||
@@ -508,6 +624,11 @@ inventory digest before parsing, and malformed ASAR operations return typed
|
||||
format diagnostics that retain the local container path. Native add-ons are
|
||||
represented by metadata only.
|
||||
|
||||
POSIX directory names are read as raw bytes and admitted as logical paths only
|
||||
when they round-trip through UTF-8. Invalid names are left untouched, retained
|
||||
siblings remain inventoried, and traversal coverage is marked unknown with the
|
||||
first rejected basename bytes identified in hexadecimal.
|
||||
|
||||
## Bounds and coverage
|
||||
|
||||
The reconstruction keeps internal resource safeguards for artifact entries,
|
||||
@@ -631,6 +752,13 @@ execution and memory limits continue to determine examined coverage. Raising onl
|
||||
a node capacity does not raise the unknown capacity. Graphs without unknown
|
||||
omissions omit this optional coverage field, preserving their existing identities.
|
||||
|
||||
These node and unknown capacities bound retained graph projection. Parsing,
|
||||
static analysis and semantic fact construction precede that projection for an
|
||||
admitted source. Lowering the capacities can reduce projection and retention
|
||||
work, but does not bound upstream semantic-analysis time or memory. Select fewer
|
||||
`source_paths` to reduce the sources analyzed; heap and deadline controls set
|
||||
separate resource failure boundaries with the runtime-specific limits above.
|
||||
|
||||
While sources remain, the sequential worker reserves one next compact static
|
||||
checkpoint at its 8 MiB bound with the same 6x retention expansion used at
|
||||
admission. Earlier checkpoints are already charged to retained bytes; the
|
||||
@@ -751,6 +879,24 @@ URL targets before selecting a later package-local file; an unsupported bare
|
||||
package or recursive alias keeps its unresolved outcome rather than selecting
|
||||
a later fallback.
|
||||
|
||||
For a proven `createRequire(new URL(relativeFile, import.meta.url))` call,
|
||||
relative `require()` edges use the selected filename's directory and retain the
|
||||
source-relative `resolution_base_path` on the graph edge. This follows Node's
|
||||
`createRequire(filename)` contract; the path is a loader-resolution fact and
|
||||
does not change the source file or call-site Evidence identity. If the filename
|
||||
cannot be proven, REA keeps the require target unresolved rather than silently
|
||||
resolving it from the caller's directory.
|
||||
|
||||
Module-reference graph edges, including native-addon `loads` edges, retain
|
||||
`properties.operation`: `load` identifies
|
||||
an import or loader invocation that may execute the target; `resolve` identifies
|
||||
a proven `require.resolve()` lookup that discovers a target without executing it.
|
||||
Both retain the exact specifier, resolution mode, selected resolution base, and
|
||||
call-site Evidence. Resolve-only edges do not propagate application ownership or
|
||||
create executable-import gaps when their targets are missing. A modified
|
||||
`.resolve` member remains an uncertain `load`, because its replacement may execute
|
||||
code. These static facts do not establish that a call runs at runtime.
|
||||
|
||||
ESM relative module paths, legacy package subpaths without exports, and selected
|
||||
package exports targets use URL suffix handling and one percent-decoding pass.
|
||||
Bare specifiers retain their complete query/fragment text for exports key matching.
|
||||
@@ -793,3 +939,17 @@ truncated under the `html_inline_script_locations_per_file` limit, and the
|
||||
scope's evidence counts the omitted locations. The graph names the
|
||||
file and up to 10 starting lines in its limitations, and coverage is partial. Import maps, speculation rules, data blocks, and blank
|
||||
bodies are not JavaScript and are not reported.
|
||||
|
||||
For static source relationships, an explicitly requested declaration filename
|
||||
(`.d.ts`, `.d.mts`, or `.d.cts`) retains its inventoried identity through both
|
||||
`import` and `require`. A declaration found only as an emitted-extension fallback
|
||||
remains unavailable as a runtime implementation. Package URL targets decode
|
||||
filename escapes once, including dots within filenames; forbidden whole segments
|
||||
and encoded separators remain rejected. ESM local-path classification uses the
|
||||
authored request before URL whitespace preprocessing, so leading whitespace
|
||||
cannot turn a bare request into a relative file dependency.
|
||||
For example, a leading tab or newline before `./dep.mjs` remains a bare
|
||||
package request; Node's later package-URL normalization can resolve it under
|
||||
`node_modules`, independently of a same-named file beside the importer. Resolver
|
||||
qualification checks the exact path and file existence: [`import.meta.resolve()`](https://github.com/nodejs/node/blob/v24.22.1/doc/api/esm.md#importmetaresolvespecifier)
|
||||
alone can return a URL for a missing file.
|
||||
@@ -85,6 +85,11 @@ publication. REA writes verified bytes to an exclusively owned output tree and
|
||||
rolls it back on failure. It also checks that the original source and engine
|
||||
bytes remain unchanged before committing. The existing output is never overwritten.
|
||||
|
||||
A denied selected-input read reports `access_denied` with the observed OS code
|
||||
and path. A source or engine identity change reports `artifact_changed`;
|
||||
neither failure is a malformed recovery request. Cancellation retains the
|
||||
caller's reason through admission, command launch and completion.
|
||||
|
||||
Multi-input relative filenames must be representable unchanged as artifact paths:
|
||||
NFC Unicode spelling and no literal backslashes. Unsupported layouts fail before
|
||||
invoking the engine rather than silently renaming inputs referenced by source maps.
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
# Static JavaScript route inventory
|
||||
|
||||
`analyze_javascript_application` and `analyze_web_bundle` retain a shared
|
||||
`route_inventory` alongside their existing graph and endpoint observations.
|
||||
The inventory answers which route patterns the supplied code suggests, where
|
||||
each pattern came from, and which parts remain unknown. It never requests a
|
||||
route, executes scripts, expands a sitemap, or establishes reachability.
|
||||
|
||||
Select a compact view in either MCP analysis call:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "summary",
|
||||
"view": {
|
||||
"kind": "javascript-routes",
|
||||
"collection": "routes",
|
||||
"where": { "path_prefix": "/reports" },
|
||||
"fields": ["path", "classification", "confidence"],
|
||||
"limit": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Keep the original analysis inputs in that call. For the CLI, pass the same
|
||||
selection as `--view '<JSON>' --detail summary --evidence_output routes.json`.
|
||||
The complete Evidence bundle is written without overwriting existing files.
|
||||
Unselected delivery retains the existing complete result. A web-bundle summary
|
||||
requires an explicit route view; application summaries also support the existing
|
||||
general summary when `view` is omitted.
|
||||
|
||||
Use the returned `normalized_result.parent_evidence_id` with
|
||||
`inspect_analysis_view` on the same connection. The CLI's exported bundle can be
|
||||
reopened with `inspect-evidence-bundle`; its `records` contain the complete
|
||||
Evidence. To use `inspect_analysis_view`'s `evidence-file` source, extract the
|
||||
selected parent record into a separate local Evidence JSON file (that source
|
||||
accepts one Evidence record, not a bundle). Views do not reopen source files or
|
||||
reconnect to a browser. Historical Evidence without `route_inventory` is
|
||||
unmeasured, rather than an empty route set.
|
||||
|
||||
Collections are `summary`, `routes`, `occurrences`, `sources`, and `unknowns`.
|
||||
All row collections support exact batch `where.ids`, field selection, `offset`,
|
||||
and `limit`. An explicit empty filter array matches nothing. Routes also support
|
||||
exact `paths`, literal `path_prefix`, `classifications`, `mechanisms`, and
|
||||
`min_confidence`; occurrences support `route_ids`, `source_ids`, and `mechanisms`;
|
||||
sources support `keys` and `statuses`; unknowns support `source_ids` and `kinds`.
|
||||
Follow `coverage.next_offset` until `coverage.exhausted`; a page may stop early
|
||||
at the 1 MiB serialized view budget. Oversized indivisible facts return a
|
||||
resource error carrying the retained parent Evidence ID.
|
||||
|
||||
Pattern IDs bind the exact path. Source IDs bind the source key, path, original
|
||||
URL, content digest, container digest, and any retained Next source applicability
|
||||
context (including the owning package digest). Occurrences identify a detector mechanism at a source location; one location
|
||||
may support multiple mechanisms. `source_count` counts distinct sources. Lookup IDs use 128 bits of SHA-256;
|
||||
full content and inventory digests remain SHA-256. Field selection always keeps
|
||||
row identity and necessary provenance. The page's deduplicated `sources` table
|
||||
contains the source descriptors referenced by returned rows. Retrieve all
|
||||
occurrences by route ID when independent supporting evidence matters. Source
|
||||
ranges use one-based lines and zero-based UTF-16 columns with exclusive ends;
|
||||
filename-only discoveries have no invented AST range.
|
||||
|
||||
The shared parser handles literal router calls, nested route objects, typed
|
||||
`_$` trees with explicitly inferred property-name fallbacks, Next App Router
|
||||
page paths, and navigation targets. It follows stable lexical bindings and
|
||||
keeps unresolved parents, mutations, spreads, and dynamic fields explicit.
|
||||
Index-route flags use the same value-at-read boundary as path fields: erased
|
||||
type wrappers and initialized stable boolean aliases preserve `true`/`false`;
|
||||
uninitialized or unresolved flags retain an interpretation unknown instead of
|
||||
silently disappearing.
|
||||
A selected route-builder root owns its configuration even when mutation prevents
|
||||
trusting its value. Ordinary wrapper keys do not become route ancestors. Missing
|
||||
outer ancestors of unbound typed subtrees remain unknown rather than invented.
|
||||
Generic Next runtime path constants require a top-level application segment
|
||||
supported by structural or navigation candidates across the supplied sources.
|
||||
This limits framework filesystem noise, while leaving unsupported candidates
|
||||
unknown. The parser shares the existing AST traversal and lexical index.
|
||||
|
||||
Literal fragments of computed expressions, module references, and page-shaped
|
||||
paths owned by Next interpretation do not also become generic route constants.
|
||||
Selected getter results retain the shared semantic owner's allocation identity;
|
||||
finite selections can claim their query data, while widened or cyclic selections
|
||||
keep an unknown in the interpretation that requested them.
|
||||
|
||||
Next page conversion receives an explicit source context. For local source files,
|
||||
the shared JSON owner first establishes a declared `next` dependency in the
|
||||
nearest owning package. Inventoried directories then select that package's `app`
|
||||
before `src/app`, following Next's [root selection](https://github.com/vercel/next.js/blob/v16.4.2/packages/next/src/lib/find-pages-dir.ts).
|
||||
Only files beneath the selected directory supply source-page support. Default
|
||||
`.next/static/chunks/app` output receives a separate compiled-directory context
|
||||
from the same package owner; custom build directories remain unmodeled. Pages Router
|
||||
files, unrelated components, and nested packages do not inherit that root merely
|
||||
because their paths contain `app`. Missing package/framework evidence remains an
|
||||
interpretation unknown for page-shaped source paths. Each page-shaped source retains its
|
||||
`next_app_context` and package identity so this derivation remains inspectable.
|
||||
|
||||
Captured script URLs use the `/_next/static/chunks/app/` asset boundary, independently
|
||||
of local package evidence. Source literals use recognized Next runtime context;
|
||||
imports, exports, and direct call arguments do not themselves declare pages.
|
||||
The parser completes its runtime-marker scan before interpreting retained module
|
||||
literals, so marker placement does not change the result. A matching filename
|
||||
suffix alone cannot establish App Router ownership.
|
||||
|
||||
[Next dynamic segments](https://nextjs.org/docs/app/api-reference/file-conventions/dynamic-routes)
|
||||
map to `:id`, `:slug+`, or `:slug*`; groups and parallel slots do not contribute
|
||||
URL segments. Intercepted routes remain unresolved. A prefix before `/_next/`
|
||||
is an **inferred** routing base: [assetPrefix](https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix)
|
||||
can differ from the routing base. Every affected occurrence keeps
|
||||
`unprefixed_path`, `inferred_base_path`, and low confidence. `system` classification
|
||||
is explicitly a path-name heuristic; `page` uses page-file or structured/typed configuration support. Name-only
|
||||
`route`/`addRoute` calls remain `unknown` because they can also register server APIs.
|
||||
|
||||
Next page-source segments keep their filename spelling when converted to route
|
||||
patterns. Next's [page-path conversion](https://github.com/vercel/next.js/blob/v16.4.2/packages/next/src/shared/lib/page-path/absolute-path-to-page.ts#L21-L39)
|
||||
removes page extensions and the `index` tail without URI-decoding segments, so
|
||||
ordinary percent sequences are not decoded: `%61` and `a` remain separate
|
||||
candidates and a literal `%` is retained. Its [App pathname normalizer](https://github.com/vercel/next.js/blob/v16.4.2/packages/next/src/server/normalizers/built/app/app-pathname-normalizer.ts#L5-L18)
|
||||
specifically applies the [underscore normalizer](https://github.com/vercel/next.js/blob/v16.4.2/packages/next/src/server/normalizers/underscore-normalizer.ts#L2-L9),
|
||||
which maps `%5F` to `_`; private-folder checks apply to the source spelling
|
||||
before that escape is mapped. When classifying a chunk URL, REA reads its
|
||||
pathname, then applies these same Next-specific rules. This is static source-path
|
||||
interpretation, not browser request-path decoding or reachability.
|
||||
|
||||
Known capture omissions remain inline in `summary.reported_capture_omissions`
|
||||
and queryable as `unknowns` of kind `capture-omission`. Each keeps a stable gap ID,
|
||||
its script-record/script-source unit, and the reported count (including `null`
|
||||
for unknown). They do not invent missing script identities or source ranges.
|
||||
|
||||
Counts describe deduplicated patterns in the provided code sources. They are
|
||||
not a denominator for complete application or capture coverage. In particular,
|
||||
server-only, lazy, inaccessible, or unselected sources may be missing.
|
||||
`coverage.application_routes` always remains `unknown`; parsing and candidate
|
||||
enumeration have their own completeness. Static work and fact admission share
|
||||
the existing 8 MiB per-source transfer ceiling, and the root inventory has an
|
||||
8 MiB admission budget. Exhaustion preserves a prefix with explicit capacity;
|
||||
the serialization owner also admits route facts within the actual space left by
|
||||
legacy facts. `result-bytes` capacity preserves an earlier work-capacity reason;
|
||||
if even that envelope cannot fit, the optional route facet remains unmeasured.
|
||||
These are accounting bounds, not a claim that total process memory is 8 MiB.
|
||||
|
||||
Projection only reduces delivery, not analysis work. Retained immutable Evidence
|
||||
can reuse validated tables through a weak cache with the snapshot's lifetime;
|
||||
subject identity is checked on every query. No live state or source content is
|
||||
cached as current. Use existing source selection and resource controls when
|
||||
they match the investigation, keeping their partial coverage visible.
|
||||
@@ -44,11 +44,19 @@ launch does not, by itself, show that fuse.
|
||||
|
||||
The caller supplies the literal-loopback HTTP endpoint directly. Selecting an endpoint exposes all targets it serves and their local script locations or renderer origins. A target must match the exact requested target ID and expose a same-port WebSocket reported by that endpoint. Targets explicitly marked attached are rejected rather than displacing another debugger. Node's `/json/list` omits attachment state, so REA preserves it as `null` (unknown), reports that limitation, and permits observation unless the endpoint explicitly reports `attached: true`. Local paths are canonicalized after symlink resolution.
|
||||
|
||||
The `attached` field in JavaScript runtime and browser target results is nullable: `true` and `false` are reported only when discovery supplied a boolean; `null` means the endpoint omitted or could not report the state. CDP `TargetInfo` worker records require an `attached` boolean; REA excludes a worker record with a missing or malformed value as an invalid protocol value.
|
||||
The `attached` field in JavaScript runtime and browser target results is nullable: `true` and `false` are reported only when discovery supplied a boolean; `null` means the endpoint omitted or could not report the state. HTTP `/json/list` discovery parses target rows independently. Malformed optional attachment metadata becomes `null` with a row and field diagnostic in `limitations`; usable siblings remain available. Rows with malformed target identity are excluded with diagnostics, and socket authority is still validated before connection. CDP `TargetInfo` worker records require an `attached` boolean; REA excludes a worker record with a missing or malformed value as an invalid protocol value.
|
||||
|
||||
`list_javascript_runtime_targets` returns every available target in one inline
|
||||
array.
|
||||
|
||||
Attachment refusals identify `attached: true` and ask the caller to disconnect
|
||||
the other debugger or select another target. A declared-role mismatch names
|
||||
`runtime_kind` and the target protocol type; correct the role or omit it to
|
||||
retain an unknown role. A role-only disagreement in supplied runtime Evidence
|
||||
is attributed to `parameters.runtime_kind`, separately from a target-ID
|
||||
disagreement. Partial-capture warnings count retained distinct script records;
|
||||
`observed_total` retains the raw script-event count before deduplication.
|
||||
|
||||
For the MCP follow-up call, pass the same literal-loopback endpoint and the selected target ID to `observe_javascript_runtime`. REA rediscovers targets and validates the ID against the live endpoint before observation.
|
||||
|
||||
### Discovery file locations
|
||||
@@ -91,9 +99,12 @@ The provider sends exactly two protocol commands:
|
||||
- `Debugger.enable`
|
||||
|
||||
It never sends `Runtime.evaluate`, reads script source, installs breakpoints,
|
||||
pauses or resumes execution, calls `Runtime.runIfWaitingForDebugger`, launches
|
||||
the target, instruments JavaScript, or invokes Electron APIs. Closing a call
|
||||
closes only REA's WebSocket.
|
||||
sends pause/resume commands, calls `Runtime.runIfWaitingForDebugger`, launches
|
||||
the target, instruments JavaScript, or invokes Electron APIs. Enabling
|
||||
`Debugger` can make target-authored `debugger` statements pause execution.
|
||||
REA does not send `Debugger.setSkipAllPauses` or resume these pauses; passive
|
||||
observation is not a guarantee of uninterrupted target execution. Closing a
|
||||
call closes only REA's WebSocket.
|
||||
|
||||
Each WebSocket handshake and command response has its own 20-second deadline,
|
||||
separate from the caller-selected observation window. A stalled handshake or
|
||||
@@ -105,7 +116,9 @@ The direct observations are:
|
||||
- `Debugger.scriptParsed`, establishing that a bounded script location was
|
||||
parsed in the observed execution context;
|
||||
- `Debugger.scriptFailedToParse`, retained only as a bounded invalid-script
|
||||
count;
|
||||
count in `scripts.parse_failed_total`. A well-formed parse-failure
|
||||
event is distinct from malformed protocol data and does not make loaded
|
||||
script coverage unavailable. Older Evidence may omit this count;
|
||||
- `Runtime.executionContextCreated`,
|
||||
`Runtime.executionContextDestroyed`, and
|
||||
`Runtime.executionContextsCleared`, establishing context lifecycle within
|
||||
@@ -119,6 +132,12 @@ handlers, or script-unload events. Those facts remain explicit unknowns.
|
||||
`scriptParsed` proves script presence, not the importing module, feature
|
||||
execution, initialization order, or causality.
|
||||
|
||||
For an authorized file script, `location.file_path` is the verified filesystem
|
||||
identity and optional script-item `reported_url` preserves the exact Inspector
|
||||
URL. Script identity includes that reported URL, so query and fragment
|
||||
variants of one file remain separate observations. Older Evidence may omit
|
||||
`reported_url` and remains readable.
|
||||
|
||||
## Node and Electron roles
|
||||
|
||||
The caller declares one role: `node`, `electron-main`, `electron-preload`, or
|
||||
@@ -128,6 +147,10 @@ Inspector does not authenticate the operating-system PID or distinguish an
|
||||
Electron role, so Evidence records the role authority as
|
||||
`caller-declared-unverified`.
|
||||
|
||||
If final script-location authorization fails, collected script and context facts
|
||||
remain available in the typed failure with explicitly unverified locations.
|
||||
Descriptor cleanup status and its retry owner remain independent of that capture.
|
||||
|
||||
Electron main, preload, and renderer behavior can therefore be observed only
|
||||
as separate Inspector targets. The provider does not infer that two
|
||||
targets belong to the same Electron application.
|
||||
|
||||
@@ -57,7 +57,11 @@ are complete Evidence objects, not only evidence IDs:
|
||||
`static_layers` requires exactly one `application` role and permits additional
|
||||
`cache` and `assets` roles. A `file-root` mapping translates a canonical runtime
|
||||
filesystem root to an artifact-relative prefix. A `url-prefix` mapping does the
|
||||
same for one exact HTTP(S) origin and path prefix. Mappings cannot add CDP
|
||||
same for one exact HTTP(S) origin and path prefix. Each URL path segment below
|
||||
the prefix is percent-decoded separately: an encoded slash (`%2F`) stays inside
|
||||
its segment and never names a nested artifact, so such a location, like one
|
||||
with malformed or non-UTF-8 escapes, has no artifact path under that mapping.
|
||||
An encoded backslash (`%5C`) remains a literal name byte. Mappings cannot add CDP
|
||||
origins, broaden Electron roots, read files, or authorize source-map access;
|
||||
they are caller-declared inference inputs applied only to already-authorized
|
||||
locations.
|
||||
@@ -133,6 +137,17 @@ classification:
|
||||
- `unknown` covers incomplete captures, layers outside runtime scope, and
|
||||
unresolved correspondence. Nodes outside a mapped artifact prefix stay
|
||||
unknown even when another prefix in the same static layer was observed.
|
||||
Nodes beneath a mapped artifact prefix also stay unknown when a runtime
|
||||
script or worker under that mapping has no artifact path.
|
||||
|
||||
Script inventory completeness remains a separate reported fact. Unavailable,
|
||||
truncated, or attach-limited worker inventory prevents negative code-presence
|
||||
claims even when the selected page's script inventory is complete. Accepted
|
||||
positive script correspondences remain loaded.
|
||||
An Electron script's explicit unknown `frame_scope` preserves its reported
|
||||
frame ID and sourceURL provenance as node properties, but does not link that
|
||||
script to the current frame inventory. Attach-limited script coverage cannot
|
||||
establish negative code-presence claims.
|
||||
|
||||
None of these states proves feature execution, initialization order,
|
||||
reachability, or causality. In particular, `not-observed-in-capture` never means
|
||||
|
||||
@@ -42,7 +42,7 @@ reaches its budget returns a typed timeout naming the phase and the effective
|
||||
milliseconds, while caller cancellation remains a cancellation. Allow execution,
|
||||
cleanup, and delivery headroom in the outer MCP client deadline.
|
||||
|
||||
Earlier verification used JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server` 1.3.0 over streamable HTTP. The official Linux x64 CE build `5.48.0.202610071539` was also verified through real CLI and production stdio MCP native-code workflows; see the [Linux CE audit](../experiments/analysis-efficiency/jeb-linux-native.md) for measurements and limits. CE verification does not establish licensed/Demo Android decompilation or GUI behavior.
|
||||
Earlier verification used JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server` 1.3.0 over streamable HTTP. The official Linux x64 CE build `5.48.0.202610071539` was also verified through real CLI and production stdio MCP native-code workflows; see the [Linux CE audit](https://github.com/morluto/rea/blob/42e1a17d6e6c0487f17be0bae248553c4caba7f7/experiments/analysis-efficiency/jeb-linux-native.md) for measurements and limits. CE verification does not establish licensed/Demo Android decompilation or GUI behavior.
|
||||
|
||||
## Operations
|
||||
|
||||
@@ -68,3 +68,10 @@ All four preserve target-file bytes. `open_jeb_project` selects or creates an en
|
||||
- An unreachable endpoint is a provider-availability failure with recovery guidance; REA does not retry or spawn the engine.
|
||||
|
||||
Real-engine verification lives in the JEB lane described in [testing.md](testing.md); mock transport tests do not establish real-engine compatibility.
|
||||
|
||||
A project-open refusal retains the selected engine-host artifact path separately
|
||||
from the service endpoint. Discovery of an unreachable service includes the same
|
||||
start/configuration recovery as a direct request. If a primary reply completes
|
||||
before a later client-information lookup fails or is cancelled, the typed failure
|
||||
retains that reply as partial Evidence. Engine metadata stays unknown; this is not
|
||||
a claim that the full operation completed.
|
||||
@@ -68,9 +68,9 @@ The background export belongs to the session. `close_binary`, or the end of a CL
|
||||
|
||||
The assembly label index is scanned once per export reader. Cancelling a request stops its wait without interrupting other readers; closing the session aborts and drains the shared scan before releasing its files.
|
||||
|
||||
Regex procedure and string search uses ECMAScript Unicode matching in a cancellable worker with a five-second matching deadline and a worker old-generation heap. Literal matching remains the default. Cancellation or a matching deadline leaves the session available. This release has no runtime override for that deadline. The worker requests 128 MiB. A positive Node.js `--max-old-space-size` on the process that starts REA, including `NODE_OPTIONS`, overrides that request and sets the effective heap. `--max-old-space-size-percentage` takes precedence over that flag in either order. REA does not turn the percentage into a MiB, so the effective heap stays unestablished instead of the 128 MiB request or the absolute flag. The reported `NODE_OPTIONS` value is the process initial environment when that block can be read. A later edit to the environment is not the value Node applied, and an unreadable initial value leaves the effective heap unestablished. The launch Node options are the initial command line on Linux when that buffer still has a program entry, not a later edit to `process.execArgv`. A preload can clear that array after V8 has applied the flags. Setting `process.title` or `--title` overwrites the command line, so a buffer without a program entry leaves the effective heap unestablished. `--env-file` or `--env-file-if-exists` can supply `NODE_OPTIONS` when the initial environment has none. That file is not part of the initial environment block, so the effective heap stays unestablished instead of the 128 MiB request. An initial `NODE_OPTIONS` is still used, because Node does not let the file replace it. A positive `--max-heap-size`, including the underscore form and launch `NODE_OPTIONS` on Node versions that allow it, sets the total heap rather than the old generation and can be lower than the 128 MiB request. The effective old generation stays unestablished while that value is in effect. A zero or empty value clears it. `--experimental-config-file` or `--experimental-default-config-file` can set `nodeOptions`, including a heap percentage, from a file that is not read back. The effective heap stays unestablished when either flag is present, including when an absolute `--max-old-space-size` is also on the command line or in the recovered `NODE_OPTIONS`. Node accepts the config path as `=` or the next argument.
|
||||
Regex procedure and string search uses ECMAScript Unicode matching in a cancellable worker with a matching deadline and a worker old-generation heap. Literal matching remains the default. Cancellation or a matching deadline leaves the session available. The deadline is `REA_REGEX_MATCHING_DEADLINE_MS`, a positive decimal integer from 1 to 2147483647 ms. Unset uses 5000 ms. A larger value is refused because Node schedules a delay past that signed 32-bit bound as 1 ms. Invalid values are configuration errors and do not fall back to the default. The setting is read from the environment that starts REA, so a change needs a restart. A deadline failure names the effective milliseconds, whether they came from the default or the setting, the default, and the maximum. The worker old-generation heap does not raise it. The worker heap request is `REA_REGEX_WORKER_HEAP_REQUEST_MIB`, a decimal integer from 7 to 16384 MiB. Unset uses 128 MiB. A request below 7 MiB aborts the worker during startup, and a larger value is refused. Invalid values are configuration errors and do not fall back to 128. The setting is read from the environment that starts REA, so a change needs a restart. A heap failure names the effective request, whether it came from the default or the setting, the default, the floor, and the maximum. A positive Node.js `--max-old-space-size` on the process that starts REA, including `NODE_OPTIONS`, overrides that request and sets the effective heap. `--max-old-space-size-percentage` takes precedence over that flag in either order. REA does not turn the percentage into a MiB, so the effective heap stays unestablished instead of the worker heap request or the absolute flag. The reported `NODE_OPTIONS` value is the process initial environment when that block can be read. A later edit to the environment is not the value Node applied, and an unreadable initial value leaves the effective heap unestablished. The launch Node options are the initial command line on Linux when that buffer still has a program entry, not a later edit to `process.execArgv`. A preload can clear that array after V8 has applied the flags. Setting `process.title` or `--title` overwrites the command line, so a buffer without a program entry leaves the effective heap unestablished. `--env-file` or `--env-file-if-exists` can supply `NODE_OPTIONS` when the initial environment has none. That file is not part of the initial environment block, so the effective heap stays unestablished instead of the worker heap request. An initial `NODE_OPTIONS` is still used, because Node does not let the file replace it. A positive `--max-heap-size`, including the underscore form and launch `NODE_OPTIONS` on Node versions that allow it, sets the total heap rather than the old generation and can be lower than the worker heap request. The effective old generation stays unestablished while that value is in effect. A zero or empty value clears it. `--experimental-config-file` or `--experimental-default-config-file` can set `nodeOptions`, including a heap percentage, from a file that is not read back. The effective heap stays unestablished when either flag is present, including when an absolute `--max-old-space-size` is also on the command line or in the recovered `NODE_OPTIONS`. Node accepts the config path as `=` or the next argument.
|
||||
|
||||
Single-function subprocess output and each cached source facet share a 16 MiB buffered-text capacity. Cached spans are checked before allocation; larger text remains in the local export and the error identifies the file and range. `analyze_function` initially returns the complete dossier, which can still be large; later retained views do not remove that first transfer. Prefer the individual text tools for a focused question.
|
||||
Single-function subprocess output and each cached source facet share a 16 MiB buffered-text capacity. Cached spans are checked before allocation; larger text remains in the local export and the error identifies the file and range. `analyze_function` with `detail: "complete"` (the CLI default) returns the complete dossier inline, which can still be large; later retained views do not remove that first transfer. The MCP default retains the dossier and returns its summary, so page the facets you need through `inspect_analysis_view`, or prefer the individual text tools for a focused question.
|
||||
|
||||
## Strings
|
||||
|
||||
@@ -125,7 +125,7 @@ REA writes Kuna's exports only inside `REA_KUNA_CACHE_DIR`:
|
||||
- `trash/` holds retired builds and evicted exports awaiting removal.
|
||||
- `home/` is Kuna's HOME.
|
||||
|
||||
Builds publish by one rename, so a reader sees a complete export or none. A session keeps the export it uses from eviction only within its own REA process. If another REA process sharing the cache evicts it, the session rebuilds it; meanwhile pseudocode falls back to single-function runs. A damaged or edited export is removed and rebuilt. Staging moves to `trash/` for removal only after REA proves its Kuna process stopped. Staging of a process it cannot prove stopped is never swept; REA keeps it owned, retries the stop on `close_binary`, and reports it until the stop is proven. Failed file removal also stays bound to its export and is retried by `close_binary`; closing does not report success while that cleanup remains. `rea doctor --provider kuna` reports the cache directory without creating it.
|
||||
Builds publish by one rename, so a reader sees a complete export or none. If later staging retirement fails, the typed cleanup error retains the completed export's key, directory and manifest in `published_export`. The session adopts that verified project; later queries reuse its immutable files while `project_export.cleanup_incomplete` reports the retained owner's live state. `close_binary` retries only the remaining cleanup, and a successful retry resolves prior cleanup errors. A failed export followed by failed staging cleanup retains both failures; it does not invent a publication receipt. A session keeps the export it uses from eviction only within its own REA process. If another REA process sharing the cache evicts it, the session rebuilds it; meanwhile pseudocode falls back to single-function runs. A damaged or edited export is removed and rebuilt. Staging moves to `trash/` for removal only after REA proves its Kuna process stopped. Staging of a process it cannot prove stopped is never swept; REA keeps it owned, retries the stop on `close_binary`, and reports it until the stop is proven. Failed file removal also stays bound to its export and is retried by `close_binary`; closing does not report success while that cleanup remains. `rea doctor --provider kuna` reports the cache directory without creating it.
|
||||
|
||||
## Verify with a real Kuna
|
||||
|
||||
@@ -139,3 +139,7 @@ REA_KUNA_COMMAND=/absolute/path/to/kuna GHIDRA_INSTALL_DIR=/absolute/path/to/ghi
|
||||
The lane compiles a small source-owned fixture with the host C compiler and verifies the MCP workflow end to end. `--targets DIR --functions a,b` adds caller-built binaries. `--compare-ghidra` (with `GHIDRA_INSTALL_DIR`) runs those same functions through Ghidra; it requires `--targets` and `--functions` and refuses empty matrices or requested function names missing from every target. See [testing](testing.md#kuna-lane) for coverage.
|
||||
|
||||
Failed runs also report process-lineage evidence and exit unsuccessfully. If provider or transport cleanup is unconfirmed, the report says `cleanup: "incomplete"` and records `retained_workspace`; that directory is preserved for investigation and cleanup retries.
|
||||
|
||||
Doctor reports an invalid Kuna cache, job count, or timeout setting with that
|
||||
setting's correction. Correct or unset the named setting and restart REA; these
|
||||
configuration rejections do not require replacing the selected Kuna executable.
|
||||
@@ -51,6 +51,11 @@ This keeps the complete source in the session and returns a distinct linked
|
||||
evidence, metadata status, module/MVID, assembly, frameworks, returned-row counts,
|
||||
coverage/issues and limitations. No assembly is loaded or executed.
|
||||
|
||||
ECMA-335 permits empty metadata streams to be omitted. An absent `#Blob`
|
||||
stream therefore does not prevent module or assembly identity from being read
|
||||
when all blob indexes are zero; a nonzero reference to that missing heap is
|
||||
reported as a missing-stream issue.
|
||||
|
||||
Use `normalized_result.parent_evidence_id` to page original rows:
|
||||
|
||||
```json
|
||||
@@ -177,6 +182,9 @@ declares API compatibility across multiple .NET implementations. REA preserves
|
||||
that target in `target_frameworks` and leaves `runtime_family` as `unknown`
|
||||
unless independent markers identify a runtime family, such as a ReadyToRun
|
||||
signature.
|
||||
When ReadyToRun is observed, `runtime_family` remains `modern-dotnet` even if
|
||||
Unity or target-framework hints are also present; the separate hint evidence
|
||||
is retained in the classification evidence list.
|
||||
|
||||
### Resource admission
|
||||
|
||||
@@ -198,10 +206,40 @@ reports `resource-limit` with unavailable or partial coverage; omitted facts do
|
||||
not become empty observations. Ambiguous PE section mappings are rejected
|
||||
rather than interpreted according to section order.
|
||||
|
||||
Minimal Edit-and-Continue metadata marked by `#JTD` is not a standalone
|
||||
assembly inventory. The pinned [.NET 9 reader](https://github.com/dotnet/runtime/blob/v9.0.0/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReader.cs)
|
||||
uses different reference widths for this mode, and delta handles require the
|
||||
baseline and prior generations. The released
|
||||
[metadata serializer](https://github.com/dotnet/runtime/blob/v9.0.0/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataBuilder.cs)
|
||||
emits this marker with `#-`; the
|
||||
[PE writer](https://github.com/dotnet/runtime/blob/v9.0.0/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedPEBuilder.cs)
|
||||
can embed that metadata root. REA reports `unsupported-metadata` at the marker
|
||||
header before interpreting table rows, preserving the selected path, artifact
|
||||
digest, parsed metadata version and stream names, and admitted PE/CLI facts.
|
||||
Metadata status is partial; member and native
|
||||
declaration inventories are unavailable. Empty collections do not establish
|
||||
absence, and a requested type name is not declared unmatched. Ordinary
|
||||
standalone `#~` and `#-` metadata keep their existing interpretation. This does
|
||||
not add raw `.dmeta` input, baseline attachment or delta reconstruction.
|
||||
Regression coverage uses authored metadata in a generated PE shell; it does
|
||||
not establish naturally emitted application PEs, CLR loadability or a real
|
||||
Edit-and-Continue execution.
|
||||
|
||||
### Selected member inspection
|
||||
|
||||
When whole-assembly member decoding reaches the representation budget, select
|
||||
known declaring types before their signatures and CIL bodies are decoded:
|
||||
Member inspection admits type names, tokens, list ranges/counts, nesting facts,
|
||||
module identity and their output representation before decoding members. If
|
||||
later decoding or projection reaches the 64 MiB representation budget, the
|
||||
result retains this type inventory with `coverage.state: "partial"` and the
|
||||
original `resource-limit` coordinate. It does not return partially admitted
|
||||
member collections: empty fields, methods, references and edges are unexamined,
|
||||
not proof of absence; exported types, user strings and signature-resolution
|
||||
attestation remain null. Summary and retained pages keep this partial coverage.
|
||||
The capacity limit is unchanged. If metadata or the type inventory itself cannot
|
||||
be admitted, the result remains unavailable.
|
||||
|
||||
Use the returned `types[].full_name` values to select declaring types before
|
||||
their signatures and CIL bodies are decoded:
|
||||
|
||||
```bash
|
||||
rea inspect-managed-members /absolute/path/Assembly.dll --type Example.Parser
|
||||
@@ -210,9 +248,12 @@ rea inspect-managed-members /absolute/path/Assembly.dll --type Example.Parser
|
||||
MCP uses `inspect_managed_members` with
|
||||
`{"path":"/absolute/path/Assembly.dll","type_names":["Example.Parser"]}`.
|
||||
Repeat `--type` or supply multiple unique `type_names` for several types.
|
||||
Names match the reported `full_name` exactly. Top-level names are namespace
|
||||
qualified; nested types currently report bare names, which can select several
|
||||
types with that name. Each type's `enclosing_type_token` records its enclosing
|
||||
Names match the reported `full_name` exactly. Top-level names require that
|
||||
namespace-qualified spelling. Nested declarations also accept their bare
|
||||
`name`, including when their own namespace is nonempty; this can select several
|
||||
types with that name. Selection uses the parsed TypeDef facts and comparison
|
||||
uses the returned declaring types, rather than reconstructing scope from the
|
||||
caller's spelling. Each type's `enclosing_type_token` records its enclosing
|
||||
TypeDef. `nesting_read` distinguishes complete source rows (true), incomplete
|
||||
current rows (false), and historical Evidence (null). A null enclosing token
|
||||
alone does not establish that a type is top-level.
|
||||
@@ -231,7 +272,36 @@ remain an assembly-wide inventory, so output size is not guaranteed to be small.
|
||||
Coverage describes the selected members. Comparisons leave members outside the
|
||||
opposite observation's selection unknown, and application graphs report their
|
||||
selected scope as partial. Neither makes an absence claim about the whole
|
||||
assembly. Older member Evidence without `selection` remains readable.
|
||||
assembly. Older member Evidence without `selection` remains readable. The
|
||||
comparison resolves a member's declaring TypeDef token in its own source, then
|
||||
checks that nominal `name_sha256` against the opposite selected type set.
|
||||
Pairing, absence and aggregate selected-scope agreement share these retained
|
||||
facts, including enclosing names, rather than requested spellings or display
|
||||
labels. Equivalent requests can cover the same types, while the same display
|
||||
name can select different nested owners. Such disjoint selected owners cannot
|
||||
establish a member pair or added/removed status even when both inventories are
|
||||
complete. Missing nominal identities cannot establish selected membership,
|
||||
including historical selected Evidence that retains only display names;
|
||||
the explicitly limited historical whole-inventory comparison remains available.
|
||||
Incomplete selected inventories or missing identities leave aggregate agreement
|
||||
unknown and coverage partial; known observed member pairs remain available.
|
||||
Complete empty selected inventories agree even if their unmatched request names
|
||||
differ. Whole-assembly and selected
|
||||
inventories remain distinct scopes, and each source retains its exact request.
|
||||
The `method_rows` interval records the examined zero-based MethodDef offset, row
|
||||
count and full table total before type selection. A complete observation can
|
||||
cover only that interval. Comparison keeps omissions from a method page unknown:
|
||||
build-local row positions cannot prove member absence across builds. Historical
|
||||
Evidence without this interval also cannot establish method absence; observed
|
||||
pairs and field inventory comparisons remain available.
|
||||
|
||||
For MCP callers, request `detail: "summary"` and page the `types` collection
|
||||
using the returned parent Evidence ID. Select one or more returned full names
|
||||
in a subsequent `inspect_managed_members` request. Selection is a recovery path,
|
||||
not a guarantee: a single type or the assembly-wide member references can still
|
||||
exhaust the representation budget. A successful command exit means the Evidence
|
||||
was returned; inspect `coverage.state` and `coverage.issues` before treating any
|
||||
collection as complete.
|
||||
|
||||
### User strings
|
||||
|
||||
@@ -290,8 +360,30 @@ when matching declared native calls to supplied native Evidence. Static inspecti
|
||||
does not load or execute the assembly. Reconstructed structure and unresolved
|
||||
native/runtime questions retain their own authority and coverage.
|
||||
|
||||
When the selected source changes during acquisition or no longer matches its
|
||||
admitted digest, inspection and comparison report retryable `artifact_changed`;
|
||||
any completed hash retains the observed digest and byte count. Retry against a
|
||||
stable source. This differs from corrupted retained Evidence, which remains an
|
||||
Evidence-integrity failure. Malformed selected PE input has the same input-format
|
||||
classification in inspection and comparison. Its issue identifies the selected
|
||||
filename and argument: `path` for inspection, or `leftPath` / `rightPath` for CLI
|
||||
comparison. A failed right-side comparison retains the completed left observation.
|
||||
|
||||
## Member comparison
|
||||
|
||||
Call edges and field accesses retain `il_offset` as their instruction site within
|
||||
the caller's build-local method token and artifact identity. Their
|
||||
`target_identity_sha256` combines the existing enclosing-type nominal digest,
|
||||
member name and resolved decoded signature. Comparison joins edges to their
|
||||
instruction sites rather than array positions and compares these identities;
|
||||
display labels alone cannot establish equal targets. Same-named nested types
|
||||
under different enclosing types and different overload signatures stay distinct.
|
||||
This is nominal metadata identity, not assembly resolution or runtime binding.
|
||||
Historical edges without a site or identity, unresolved/cyclic type scopes,
|
||||
TypeSpec owners and unresolved MethodSpec instantiations retain unknown target
|
||||
equivalence. Valid MethodSpecs combine the base member identity with the
|
||||
resolved instantiation signature, preserving generic argument distinctions.
|
||||
|
||||
Names and semantic labels do not establish durable member identity. Comparison
|
||||
preserves observed differences, structural inferences, and unknowns separately.
|
||||
|
||||
@@ -367,6 +459,24 @@ constants, and bounded shape context. It reports all candidates at the winning
|
||||
score and remains ambiguous when the evidence does not distinguish them. The
|
||||
digest does not itself remap metadata tokens.
|
||||
|
||||
Matched methods also compare aligned references. `call-shape` and
|
||||
`field-shape` report opcode and operand-kind differences; `call-target` and
|
||||
`field-target` report differences in the observed target declaring type or
|
||||
name from `call_edges` and `field_accesses`. Target tokens remain build-local
|
||||
and are not compared across artifacts. Missing or unaligned edges and missing
|
||||
target names leave that facet unknown; equal names do not establish overload
|
||||
identity or runtime dispatch. `exception-shape` compares clause flags and
|
||||
ranges. `exception-target` compares the nested nominal catch-type name digest;
|
||||
for resolved TypeDefs it also compares the enclosing assembly identity. A
|
||||
different known name or assembly scope establishes a change. Equal TypeRef
|
||||
names remain unknown because their resolution scope is not resolved here.
|
||||
Exception tokens remain in the source facts but are not cross-build identity;
|
||||
an unresolved catch type leaves that facet unknown. The method body retains the
|
||||
display `class_type`, nested `class_type_name_sha256`, and whether its local
|
||||
token identifies a TypeDef. A TypeDef digest alone does not prove identity
|
||||
without matching assembly identity. Missing assembly identity and equal TypeRef
|
||||
names leave the comparison unknown.
|
||||
|
||||
Tokens are always remapped through observed structure. A caller cannot carry
|
||||
`0x06001234` into a new MVID and assume it names the same method.
|
||||
|
||||
@@ -417,7 +527,47 @@ point identities with supplied native export or function evidence. Name/module
|
||||
matches establish declaration links; they do not establish that a call executed.
|
||||
When either side lacks a usable module identity, a matching name remains
|
||||
unresolved and retains the candidate evidence; REA reports a module mismatch
|
||||
only when both identities are available and differ.
|
||||
only when both identities are available and their format-specific comparison
|
||||
establishes a difference. ELF module names retain case, PE module names compare
|
||||
case-insensitively, and a case-only match for Mach-O or an unknown format stays
|
||||
unresolved because the available Evidence does not establish case behavior.
|
||||
For PE imports, `.dll`, `.exe`, a trailing period, and absolute paths suppress
|
||||
appending `.dll`; other names may also be tried with `.dll`. Unix loaders use
|
||||
the platform suffix (`.so` or `.dylib`) and `lib` prefix variations. A suffix
|
||||
already present at the end of the name or before a period still gets an
|
||||
appended-suffix variation, so Linux can match `nativedep.so.6` with
|
||||
`libnativedep.so.6`. The `lib` prefix is omitted when the name contains `/`, and
|
||||
absolute paths are used as supplied. POSIX paths keep backslashes as literal
|
||||
characters. These candidates follow [Microsoft's native library loading rules](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/native-library-loading).
|
||||
These filename rules compare declared and observed identities; they do not
|
||||
establish which library the runtime loaded.
|
||||
|
||||
Signatures retain their decoded fields, but a Field or MethodDef row whose
|
||||
signature has the other member kind is malformed and cannot establish an
|
||||
unchanged signature. Comparison retains exact byte-based pairs for malformed
|
||||
signatures, including empty, truncated, or invalid headers, but reports them as
|
||||
unknown with a `signature-malformed` limitation when no change is observed.
|
||||
This applies to historical Evidence without signature-resolution attestation as
|
||||
well. Runtime-provided methods are counted separately in
|
||||
`native_implementations.runtime_provided`; they have no file-backed native body
|
||||
for this workflow to map and do not increase `native_body_unresolved`. Historical
|
||||
verification Evidence without this separate count leaves it unknown (`null`).
|
||||
|
||||
An empty declared import name remains unchanged in verification Evidence and
|
||||
produces no native-name candidates. With a supplied PE native context and
|
||||
`no_mangle: false`, an unspecified charset includes the ANSI `A` candidate;
|
||||
`auto` includes the Windows Unicode `W` candidate. Other native formats do not
|
||||
inherit these Windows defaults. Known ELF and Mach-O contexts use the declared
|
||||
entry point without A/W suffixes or Windows stdcall decoration, including when
|
||||
the metadata declares an explicit ANSI or Unicode marshalling charset. The
|
||||
original charset and calling convention remain unchanged. This follows the
|
||||
[.NET runtime's platform-specific entry-point lookup](https://github.com/dotnet/runtime/blob/v10.0.0/src/coreclr/vm/method.cpp#L3134-L3177).
|
||||
Windows name decorations are static inferred candidates;
|
||||
exact spelling still suppresses decoration and no runtime binding is proven.
|
||||
The mapping follows [Microsoft's charset guidance](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/charset).
|
||||
Partial input coverage records a residual question even when no unresolved
|
||||
individual boundary was produced. Selected literal inventories likewise remain
|
||||
partial when unknown MethodDef ownership can hide a selected body.
|
||||
|
||||
Metadata tokens are not native addresses. Native body mapping for C++/CLI,
|
||||
ReadyToRun, NativeAOT, and IL2CPP requires separately verified format and provider
|
||||
@@ -428,6 +578,32 @@ Declared import inventory supports a bounded positive claim. Its absence does
|
||||
not exclude dynamic resolution, generated code, protected code, native helpers,
|
||||
or server-side behavior.
|
||||
|
||||
## Application graph relations
|
||||
|
||||
`project_managed_application_graph` turns member Evidence into `contains`
|
||||
edges for declarations, `imports` edges for P/Invoke declarations, and two
|
||||
relation kinds read from method bodies:
|
||||
|
||||
- `calls` links two `managed-method` nodes when a `call`, `callvirt`, `newobj`
|
||||
or `jmp` site in the caller targets a MethodDef in the inspected module.
|
||||
- `accesses` links a `managed-method` node to a `managed-field` node for each
|
||||
field it loads, stores or takes the address of.
|
||||
|
||||
Each edge stands for every site between one source and one target. Its
|
||||
properties carry `site_count` and per-opcode `opcodes` counts; `accesses`
|
||||
edges also list `access` as `read`, `write` and/or `address`. Edge evidence
|
||||
links the member Evidence.
|
||||
|
||||
Some sites are counted rather than projected: calls to MemberRefs (members of
|
||||
other assemblies or of generic type instantiations), generic method
|
||||
instantiations (`MethodSpec`), unknown targets, `ldftn`/`ldvirtftn` function
|
||||
pointer loads, recursive self-calls (the graph has no self-loops), MemberRef
|
||||
fields, and sites whose method or field node is absent, for example under a
|
||||
type selection. Limitations name each counted class, and graph coverage is
|
||||
`partial` with `omitted_count` equal to the omitted sites when nothing else is
|
||||
incomplete. A missing `calls` edge is therefore not evidence that a method is
|
||||
never called.
|
||||
|
||||
## Evidence record shape
|
||||
|
||||
Member comparison, application graph projection, and managed/native verification
|
||||
@@ -461,6 +637,10 @@ preserving readable member and attribute facts.
|
||||
The retained inverse indexes are admitted against the existing representation
|
||||
budget before allocation.
|
||||
|
||||
Custom-attribute `type_name` uses `Outer+Nested` when its local TypeDef or
|
||||
TypeRef enclosing chain is readable. It remains `null` when the constructor's
|
||||
type or required enclosing facts cannot be resolved.
|
||||
|
||||
`exported_types` reports ExportedType rows as declared. Facade assemblies such
|
||||
as .NET `System.Runtime.dll` provide their types this way: `forwarder: true`
|
||||
marks a type forwarder, and `implementation_token` names the AssemblyRef, File,
|
||||
@@ -474,6 +654,19 @@ CLI `#GUID` heap values retain their exact 16-byte GUID text. REA does not impos
|
||||
RFC 4122 UUID version or variant bits on MVID, EncId, or EncBaseId because
|
||||
ECMA-335 metadata does not require those bit patterns. Tokens and RVAs are
|
||||
build-local coordinates, not durable cross-build identities or native addresses.
|
||||
Field and method signatures validate embedded TypeDefOrRefOrSpec operands
|
||||
against the artifact's metadata tables. An invalid operand leaves the member
|
||||
name and raw signature digest available while marking that signature malformed
|
||||
and coverage partial.
|
||||
A fat method header's nonzero LocalVarSigTok must name an existing StandAloneSig
|
||||
row whose blob is a structurally valid LocalVarSig. REA validates its local
|
||||
types and exact blob coverage but does not project their identities. An invalid
|
||||
token or malformed signature leaves decoded CIL anchors intact but marks the
|
||||
body malformed and coverage partial. Unsupported local type encodings retain
|
||||
the readable body with partial coverage. Zero continues to mean no locals
|
||||
signature. Comparison detects whether a locals declaration is present, but raw
|
||||
table tokens are not cross-build identities and local type contents are not
|
||||
compared.
|
||||
|
||||
## Decoded-CIL fingerprint
|
||||
|
||||
@@ -499,6 +692,10 @@ Tuple operands use these exact projections:
|
||||
- signed integer and variable operands: decimal strings; floating-point
|
||||
operands: JavaScript's `String` representation of the decoded IEEE value.
|
||||
|
||||
For `ldtoken` MemberRef operands, `operand_kind` follows the parsed member
|
||||
signature (`field` or `method`); if that signature does not establish the kind,
|
||||
the anchor keeps the generic `token` classification.
|
||||
|
||||
Opcode names retain short/long forms and prefix opcodes. This makes the value a
|
||||
reproducible decoded-tuple fingerprint, not a canonical semantic CIL identity:
|
||||
it does not resolve tokens across MVIDs, does not commit full control flow or
|
||||
@@ -506,7 +703,8 @@ exception semantics, and does not replace the raw CIL digest. Cross-build
|
||||
comparison requires equal raw CIL digests as well for exact CIL/signature
|
||||
matches and reports ambiguity when structural evidence is insufficient.
|
||||
|
||||
For example, a decoded `nop; ret` body serializes exactly as
|
||||
For example, a present body (including any valid declared locals signature)
|
||||
whose IL is `nop; ret` serializes exactly as
|
||||
`[["nop","none",null],["ret","none",null]]` and has digest
|
||||
`5e5fad7741cb44bca3a4f045546b7449990da343f612f5f34c0ca30e9eee0636`.
|
||||
This vector specifies the tuple order, `null` handling, absence of whitespace,
|
||||
@@ -539,7 +737,9 @@ runtime, supported host, output contract, and conformance results.
|
||||
Target platform is not host support. The canonical parser must inspect
|
||||
Windows-produced PE/CLI bytes on REA's supported Linux and macOS hosts with the
|
||||
same result. Pinned Windows oracle jobs may provide additional conformance, but
|
||||
they do not claim that the REA application itself supports Windows. Native
|
||||
do not establish that the REA managed analysis workflow has been verified on a
|
||||
Windows host. The [experimental Windows Ghidra boundary](windows-ghidra-p0.md)
|
||||
has its own host and provider verification scope. Native
|
||||
ReadyToRun, C++/CLI, NativeAOT, or IL2CPP coverage is additionally constrained
|
||||
by the selected Hopper/Ghidra host and format matrix.
|
||||
|
||||
@@ -667,6 +867,10 @@ Managed member comparisons report a `metadata` dimension when an observed
|
||||
MethodDef flags or implementation flags value differs, including accessibility
|
||||
or synchronization changes. Observed method-body `init_locals` and `max_stack`
|
||||
differences also contribute to `metadata`, even when CIL decoding is incomplete.
|
||||
For complete bodies, presence or absence of a locals signature also contributes;
|
||||
the locals table token is build-local and is not compared. Local type contents
|
||||
are not currently compared, so this facet does not establish equivalent locals
|
||||
declarations.
|
||||
An unavailable header value keeps body coverage unknown rather than establishing
|
||||
equality or a difference. These comparisons are separate from the CIL/signature
|
||||
matching tiers: identical instructions do not establish unchanged method metadata.
|
||||
|
||||
+236
-43
@@ -21,11 +21,37 @@ as an upstream tool's original report, and is `null` when there is none. The sam
|
||||
record is retained in the session bundle. `analysis_profile` is always present:
|
||||
a concrete profile object or `null`; either value participates in semantic
|
||||
identity. Records omitting this field are rejected. Pass the returned Evidence
|
||||
directly to a compatible comparison tool: `analyze_function` Evidence can be passed
|
||||
directly to `compare_functions`, and `inspect_artifact` Evidence to
|
||||
`compare_artifacts`. Use `get_evidence_bundle` when the task needs broader
|
||||
directly to a compatible comparison tool: complete `analyze_function` Evidence
|
||||
can be passed directly to `compare_functions`, and `inspect_artifact` Evidence
|
||||
to `compare_artifacts`. Use `get_evidence_bundle` when the task needs broader
|
||||
retained session history or an explicit bundle for transfer.
|
||||
|
||||
`analyze_function` does not deliver its complete record by default. Without
|
||||
`detail`, it retains the complete dossier in the session and returns
|
||||
`inspect_analysis_view` summary Evidence whose
|
||||
`normalized_result.parent_evidence_id` names that dossier; see
|
||||
[retained analysis Evidence](#retained-application-evidence-inputs). Page the
|
||||
dossier with `inspect_analysis_view`. Tools that consume function dossiers
|
||||
reject the summary record. Give `compare_functions` the retained dossier as
|
||||
`{"kind": "retained-evidence", "evidence_id": "<parent_evidence_id>"}`; give
|
||||
`match_native_functions` the same reference inside `left_observations` or
|
||||
`right_observations`, since its `left` and `right` take `list_procedures`
|
||||
Evidence. Request `detail: "complete"` for `build_call_path`, which accepts
|
||||
only inline complete dossiers.
|
||||
|
||||
Saved records from the pre-6.6 identity projection that omitted
|
||||
`relationship_coverage: null` from its digest remain incompatible, even when
|
||||
the serialized field is present. REA recognizes that exact previous commitment
|
||||
for its diagnostic and keeps rejecting it; the original ID and file are not
|
||||
rewritten. Recreate the observation with the current release. Re-exporting the
|
||||
old bundle cannot update its identity. A nonmatching digest that does not
|
||||
verify under that previous rule remains an ordinary integrity mismatch.
|
||||
|
||||
Application comparison, export-shape comparison and feature tracing preserve
|
||||
the same Evidence authentication failures: an admitted source that fails
|
||||
authentication is `evidence_integrity_mismatch`, while an authenticated source with an unsupported
|
||||
workflow role or invalid selector is `invalid_request`.
|
||||
|
||||
Ghidra inventories and function replies use `raw_result: null` when their facts
|
||||
are already preserved in `normalized_result`, limitations, locations and
|
||||
relationship coverage. Distinct overview details, measured load-image
|
||||
@@ -34,22 +60,36 @@ derived metadata keep their upstream representation. Read function results from
|
||||
`normalized_result`; older retained Evidence keeps its original representation
|
||||
and identity.
|
||||
|
||||
Artifact source-map filename relationships require a unique, identified regular
|
||||
file at the sibling path. A known directory cannot be the mapped file; the map
|
||||
retains its actual container relation. This admission does not establish source
|
||||
byte correspondence or decode the map's original source paths.
|
||||
|
||||
Artifact inventories declare `coverage.scope` (`root` or `container-entries`)
|
||||
and `coverage.status` (`complete` or `unknown`) independently of diagnostic
|
||||
wording. Comparison requires matching scopes, complete graph members and complete
|
||||
traversal coverage before concluding equality. A fully observed root without
|
||||
container children can be `unchanged`; its scope notice stays in `limitations`
|
||||
without creating a gap. Content and executable metadata changes still produce
|
||||
root deltas. Missing graph members yield `truncated`; unknown traversal coverage
|
||||
or unavailable occurrence hashes retain uncertainty. Integrity mismatches remain
|
||||
traversal coverage before concluding equality. Fully observed roots can be
|
||||
`unchanged` within root scope; their scope notice stays in `limitations` without
|
||||
creating a gap. Root-scoped equality establishes equality of the observed root
|
||||
bytes. Child-container presence and unexamined format details remain unresolved,
|
||||
including when the root format is `unknown`. Content and executable metadata
|
||||
changes still produce root deltas. Missing graph members yield `truncated`;
|
||||
unknown traversal coverage or unavailable occurrence hashes retain uncertainty.
|
||||
Integrity mismatches remain
|
||||
contradictions. Static artifact equality does not establish unchanged runtime
|
||||
behavior.
|
||||
Source-map relation deltas include the content identity of the mapped source,
|
||||
so an unchanged map file is reported when its target source bytes change.
|
||||
|
||||
Exhausted traversals with deliberately omitted filesystem entries retain
|
||||
`unknown` coverage and identify each omitted path/type in `limitations`; FIFO,
|
||||
socket and device entries are not opened. Case-distinct archive members remain
|
||||
separate observations. Their case-insensitive materialization diagnostic does
|
||||
not reduce traversal coverage when both spellings were read.
|
||||
PyInstaller inventory also preserves distinct table rows that share a logical
|
||||
path, including decoded children beneath a colliding file/directory path.
|
||||
Inventory coverage describes observed archive facts; extraction still refuses
|
||||
destinations where those paths cannot coexist.
|
||||
|
||||
Current inventory Evidence requires scoped `coverage` with a `complete` or
|
||||
`unknown` status. Consumers use this producer-owned fact and reject records
|
||||
@@ -80,6 +120,12 @@ can observe backend recovery; healthy dossiers keep their existing cache policy.
|
||||
`procedure_pseudo_code` still fails for that function. Hopper's unavailable reference-kind metadata and reported unresolved
|
||||
calls still prevent equality conclusions for those dimensions. Analysis profiles
|
||||
version these declarations so older snapshots are not reused as current results.
|
||||
For `strings_names`, a string marked with `decoding.available: false` keeps its
|
||||
provider display text in the source Evidence, but that text is not compared as
|
||||
decoded content. If no independently known string, name, or literal fact differs,
|
||||
the dimension remains `unknown` with the decoding reason. Known content and typed
|
||||
string-object differences can establish a change; missing typed metadata remains
|
||||
unknown, and `encoding_status` describes provenance rather than content.
|
||||
IDA declares the facets its selected adapter cannot provide; an empty
|
||||
unsupported array does not prove observed absence.
|
||||
Evidence and bundle imports reject unsupported fields in their envelopes,
|
||||
@@ -95,11 +141,15 @@ optional bounded `recovery` next step. `recovery.kind=inspect_input_contract`
|
||||
names the rejected operation: re-read that operation's advertised input schema
|
||||
(call `tools/list` if the catalog may be stale), correct the listed fields, and
|
||||
do not repeat the unchanged call. `recovery.kind=inspect_timeout_state` names
|
||||
the operation and the REA server/provider deadline; inspect partial Evidence
|
||||
and reported cleanup state, then follow producer-specific remediation or reduce
|
||||
supported work. Generic errors do not assume adjustable timeout controls. Raising
|
||||
only the MCP client's deadline cannot extend this budget. These hints never
|
||||
include the complete catalog or caller arguments.
|
||||
the operation and the REA server/provider deadline. Inspect partial Evidence
|
||||
and reported cleanup state, then follow the returned remediation. Change a
|
||||
request field or REA_* setting only when the returned remediation explicitly
|
||||
recommends that change. Restart REA when that text says the value is read at
|
||||
startup. A mention that a control does not raise the deadline is not a
|
||||
recommendation to change it. When it names no setting, reduce supported work;
|
||||
do not invent one. Raising only the MCP
|
||||
client's deadline cannot extend this budget. These hints never include the
|
||||
complete catalog or caller arguments.
|
||||
Failures omit `structuredContent`, since each advertised `outputSchema`
|
||||
describes successful results. Successful replies retain their schema-validated
|
||||
structured data and matching text projection. Oversized errors are retained
|
||||
@@ -127,6 +177,11 @@ visible. When a requested goal is not reached but an expansion is unresolved,
|
||||
`goal_status` is `unknown`; `not_reached` means traversal exhausted without
|
||||
unresolved relationship reads. Cancellation remains a tool error.
|
||||
|
||||
`trace_feature` validates each reference's containing-procedure resolution using
|
||||
the same contract as navigation context. Explicit absence remains a successful
|
||||
negative observation; malformed provider replies return `unreadable_output`
|
||||
for `resolve_containing_procedure` before successful-result projection.
|
||||
|
||||
REA prepares complete MCP results within the pinned stdio client's 10 MiB
|
||||
receive-buffer budget, including both text and structured representations and
|
||||
room for the JSON-RPC envelope. Tool errors omit `structuredContent` from the
|
||||
@@ -223,6 +278,16 @@ Evidence ID. Distinct results retain independent questions and source links;
|
||||
repeating the same result does not create another unknown. Workflow Evidence
|
||||
keeps the original residual question text. Explicit `record_unknown` requests
|
||||
keep caller-selected identity and reject duplicate creation.
|
||||
Duplicate creation is a nonretryable `invalid_request`; use the existing head
|
||||
or update it with its current `expected_revision`. A stale update remains a
|
||||
retryable `revision_conflict`.
|
||||
|
||||
When `record_unknown` or `update_unknown` commits but its complete reply exceeds
|
||||
the response budget, `details.reported_limits.committed_unknown` identifies the
|
||||
exact unknown ID, committed revision and mutation Evidence IDs. Its retained
|
||||
Evidence reference remains available on the same connection. Export the bundle
|
||||
or page `list_unknowns` to recover the result; do not repeat the committed
|
||||
mutation. A delivery failure does not undo that revision.
|
||||
|
||||
For a portable exported bundle, the CLI shares this projection:
|
||||
`rea inspect-evidence-bundle '{"path":"/tmp/evidence.json","detail":"summary","filters":{"operation":"analyze_function"}}' --format json`.
|
||||
@@ -302,15 +367,19 @@ after target content was read.
|
||||
## Retained application Evidence inputs
|
||||
|
||||
`analyze_javascript_application`, `inspect_binary_layout`, `inspect_artifact`,
|
||||
`inspect_wasm_artifact`, `inspect_macho`, and `decode_interface_builder`
|
||||
accept `detail`:
|
||||
`complete` (default) returns the complete analysis Evidence; `summary` records
|
||||
`inspect_wasm_artifact`, `inspect_macho`, `decode_interface_builder`, and
|
||||
`analyze_function` accept `detail`:
|
||||
`complete` returns the complete analysis Evidence; `summary` records
|
||||
that complete Evidence in the session and returns the derived
|
||||
`inspect_analysis_view` summary Evidence instead. Its
|
||||
`inspect_analysis_view` summary Evidence instead. `complete` is the default
|
||||
except for `analyze_function`, whose dossier can reach several megabytes of
|
||||
value flow and references, so it defaults to `summary`. Its
|
||||
`normalized_result.parent_evidence_id` and `evidence_links` name the retained
|
||||
analysis, so later views and application workflows read it without repeating
|
||||
analysis or moving the complete record through the response. A server without
|
||||
session retention refuses summary detail with `capability_unavailable`.
|
||||
session retention refuses summary detail with `capability_unavailable`;
|
||||
`inspect_artifact`, `inspect_wasm_artifact`, `inspect_macho` and
|
||||
`decode_interface_builder` refuse before any analysis starts.
|
||||
For `inspect_artifact`, omission also keeps the original request parameters and
|
||||
complete output shape. Delivery selection never reaches the inventory engine;
|
||||
the complete inspection and its atomic inventory Evidence remain retained.
|
||||
@@ -400,6 +469,13 @@ unreturned paths are absent. Views carry their own Evidence IDs and preserve
|
||||
links to the original parent; complete producer output and historical inline
|
||||
Evidence input parsing retain their existing shapes.
|
||||
|
||||
An `inspect_artifact` bundle published by the CLI includes its atomic substep
|
||||
Evidence as addressable records alongside the inspection parent. MCP retains
|
||||
the same source closure. After importing that bundle, select either the parent
|
||||
or an atomic inventory by its retained Evidence ID without reopening the target.
|
||||
Unsupported view families report an input issue at `view`; an invalid source
|
||||
reports its issue at `source`.
|
||||
|
||||
Completed `inspect_wasm_artifact` Evidence supports a summary and
|
||||
`view: {kind: "wasm", facet: "exports", offset: 0, limit: 64}`. Other facets
|
||||
are `sections`, `imports`, `candidates`, `glue`, `wat`, `headers`, `details`, and
|
||||
@@ -540,7 +616,14 @@ should expand `tool_availability.groups` and use `tools/list` for schemas. The
|
||||
same catalog identity summary is returned by CLI `capabilities` and `providers`;
|
||||
MCP tool definitions remain available through `tools/list`.
|
||||
|
||||
Provider capabilities are grouped by provider identity and shared facts. In each
|
||||
Provider capabilities are grouped by provider identity and shared facts.
|
||||
Tool families group discovery; they do not imply an active-binary prerequisite.
|
||||
Contracts whose handlers dispatch through that session declare it explicitly.
|
||||
Cutter, GDB and Rizin debugger tools use their independent session/input owners,
|
||||
so discovery does not ask them to open an unrelated binary. Session tool effects
|
||||
also retain the applicable provider composition's UI, process, network and
|
||||
filesystem declarations; for example, attached IDA navigation may show UI while
|
||||
a headless profile launches a worker. In each
|
||||
set, `shared_limitations` is the ordered common prefix; append a group's ordered
|
||||
`limitations` suffix to recover each operation's exact limitations. Group
|
||||
`effects` arrays use the same true-only closed-vocabulary rule as tool
|
||||
@@ -573,6 +656,12 @@ Binary-only operations and binary analysis snapshots do not apply in this
|
||||
session. `open_binary` can switch back to executable analysis, and
|
||||
`close_binary` retires either session kind.
|
||||
|
||||
`inspect_plist` requires an open binary or bundle-resource session. An explicit
|
||||
`path` selects a plist in that context and does not create a session;
|
||||
`binary_session` reports `target_required` while neither session is open.
|
||||
Use `open_bundle_resources` for a bundle whose executable cannot be resolved.
|
||||
The CLI `rea inspect-plist <path>` opens its selected artifact for one invocation.
|
||||
|
||||
Each provider client receives `binary_session.analysis_run.run_id` before any
|
||||
provider startup, including clients whose startup fails. `process_lineage` is
|
||||
`not_observed` until a dynamic provider starts, then becomes `snapshots` with
|
||||
@@ -600,6 +689,15 @@ error's reported resources.
|
||||
|
||||
## Transports
|
||||
|
||||
The eager stdio runtime owns SIGINT, SIGTERM and stdin EOF before opening an
|
||||
initial target or loading optional providers. Shutdown cancels the initial open,
|
||||
waits for late startup handles, and closes them through the same retryable owner
|
||||
as a running server, retaining provider documents. Incoming stdio bytes during
|
||||
startup are staged without parsing, within the installed SDK's stdio buffer
|
||||
capacity, then handed unchanged to its single framing owner. Exceeding that
|
||||
startup capacity ends startup with a transport resource constraint; it has no
|
||||
runtime override. HTTP startup never reads stdin.
|
||||
|
||||
stdio is the default transport and the only one that client registrations,
|
||||
`rea mcp doctor` and the MCP registry entry describe. `rea mcp --http [--host H]
|
||||
[--port N]` is an opt-in alternative that serves the same catalog over the
|
||||
@@ -677,7 +775,7 @@ the bound address is local; names are added only after classifying the actual IP
|
||||
On such a bind, requests whose `Host` header is outside the SDK loopback allowlist plus
|
||||
the configured name and the bound address are refused with `403`, and a browser
|
||||
`Origin` outside that allowlist is refused the same way (a missing `Origin`
|
||||
passes); a non-loopback bind relaxes both checks and logs the relaxation. A reply sent before a request body was read (a refused `Host` or `Origin`, an unknown route, the startup `503`) closes the connection after the reply, so an unread upload cannot keep a socket open. Request targets are taken verbatim as origin-form paths (`//host/path` is a path, not another origin); an absolute-form target, a target containing a raw fragment delimiter (`#`), or one whose path URL parsing would rewrite (dot segments such as `/public/../mcp`), is refused with `400`, and a method Fetch does not support with `405`. Percent-encoded `%23` remains valid query data. Only a lone `initialize` can replace an open session; a batch that carries one is left to the transport's refusal. Scoped
|
||||
passes); a non-loopback bind relaxes both checks and logs the relaxation. A reply sent before a request body was read (a refused `Host` or `Origin`, an unknown route, the startup `503`) closes the connection after the reply, so an unread upload cannot keep a socket open. Request targets are taken verbatim as origin-form paths (`//host/path` is a path, not another origin); an absolute-form target, a target containing a raw fragment delimiter (`#`), or one whose path URL parsing would rewrite (dot segments such as `/public/../mcp`), is refused with `400`, and a method Fetch does not support with `405`. Percent-encoded `%23` remains valid query data. Only an `initialize` object can replace an open session. A sessionless array containing initialize, including a one-element array, is refused with HTTP `400` before session creation or handover. Scoped
|
||||
IPv6 literals (`fe80::1%eth0`) are refused before binding, because no URL can
|
||||
name them. Listener errors reach the runtime's connection-loss handler only
|
||||
after a successful bind; a bind failure prints the listen-failure message alone. Request bodies are bounded by the SDK's
|
||||
@@ -810,6 +908,11 @@ temporary source files are declared as `launchesProcess` and `writesFilesystem`,
|
||||
including for a single JavaScript source. This internal analysis does not execute
|
||||
the supplied application code.
|
||||
|
||||
`analyze_web_bundle` also declares `launchesProcess` and `writesFilesystem`:
|
||||
analysis of captured sources starts an owned worker and transfers source through
|
||||
private temporary files. Its network and session effects remain declared, and
|
||||
the worker does not execute the captured application code.
|
||||
|
||||
## Static Ghidra bytes and source mappings
|
||||
|
||||
`read_bytes` returns initialized analysis memory, including loader fixups, rather
|
||||
@@ -855,6 +958,13 @@ deadlines remain unchanged. Doctor reports an informational recommendation
|
||||
when a configured deadline is shorter than the known provider allowance. An
|
||||
explicit JEB upstream deadline (`REA_JEB_MCP_TIMEOUT_MS`, shared by catalog
|
||||
discovery and each tools/call) also contributes to that allowance.
|
||||
An explicit `REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS` is added to the Ghidra startup
|
||||
allowance as a single-decompile estimate. A multi-function call needs a client
|
||||
deadline covering every attempted decompile plus other work; the static
|
||||
registration value does not bound an arbitrary batch or `trace_native_values`
|
||||
traversal. Split batches, use `decompile_all` limit/resume, or reduce the trace
|
||||
`max_functions` to select work for the whole-call deadline. Existing caller
|
||||
deadlines remain unchanged.
|
||||
|
||||
For an already connected client using the pinned SDK, request options are the
|
||||
**second** argument of `callTool`:
|
||||
@@ -962,12 +1072,33 @@ current/containing procedure. Its capability inventory exposes a
|
||||
`current_selection` mode and an `explicit_document` mode; the latter works when
|
||||
the caller supplies `document` and the provider lacks `current_document`. A
|
||||
cursor outside a procedure is represented as
|
||||
`procedure: null`. `inspect_address_context` requires an explicit address and
|
||||
`procedure: null` only after a valid explicit absence result. Malformed
|
||||
procedure resolution returns typed unreadable output; provider errors never
|
||||
become absence from their message text. `inspect_address_context` requires an explicit address and
|
||||
returns name, procedure, comment, inline-comment, and bookmark facets;
|
||||
unsupported facets are local `unavailable` outcomes. Use `current_document`,
|
||||
`current_address`, and `current_procedure` for direct single-field lookups; use
|
||||
the aggregate tools when you need related context together.
|
||||
|
||||
## Integrity attribution and reconstruction claims
|
||||
|
||||
Artifact comparison results include `integrity_mismatch_evidence_ids`, naming
|
||||
the actual inventory Evidence carriers with declared/observed integrity
|
||||
mismatches. Argument order does not assign blame. The field is absent on older
|
||||
comparison records, where culprit attribution remains unknown. Automatically
|
||||
retained artifact unknowns use these source identities for contradiction links.
|
||||
|
||||
Static/runtime correlation preserves a selected contradiction or unresolved
|
||||
conclusion without treating it as an observed content change. Such a selection
|
||||
produces an unresolved pattern and cannot support a cochange hypothesis.
|
||||
Ordinary observed content changes remain eligible for explicit hypotheses;
|
||||
correlation does not establish causality.
|
||||
|
||||
An `overall` structural-function reconstruction claim selects every constituent
|
||||
function dimension and its active scoped unknowns. A question scoped only to
|
||||
pseudocode therefore keeps the aggregate unknown while an independent assembly
|
||||
claim may pass. Unknown citations and recommended probes are retained.
|
||||
|
||||
## Integrity record-and-continue
|
||||
|
||||
Artifact integrity fails closed by default. A request can explicitly select
|
||||
@@ -991,6 +1122,32 @@ bytes and returns the `integrity_contradictions` records for the extracted
|
||||
files. An active ASAR unpacked entry whose companion file is absent cannot be
|
||||
materialized; extraction fails as `unavailable` with that logical path.
|
||||
|
||||
If native DMG child traversal is unavailable after carrier identification,
|
||||
the typed failure retains a revalidated root-only inventory under
|
||||
`details.partial_observation.inventory`, with unknown child coverage and the
|
||||
producer's diagnostics/provenance. It does not borrow or detach an existing
|
||||
attachment. Native checksum verification failures still refuse traversal;
|
||||
`record-and-continue` recovery applies to structured entry contradictions.
|
||||
|
||||
Occurrences use normalized logical paths for identity and lookup. Optional
|
||||
`source_path` retains the original path within the owning reader when Unicode
|
||||
NFC or reader admission changes its spelling; it is source evidence, not an
|
||||
output destination. POSIX letter-colon names such as `a:b` remain valid logical
|
||||
names; Windows extraction refuses names its filesystem cannot materialize.
|
||||
|
||||
Extraction materializes regular files exposed by the active archive reader.
|
||||
Its temporary parent follows the filesystem semantics of `TMPDIR`, including
|
||||
POSIX symlinks followed by `..`. A destination inside a directory source is
|
||||
refused before copying; select a temporary parent outside that source.
|
||||
When that reader exposes a nested archive as a file, extraction copies the
|
||||
archive file; inspect and extract that file separately to unpack its contents.
|
||||
Extraction retains inventory and materialization traversal limitations in its
|
||||
result and outer Evidence. If either traversal has unknown coverage, the
|
||||
observed regular files can still be copied, but the result does not establish
|
||||
a complete source copy. For example, a POSIX filename that is not valid UTF-8
|
||||
remains omitted with its raw-name explanation; it is not replacement-decoded
|
||||
into an output filename.
|
||||
|
||||
Packaging tools commonly sign or strip `.asar.unpacked` native binaries after
|
||||
writing the archive header, so their declared hashes no longer match. Electron
|
||||
does not check unpacked companions against those hashes at runtime. Integrity
|
||||
@@ -1000,13 +1157,12 @@ failures for such entries name this cause and the operation to rerun with
|
||||
## Schema advertisement
|
||||
|
||||
Advertised input and output schemas contain no reachable recursive references.
|
||||
Schemas share repeated definitions through schema-local references while
|
||||
preserving complete nested fields and validation rules. Input schemas whose
|
||||
shared definitions would exhaust a client's declaration-rendering reference
|
||||
budget (Pi 1.1.0 code-mode shares 32 reference expansions across an input and
|
||||
renders every later reference as `unknown`) are re-projected without shared
|
||||
definitions, so every advertised field renders with complete structure.
|
||||
Input properties retain their descriptions and literal examples.
|
||||
Input schemas inline repeated definitions before applying the selected
|
||||
presentation profile. Both `full` and `compact` start from this self-contained
|
||||
projection; output schemas can share schema-local definitions. Full inputs
|
||||
retain nested fields, descriptions and valid literal examples. The compatibility
|
||||
projections below can relax advertised constraints; canonical runtime validation
|
||||
still enforces the complete input contract.
|
||||
|
||||
When a host forwards input schemas to the model, their examples consume context.
|
||||
REA's advertised examples therefore never copy complete producer Evidence.
|
||||
@@ -1018,7 +1174,9 @@ advertised. Every advertised example is a distinct valid canonical input.
|
||||
Root object unions are presented as
|
||||
one object without root `anyOf`, `oneOf`, or `allOf` for model API compatibility.
|
||||
The projection merges properties, retains shared required fields, and
|
||||
describes the accepted input groups. Which fields belong together is guidance
|
||||
describes the accepted input groups. A property default is advertised only when
|
||||
every input group accepts that property with the same default, so default-filling
|
||||
clients cannot inject a scenario-only property into a passive comparison. Which fields belong together is guidance
|
||||
in that description rather than a `dependentRequired` or `dependencies`
|
||||
keyword, because Gemini function declarations reject both and one unknown
|
||||
keyword fails the client's whole tool catalog. Canonical runtime validation
|
||||
@@ -1035,9 +1193,11 @@ masks without a repeating regular expression or temporary byte arrays. The
|
||||
server rejects malformed or unconstrained patterns before provider dispatch;
|
||||
the advertised strings alone do not express those rules.
|
||||
|
||||
Boolean literal constraints in input schemas are relaxed only in this
|
||||
advertisement. For example, a boolean `const` is presented as `type: boolean`;
|
||||
an affected `not` is omitted, conditional predicates and their branches are
|
||||
Boolean and empty-string literal constraints in input schemas are relaxed only
|
||||
in this advertisement. Boolean `const` becomes `type: boolean`; empty-string
|
||||
`const` or an enum containing an empty string is widened without dropping the
|
||||
accepted empty value. Defaults and descriptions remain intact.
|
||||
An affected `not` is omitted, conditional predicates and their branches are
|
||||
omitted together, pure boolean-literal `oneOf` branches collapse to a boolean,
|
||||
and other affected `oneOf` branches are presented as `anyOf` to retain branch
|
||||
guidance without making overlapping widened branches reject inputs.
|
||||
@@ -1142,20 +1302,44 @@ includes all dimension limitations. Equal CFGs establish only equal observed
|
||||
block sizes and successor relationships. Text from different provider
|
||||
identities remains unknown.
|
||||
|
||||
The `references` dimension preserves each edge's direction, endpoint identity
|
||||
and available kind facts. Within one artifact it compares exact raw endpoint
|
||||
coordinates, including the address space; hexadecimal case and zero padding
|
||||
do not change identity. Resolving an endpoint to a containing procedure does
|
||||
not replace the raw coordinate. Unparseable coordinates stay unknown.
|
||||
The `calls` and `references` dimensions share endpoint identity and name
|
||||
ambiguity rules. Calls retain procedure entry coordinates and direction;
|
||||
references retain each edge's raw source and target coordinates, direction and
|
||||
available kind facts. Resolving a reference endpoint to a containing procedure
|
||||
does not replace its raw coordinate. Hexadecimal case and zero padding do not
|
||||
change coordinates; address spaces remain distinct.
|
||||
|
||||
Coordinate equality requires equal artifact digests and formats, a known equal
|
||||
architecture, and matching provider-owned native coordinate commitments. Those
|
||||
commitments retain the exact selected Mach-O image where available and a digest
|
||||
of the loader's mapping inputs. They exclude unrelated analysis settings, so
|
||||
changing an analyzer option alone does not invalidate coordinate correspondence.
|
||||
Equal coordinates in different slices or mappings cannot establish equality.
|
||||
Hopper's default executable profile and Ghidra's supported deterministic imports
|
||||
publish these commitments. Hopper loader overrides and attached databases, IDA
|
||||
profiles, Ghidra universal-image imports without an exact slice commitment, and
|
||||
older or imported Evidence without a commitment leave coordinate identity
|
||||
unknown. Unparseable coordinates also remain unknown.
|
||||
|
||||
Across different artifacts, endpoints use nonempty procedure names whose observed
|
||||
symbol facts establish a named origin. Dynamic/default symbols and unknown origin
|
||||
remain unknown. Ghidra emits the same source/dynamic symbol facts for inventories
|
||||
and procedure identities; Hopper, IDA and older imported dossiers without verified
|
||||
origin facts cannot establish correspondence from display labels. No name-spelling
|
||||
rule classifies `EntryPoint` or a `FUN_` prefix as generated. A name observed at
|
||||
multiple procedure entries retains its label constraint but cannot establish equal
|
||||
endpoints. Same-container records without a common coordinate context likewise
|
||||
cannot establish equality from matching labels.
|
||||
|
||||
Across artifacts, reference endpoints use stable procedure names. Missing or
|
||||
address-generated names remain unknown. A name observed at multiple procedure
|
||||
entries retains its label constraint but cannot establish equal endpoints.
|
||||
Pairing preserves duplicate counts and considers alternative compatible
|
||||
pairings; array order never identifies an edge. Compatible records with
|
||||
unknown endpoint identity or kind facts remain `unknown`. Independent known
|
||||
pairings; array order never identifies an edge. Compatible records with unknown
|
||||
endpoint identity or reference kind facts remain `unknown`. Independent known
|
||||
facts that contradict every possible pairing still establish `changed`.
|
||||
Source Evidence retains the original addresses, names and unavailable facts.
|
||||
Reconstruction verification uses the selected dimension's status: a same-name
|
||||
call retargeting in a common context fails a calls claim; an unresolved slice or
|
||||
mapping cannot pass it. Empty function labels leave `function_match` ambiguous
|
||||
and the identity dimension unknown.
|
||||
|
||||
### Comparison input shapes
|
||||
|
||||
@@ -1166,7 +1350,8 @@ Source Evidence retains the original addresses, names and unavailable facts.
|
||||
the complete `normalized_result` from `discover_webmcp_tools`, or null.
|
||||
- Scenario: `before_scenario` and `after_scenario` contain complete
|
||||
`normalized_result` objects from `capture_browser_scenario`. Optional
|
||||
`normalization` defaults to `{ "rules": [] }`.
|
||||
`normalization` defaults to `{ "rules": [] }` in the canonical scenario parser;
|
||||
this scenario-only default is omitted from the merged advertised input.
|
||||
|
||||
The advertised schema includes complete nested capture fields and rejects
|
||||
incomplete pairs and structurally malformed captures. Mixed comparison families,
|
||||
@@ -1176,6 +1361,14 @@ invoking the handler. Domain validation additionally checks
|
||||
relationships such as event sequence references and retained counts; JSON
|
||||
Schema does not express those cross-field invariants.
|
||||
|
||||
Comparison also preserves the capture's coverage. A partial HTTP inventory can
|
||||
establish an observed difference against a complete peer, but an omitted request
|
||||
does not establish removal. Partial WebSocket streams do not establish frame
|
||||
alignment. WebMCP frame IDs and tool keys are local to a capture; semantic owner
|
||||
facts and known counts determine correspondence. Retained script digests can
|
||||
establish source changes. See [browser comparison semantics](browser-observation.md)
|
||||
for value uncertainty and storage-fingerprint recapture requirements.
|
||||
|
||||
## Generated catalog
|
||||
|
||||
Custom clients upgrading from 6.1 should follow the
|
||||
|
||||
@@ -61,6 +61,11 @@ The server advertises `listChanged: true`. Updating a registered guided prompt
|
||||
emits `notifications/prompts/list_changed`, allowing clients to refresh their
|
||||
cached prompt catalog.
|
||||
|
||||
Schema-invalid parameters to `prompts/get`, `prompts/list`,
|
||||
`completion/complete`, and `tools/list` return JSON-RPC `-32602` with field
|
||||
diagnostics. Correct the named fields and retry on the same connection.
|
||||
Unexpected handler failures retain JSON-RPC `-32603`.
|
||||
|
||||
## Session-aware completion
|
||||
|
||||
MCP `completion/complete` completes guided-prompt arguments; it does not define
|
||||
|
||||
+123
-5
@@ -30,6 +30,11 @@ result records the exact selected path, file size, header length, header SHA-256
|
||||
bytes read and read scope. GGUF alignment padding is outside this prefix.
|
||||
`artifact.sha256` and the Evidence subject are null: a header digest is not a
|
||||
commitment to the weights. Different files can have identical headers and sizes.
|
||||
The reported GGUF tensor-data offset follows the aligned tensor directory when
|
||||
padding is present. For zero-tensor metadata-only files that end at the
|
||||
directory, it is the directory end; no tensor payload is implied. A partial
|
||||
alignment gap is rejected. `header_length` still ends at the directory, before
|
||||
any padding.
|
||||
|
||||
`hash: "full"` additionally streams the remaining file bytes through SHA-256.
|
||||
It returns a full-file digest and Evidence subject, while retaining the same
|
||||
@@ -39,13 +44,34 @@ check descriptor and selected-path identity and metadata before completion;
|
||||
this detects observed replacement or modification, not adversarial modification
|
||||
that defeats filesystem metadata checks.
|
||||
|
||||
If full-file hashing fails or is cancelled after either format's header has
|
||||
been validated, partial Evidence retains the parsed metadata and tensor directory.
|
||||
Header coverage is complete; digests and final file identity remain unknown.
|
||||
Safetensors header parsing is atomic, so interruption before validation does not
|
||||
expose unvalidated JSON rows.
|
||||
|
||||
## Facts and selected queries
|
||||
|
||||
GGUF reports version, byte order, alignment, typed metadata, and each tensor's
|
||||
name, original type ID, dimensions, relative data offset, file offset and derived
|
||||
byte length. Alignment accepts every positive UINT32 multiple of eight, including
|
||||
non-powers of two. Metadata keys use ASCII lower-snake-case hierarchy components;
|
||||
non-powers of two. Partial Evidence retains explicit alignment once validated;
|
||||
the default alignment becomes known after metadata completes. Cancellation
|
||||
retains completed declarations with unknown digests and unverified final file
|
||||
identity. Metadata keys preserve their spelling and use lowercase
|
||||
ASCII alphanumeric components separated by dots, underscores or hyphens;
|
||||
standard numeric components such as `general.base_model.0.name` are preserved.
|
||||
Hyphenated components are retained for compatibility with the published
|
||||
llama.cpp v0.7.0 writer, which emits keys such as
|
||||
`gpt-oss.context_length` and `wavtokenizer-dec.context_length`; the current
|
||||
[GGUF specification](https://github.com/ggml-org/ggml/blob/master/docs/gguf.md#standardized-key-value-pairs)
|
||||
restricts `general.architecture` values to letters and digits.
|
||||
This documents producer output that conflicts with that written restriction,
|
||||
not a claim that the specification permits hyphens. See the pinned
|
||||
[WAVTOKENIZER_DEC](https://github.com/ggml-org/llama.cpp/blob/620d2225cc436ab0b001e3200e590e3b286ef9ab/gguf-py/gguf/constants.py#L1447)
|
||||
and [GPT_OSS](https://github.com/ggml-org/llama.cpp/blob/620d2225cc436ab0b001e3200e590e3b286ef9ab/gguf-py/gguf/constants.py#L1466)
|
||||
architecture names, [context-length key template](https://github.com/ggml-org/llama.cpp/blob/620d2225cc436ab0b001e3200e590e3b286ef9ab/gguf-py/gguf/constants.py#L110),
|
||||
and [writer method](https://github.com/ggml-org/llama.cpp/blob/620d2225cc436ab0b001e3200e590e3b286ef9ab/gguf-py/gguf/gguf_writer.py#L698-L699).
|
||||
Scalar metadata is complete. Arrays have declared element types,
|
||||
counts and at most eight preview elements per array, with an explicit
|
||||
`preview_complete` flag. This includes tokenizer vocabularies: omitted values
|
||||
@@ -55,11 +81,101 @@ Nonfinite floating metadata uses `NaN`, `+Infinity` or `-Infinity` with its
|
||||
declared float type; negative zero is `"-0"`. Unknown tensor types retain their numeric IDs and null byte
|
||||
lengths; affected totals and storage validation remain unknown.
|
||||
|
||||
Q8_1 (type ID 9) has two released layouts: 40 bytes per 32 elements in
|
||||
[llama.cpp b1500](https://github.com/ggml-org/llama.cpp/blob/57ad015dc3011b046ed5a23186c86ea55f987c54/ggml-quants.h#L49-L55)
|
||||
and 36 bytes in the pinned
|
||||
[modern ggml layout](https://github.com/ggml-org/ggml/blob/da8e2a54b919557b816b1c2beb432ccb766f7ce0/src/ggml-common.h#L242-L253). Both use GGUF v3 and
|
||||
quantization version 2, so those declarations do not select a storage layout.
|
||||
REA retains the known type, shape, parameter count and starting offsets, with
|
||||
`byte_length: null`, `storage_validation: "unknown"`, partial storage/range
|
||||
coverage and an explicit limitation for nonempty Q8_1 tensors. Per-type and
|
||||
overall byte totals remain unknown where affected. Padding, the next tensor's
|
||||
offset and file size do not establish a layout; retained queries preserve the
|
||||
same uncertainty. Empty Q8_1 tensors still have a derived zero-byte length,
|
||||
and the known 32-element row-block constraint remains enforced.
|
||||
|
||||
K-quantization IDs 10–15 also have incompatible released layouts. The
|
||||
[b1500 build option](https://github.com/ggml-org/llama.cpp/blob/57ad015dc3011b046ed5a23186c86ea55f987c54/CMakeLists.txt#L88)
|
||||
`LLAMA_QKK_64` selects `GGML_QKK_64`; the same release's default and
|
||||
[b3001](https://github.com/ggml-org/llama.cpp/blob/c429b33beb35f13934a4dfbe0c138d30b45e5d54/ggml-common.h)
|
||||
use 256-element blocks. The
|
||||
[b1500 structures](https://github.com/ggml-org/llama.cpp/blob/57ad015dc3011b046ed5a23186c86ea55f987c54/ggml-quants.h#L61-L169)
|
||||
give these source-derived sizes:
|
||||
|
||||
| ID | Stable name | Bytes per 64 elements, K64 | Bytes per 256 elements, K256 | Padded extent for 256 elements, either layout |
|
||||
| --- | ----------- | -------------------------- | ---------------------------- | --------------------------------------------- |
|
||||
| 10 | Q2_K | 24 | 84 | 96 |
|
||||
| 11 | Q3_K | 28 | 110 | 128 |
|
||||
| 12 | Q4_K | 38 | 144 | 160 |
|
||||
| 13 | Q5_K | 46 | 176 | 192 |
|
||||
| 14 | Q6_K | 54 | 210 | 224 |
|
||||
| 15 | Q8_K | 76 | 292 | 320 |
|
||||
|
||||
Both builds write GGUF v3 and quantization version 2. The
|
||||
[quantizer](https://github.com/ggml-org/llama.cpp/blob/57ad015dc3011b046ed5a23186c86ea55f987c54/llama.cpp#L7430-L7492)
|
||||
writes `general.file_type` directly from the selected `llama_ftype`, without
|
||||
encoding the K64 build option. `GGML_QNT_VERSION_FACTOR` is defined in the
|
||||
header but is not used by this GGUF serialization path. Global file type also
|
||||
does not identify individual tensor types: the quantizer
|
||||
[retains unselected tensors](https://github.com/ggml-org/llama.cpp/blob/57ad015dc3011b046ed5a23186c86ea55f987c54/llama.cpp#L7561-L7586).
|
||||
The [writer pads each payload](https://github.com/ggml-org/llama.cpp/blob/57ad015dc3011b046ed5a23186c86ea55f987c54/ggml.c#L19549-L19565)
|
||||
to the default 32-byte alignment; the final column shows why even the same file
|
||||
extents need not distinguish these layouts.
|
||||
REA preserves each K type's name and enforces the common 64-element row
|
||||
multiple, which does not establish a storage block size. Nonempty K tensor
|
||||
lengths and affected totals are now unknown, including common modern files.
|
||||
Empty K tensors still have derived zero-byte storage.
|
||||
|
||||
Integer IDs also shifted each time IQ types were added. These released
|
||||
`ggml.h` snapshots all declare GGUF v3 and quantization version 2:
|
||||
|
||||
| Released snapshot | I8 ID | I16 ID | I32 ID |
|
||||
| --------------------------------------------------------------------------------------------------- | ----- | ------ | ------ |
|
||||
| [b1500](https://github.com/ggml-org/llama.cpp/blob/57ad015dc3011b046ed5a23186c86ea55f987c54/ggml.h) | 16 | 17 | 18 |
|
||||
| [b1803](https://github.com/ggml-org/llama.cpp/blob/36e5a08b203542dca53cca4eaf172c5dc4bbc991/ggml.h) | 17 | 18 | 19 |
|
||||
| [b1871](https://github.com/ggml-org/llama.cpp/blob/bb0c1392479398f9aba86d9ec98db0b95ede6e6d/ggml.h) | 18 | 19 | 20 |
|
||||
| [b2100](https://github.com/ggml-org/llama.cpp/blob/4aa43fab569215a13495a7f1a0f8afc541b16d03/ggml.h) | 19 | 20 | 21 |
|
||||
| [b2172](https://github.com/ggml-org/llama.cpp/blob/bd2d4e393b2b7d2a1b2e201058e26017c9728ead/ggml.h) | 20 | 21 | 22 |
|
||||
| [b2221](https://github.com/ggml-org/llama.cpp/blob/a14679cc30c785e75d38028bae6ec39c6209ddef/ggml.h) | 21 | 22 | 23 |
|
||||
| [b2253](https://github.com/ggml-org/llama.cpp/blob/4c4cb30736582cacb1a164a9d4bc8e17b1014be7/ggml.h) | 22 | 23 | 24 |
|
||||
| [b2275](https://github.com/ggml-org/llama.cpp/blob/a33e6a0d2a66104ea9a906bdbf8a94d050189d91/ggml.h) | 23 | 24 | 25 |
|
||||
| [b2300](https://github.com/ggml-org/llama.cpp/blob/6ea0f010ff6967034528d9e0b8330b9b0f0b7c13/ggml.h) | 24 | 25 | 26 |
|
||||
|
||||
[Upstream PR 6050](https://github.com/ggml-org/llama.cpp/pull/6050) later pinned
|
||||
integer values explicitly. Modern IDs 16–23 mean IQ2_XXS, IQ2_XS, IQ3_XXS,
|
||||
IQ1_S, IQ4_NL, IQ3_S, IQ2_S and IQ4_XS, respectively. Thus every ID 16–25 has
|
||||
multiple released meanings. REA keeps its numeric `type_id`, uses
|
||||
`AMBIGUOUS_<id>` for `type`, and records the released candidate names in a
|
||||
limitation. Nonempty byte lengths are unknown. Empty declarations still derive
|
||||
zero-byte storage, while their textual identity remains ambiguous. Type filters
|
||||
match the reported `AMBIGUOUS_<id>` label. ID 26 remains I32 across these
|
||||
snapshots; later stable entries retain their existing interpretation. This audit
|
||||
does not establish compatibility with every producer fork or future revision.
|
||||
|
||||
The inventory does not select a historical layout from model names, global
|
||||
file types, padding or guessed producer provenance. A source revision together
|
||||
with its build configuration can establish an external layout fact; these
|
||||
headers do not carry that identification, and REA currently has no layout
|
||||
selection input. Authored regression fixtures use the source-derived sizes
|
||||
above; historical executables were not run.
|
||||
|
||||
Safetensors reports the header extent, exact string `__metadata__` values, dtype,
|
||||
shape and declared data ranges. JSON duplicate keys, invalid UTF-8, out-of-file
|
||||
shape and declared data ranges. A null `__metadata__` is treated as omitted;
|
||||
unknown fields on tensor entries are ignored within the reader's 32-level JSON
|
||||
nesting limit, while the required dtype, shape and data offsets are still
|
||||
validated. JSON duplicate keys, invalid UTF-8, out-of-file
|
||||
ranges, overlaps, gaps, trailing unindexed data and known dtype/shape byte-length
|
||||
contradictions are rejected with a typed `file_format_rejected` constraint (public code `target_unavailable`, category `unsupported_target`). Empty
|
||||
tensors and scalar shapes are supported; packed sub-byte types require whole
|
||||
contradictions are rejected with a typed `file_format_rejected` constraint (public code `target_unavailable`, category `unsupported_target`).
|
||||
A missing, non-regular or otherwise unusable selected path is also `target_unavailable`;
|
||||
host I/O or resource failures on a valid selection, such as `EMFILE` or `EIO`, are
|
||||
`execution_failure` with the path and reason in `details.diagnostics`. Empty
|
||||
tensors and scalar shapes are supported. The reader retains shape dimensions as
|
||||
exact unsigned 64-bit values, including values above JavaScript's safe-integer
|
||||
range when a zero extent makes storage empty. Shape products are checked in
|
||||
declaration order, as in the
|
||||
[64-bit upstream reader](https://github.com/huggingface/safetensors/blob/e246a2560645b7525f5775669ed816eb57c5bcc8/safetensors/src/tensor.rs#L604-L609);
|
||||
a later zero does not erase an earlier product overflow. Actual file and data
|
||||
offsets must still fit exact host offset precision. Packed sub-byte types require whole
|
||||
byte storage. Unknown dtypes retain declared ranges and an explicit limitation
|
||||
that shape-to-storage validation is unavailable. Each tensor records
|
||||
`storage_validation: "consistent" | "unknown"`; `coverage.storage_sizes` remains
|
||||
@@ -76,7 +192,9 @@ GGUF supports zero extents and scalar shapes, with at most four dimensions;
|
||||
known quantized types still require complete blocks in the first dimension.
|
||||
|
||||
MCP defaults to a compact summary with counts, selected short headline metadata,
|
||||
chat-template presence and derived totals. The full type histogram and all rows
|
||||
chat-template presence (default or named template content) and derived totals.
|
||||
A names-only `tokenizer.chat_templates` inventory does not establish template
|
||||
content. The full type histogram and all rows
|
||||
remain in complete Evidence. Headline metadata uses short keys/types and scalar
|
||||
values; long or compound values remain available through explicit metadata
|
||||
queries. Use the returned `parent_evidence_id`:
|
||||
|
||||
@@ -19,15 +19,25 @@ session references as `left` and `right`:
|
||||
}
|
||||
```
|
||||
|
||||
Unique exact reported names are paired first. Optional `list_names` Evidence
|
||||
identifies dynamic/default names to exclude. If origin is unavailable, the
|
||||
existing automatic-name exclusion rule applies and each function retains that
|
||||
limitation. Exact names establish name correspondence, not unchanged code or
|
||||
original-source identity.
|
||||
Unique exact names with observed symbol origin are paired first. Inventory rows
|
||||
or optional `list_names` Evidence identify dynamic/default names to exclude.
|
||||
Unknown or conflicting origin does not establish name correspondence; complete
|
||||
independent body bytes remain eligible. Exact names establish name correspondence,
|
||||
not unchanged code or original-source identity. Ghidra publishes its symbol facts;
|
||||
Hopper, IDA and older imported dossiers retain unknown origin when no such facts
|
||||
were observed. Display spelling alone, including `EntryPoint` or `FUN_` prefixes,
|
||||
does not establish whether a name was generated.
|
||||
|
||||
Authentic Evidence selected for the wrong side, role, target, provider, profile
|
||||
or document returns an input error naming the selected field or observation
|
||||
index. Re-select compatible observations; authentication or contradictory producer
|
||||
facts retain their separate integrity failures.
|
||||
|
||||
For unmatched candidates, optionally supply `left_observations` and
|
||||
`right_observations`. `procedure_info` or existing `analyze_function` Evidence
|
||||
gives exact inclusive body ranges; `read_bytes` supplies memory bytes. Observations
|
||||
`right_observations`. `procedure_info` or existing `analyze_function` dossiers
|
||||
give exact inclusive body ranges; `read_bytes` supplies memory bytes. An MCP
|
||||
`analyze_function` summary reply is not a dossier: pass its
|
||||
`parent_evidence_id` as a `retained-evidence` reference instead. Observations
|
||||
must belong to the side's exact target/provider/profile and document context.
|
||||
Adjacent, consistent overlapping and partially returned reads compose by their
|
||||
observed extent; conflicting intervals remain gaps. A byte match requires every
|
||||
@@ -78,9 +88,9 @@ inventory Evidence and the owning inventory’s `source_index`, preserving same-
|
||||
One validation, lazy member index and bounded recent filter are retained per
|
||||
immutable parent; no live target state is cached.
|
||||
|
||||
When both functions already have unambiguous `analyze_function` dossiers, a matched
|
||||
relation includes `compare_functions_input` with directly usable retained
|
||||
references. Otherwise analyze just the selected canonical addresses in their
|
||||
When both functions already have unambiguous `analyze_function` dossiers in the
|
||||
observations, a matched relation includes `compare_functions_input` with
|
||||
directly usable retained references. Otherwise analyze just the selected canonical addresses in their
|
||||
respective targets, then compare those dossiers. Matching never claims behavioral
|
||||
equivalence. Optional informative CFG/call/string fingerprints from supplied
|
||||
dossiers connect unresolved candidate groups only. They require observed CFG,
|
||||
@@ -110,6 +120,11 @@ extract its matching Evidence record to a JSON file for the existing CLI
|
||||
Representation admission is bounded at 96 MiB before cloning selected observations
|
||||
and expanding matching indexes and repeated per-function source links. Work yields to cancellation. No worker/provider
|
||||
concurrency is introduced; this budget is not a bound on total process RSS.
|
||||
Summary delivery happens after complete matching Evidence is constructed and
|
||||
retained. It reduces the initial reply, but does not avoid that construction or
|
||||
raise the allowance. There is no runtime override. Repeated provenance remains
|
||||
part of the complete member contract; splitting inventories into separate matches
|
||||
would change global name/body uniqueness and is not an equivalent workaround.
|
||||
|
||||
The self-owned source fixture is [nativeFunctionMatching.c](https://github.com/morluto/rea/blob/main/tests/fixtures/nativeFunctionMatching.c).
|
||||
On Linux x86-64, compile with `cc -O2 -g0 -fno-pie -no-pie -DVERSION=1` and
|
||||
@@ -118,3 +133,5 @@ It includes unchanged/changed code, a byte-identical rename, a removed/added
|
||||
observation, an inlined function, identical-code aliases and a renamed changed
|
||||
function with an observed call/string signature. Analyze without executing the
|
||||
fixture. Full-provider verification still needs caller-supplied Ghidra and Java.
|
||||
|
||||
Malformed inline Evidence is rejected after admission with its selected input field and observation index. A structurally valid record whose authenticated contents or digest disagree remains an integrity failure; changing input diagnostics does not make that record trustworthy.
|
||||
+426
-37
@@ -6,6 +6,15 @@ Use a native provider to discover functions, inspect instructions and pseudocode
|
||||
follow references, and examine database types. Static resources and runtime
|
||||
observation complement that analysis when they answer a specific question.
|
||||
|
||||
`inspect_macho` reports build platforms from the selected slice. Legacy
|
||||
`LC_VERSION_MIN_IPHONEOS`, `LC_VERSION_MIN_TVOS` and
|
||||
`LC_VERSION_MIN_WATCHOS` commands are interpreted with the Mach-O CPU type, so
|
||||
Intel slices report simulator platforms while device CPU slices keep the
|
||||
device platform. A chained-import row ending in `+0x...` is ambiguous in
|
||||
dyld's text output: it may be an addend or part of a literal symbol name. REA
|
||||
keeps the full expression in `raw_name`, sets `name` and `addend` to null, and
|
||||
marks `name_status: "ambiguous"`; it does not synthesize a symbol identity.
|
||||
|
||||
## Start with a function
|
||||
|
||||
Choose an installed provider from [setup](installation.md#hopper). For Ghidra:
|
||||
@@ -87,6 +96,14 @@ After metadata edits, snapshot saves and imports remain unavailable in that
|
||||
session. Use `export_evidence_bundle` to retain the observations, close without
|
||||
`snapshot_path`, and reopen the target before importing or saving a snapshot.
|
||||
|
||||
Hopper executable opens use an owned private image verified against the digest
|
||||
selected by `open_binary`. The ordinary ELF/PE path and configured loader overrides
|
||||
copy the whole source; default Mach-O opens retain their selected-image checks.
|
||||
A changed source fails before the launcher reads it. The bridge binds its Hopper document to that private
|
||||
loaded path, while Evidence retains the selected original artifact identity. Hopper
|
||||
database opens retain their separate database semantics. Real Hopper conformance for ordinary
|
||||
copies and loader overrides remains unverified by the source regression tests.
|
||||
|
||||
Ghidra checks its private import copy against the artifact digest selected by
|
||||
`open_binary`. If the source changes before that copy is acquired, the query
|
||||
reports `artifact_changed` with the selected path and both digests. Reopen the
|
||||
@@ -166,6 +183,54 @@ remains valid. This tool
|
||||
has no dedicated CLI command; the existing `decompile` command remains the
|
||||
single-procedure CLI operation.
|
||||
|
||||
Each batch schedules one provider read at a time, but has no shared input-count
|
||||
or aggregate retained-output allowance. It retains every requested row and full
|
||||
pseudocode body through Evidence construction. The MCP response budget checks
|
||||
complete delivery afterward; it does not bound provider work or retained batch
|
||||
state. Select small explicit batches when using this workflow. A general batch
|
||||
admission/output policy, including completed per-item facts when capacity is
|
||||
exhausted, remains unresolved.
|
||||
|
||||
`decompile_all` (`rea decompile-all <target> --output <directory>`) writes one
|
||||
pseudocode file per analyzed function and returns only a manifest: counts, the
|
||||
output directory, capped failure reasons, provider identity, and the
|
||||
analysis-profile digest. The manifest and `decompile-index.json` do not contain
|
||||
pseudocode. Each file records the provider name, canonical address, inclusive
|
||||
body ranges when `procedure_info` supplies them, and the `procedure_info` and
|
||||
pseudocode limitations for that function. Header values are abbreviated to
|
||||
2,000 characters per value and the joined limitation line to 4,000 characters;
|
||||
the index retains the complete selected names, addresses, provider facts and
|
||||
caveats. Failure details retain at most 32 rows within 2,048 JSON-encoded
|
||||
UTF-8 bytes; names may be omitted to keep complete addresses and reasons. The
|
||||
index retains every failure and its complete observed name.
|
||||
Each function's `decompile-index.json` record, whatever its status, keeps the
|
||||
complete limitations of the observations it used. The returned Evidence keeps
|
||||
at most eight first-observed distinct per-function provider limitations within an 8,000-byte JSON-encoded UTF-8 text budget and counts omitted observations. Repeated omitted text is counted per observation, not as a distinct caveat. This summary does not retain every omitted string in memory. A fresh output directory must not already exist. `--resume`
|
||||
continues an export for the same target SHA-256, provider, analysis profile,
|
||||
and layout; it requires the directory to exist, and an export without a known
|
||||
target or profile digest cannot be resumed. Resume skips files whose bytes still match the last write the journal
|
||||
recorded, even after a later read failure, tries
|
||||
functions it has not attempted before retrying recorded failures, ignores a
|
||||
journal line torn by an interrupted write, and never overwrites a conflicting
|
||||
file. File reservations compare ASCII paths without case, so distinct address
|
||||
spaces remain distinct on case-insensitive filesystems. A resumed journal with
|
||||
conflicting reservations is refused. One function's decompiler error or deadline is counted and does not stop
|
||||
the others. A function that `procedure_info` classifies as external, such as an
|
||||
import, is counted as `external` and recorded with that status and the body
|
||||
fact as observed. It gets no file, no pseudocode is requested, and a resume
|
||||
skips it. A function whose classification is unknown is decompiled as usual;
|
||||
a provider that returns null pseudocode for it is reported as `no_pseudocode`. Inventory rows without an address are counted in the limitations. `--layout dwarf`
|
||||
reports that compilation-unit paths are unavailable and still names files by
|
||||
address. Filters are a case-sensitive name substring, a case-sensitive name
|
||||
prefix, an inclusive address range (including uppercase hexadecimal input), and a positive safe-integer attempt limit. The limit does not allocate space in proportion to its value. Cancellation leaves
|
||||
files already written in place. One run at a time holds an output directory,
|
||||
identified by its device and inode, through export finalization and any retained cleanup retry; another
|
||||
run on the same directory is refused with the holder's process ID, and a
|
||||
crashed run can be recovered by the next run. Failed descriptor or staging cleanup remains owned by the session; close the target to retry it. Initialization cleanup preserves unexpected entries rather than recursively removing the directory. Resume hashes existing files in bounded chunks, and index generation reuses committed journal facts already held by the export owner. After five consecutive functions whose files
|
||||
could not be written or read, for example on a full disk, the export stops with
|
||||
an error instead of decompiling the rest. Files and records already committed
|
||||
stay, `decompile-index.json` is still written, and a later resume continues.
|
||||
|
||||
Type inspection selects one exact database pathname or typed data address.
|
||||
Struct/union fields, enums, pointers, arrays, typedefs, size, alignment, packing
|
||||
and bitfields retain database authority. Child types use exact IDs for further
|
||||
@@ -198,18 +263,43 @@ observed source encoding. Hopper can split long literals into adjacent
|
||||
unterminated objects; search matches each object's decoded bytes independently.
|
||||
Undecodable objects retain native display text with `decoding.available: false`
|
||||
and a reason, so one uncertain object does not block unrelated inspections.
|
||||
Function dossiers retain this same string evidence. Native call edges retain
|
||||
Function dossiers and `trace_feature` string matches retain this same string
|
||||
evidence, including decoding uncertainty; procedure matches keep their own
|
||||
named observation fields without invented string qualifications. Tracing keeps
|
||||
same-address search alternatives, including different string encodings, in their
|
||||
original search order and shares one reference read per target address within
|
||||
the call. Reference rows are emitted once per target address, preserving any
|
||||
duplicates in the provider's observation. Native call edges retain
|
||||
Hopper's partial `CallReference` classification and exact endpoints; detailed
|
||||
reference flags remain unavailable rather than being invented.
|
||||
reference flags remain unavailable rather than being invented. Caller and callee
|
||||
procedure inventories come from independent Hopper APIs; REA retains observed
|
||||
relationships but reports their completeness as unknown. In particular, a resolved
|
||||
tail call observed in one direction does not prove that the reverse inventory is
|
||||
complete. An empty Hopper inventory cannot establish that no such call exists.
|
||||
No whole-binary reverse scan is performed.
|
||||
Regex searches use ECMAScript Unicode syntax in a cancellable worker with a
|
||||
five-second matching deadline and a worker old-generation heap. Deadline or
|
||||
cancellation stops matching while leaving the Hopper API available. This
|
||||
release has no runtime override for that deadline. The worker requests 128
|
||||
MiB. A positive Node.js `--max-old-space-size` on the process that starts
|
||||
matching deadline and a worker old-generation heap. Deadline or cancellation
|
||||
stops matching while leaving the Hopper API available. The deadline is
|
||||
`REA_REGEX_MATCHING_DEADLINE_MS`, a positive decimal integer from 1 to
|
||||
2147483647 ms. Unset uses 5000 ms. A larger value is refused because Node
|
||||
schedules a delay past that signed 32-bit bound as 1 ms. Invalid values are
|
||||
configuration errors and do not fall back to the default. The setting is read
|
||||
from the environment that starts REA, so a change needs a restart. A deadline
|
||||
failure names the effective milliseconds, whether they came from the default
|
||||
or the setting, the default, and the maximum. The worker old-generation heap
|
||||
does not raise it. Kuna regex search uses the same deadline. The worker
|
||||
heap request is `REA_REGEX_WORKER_HEAP_REQUEST_MIB`, a decimal integer from
|
||||
7 to 16384 MiB. Unset uses 128 MiB. A request below 7 MiB aborts the worker
|
||||
during startup, and a larger value is refused. Invalid values are
|
||||
configuration errors and do not fall back to 128. The setting is read from
|
||||
the environment that starts REA, so a change needs a restart. A heap failure
|
||||
names the effective request, whether it came from the default or the setting,
|
||||
the default, the floor, and the maximum. A positive Node.js
|
||||
`--max-old-space-size` on the process that starts
|
||||
REA, including `NODE_OPTIONS`, overrides that request and sets the effective
|
||||
heap. `--max-old-space-size-percentage` takes precedence over that flag in
|
||||
either order. REA does not turn the percentage into a MiB, so the effective
|
||||
heap stays unestablished instead of the 128 MiB request or the absolute flag.
|
||||
heap stays unestablished instead of the worker heap request or the absolute flag.
|
||||
The reported `NODE_OPTIONS` value is the process initial environment when that
|
||||
block can be read. A later edit to the environment is not the value Node
|
||||
applied, and an unreadable initial value leaves the effective heap
|
||||
@@ -221,11 +311,11 @@ buffer without a program entry leaves the effective heap unestablished.
|
||||
`--env-file` or `--env-file-if-exists` can supply `NODE_OPTIONS`
|
||||
when the initial environment has none. That file is not part of the initial
|
||||
environment block, so the effective heap stays unestablished instead of the
|
||||
128 MiB request. An initial `NODE_OPTIONS` is still used, because Node does
|
||||
worker heap request. An initial `NODE_OPTIONS` is still used, because Node does
|
||||
not let the file replace it. A positive `--max-heap-size`, including the
|
||||
underscore form and launch `NODE_OPTIONS` on Node versions that allow it,
|
||||
sets the total heap rather than the old generation and can be lower than the
|
||||
128 MiB request. The effective old generation stays unestablished while that
|
||||
worker heap request. The effective old generation stays unestablished while that
|
||||
value is in effect. A zero or empty value clears it.
|
||||
`--experimental-config-file` or `--experimental-default-config-file` can set
|
||||
`nodeOptions`, including a heap percentage, from a file that is not read
|
||||
@@ -288,8 +378,12 @@ nodes and edges. Node payloads are bounded to 8 MiB and serialized results to
|
||||
stable IDs for endpoints outside that page.
|
||||
|
||||
Within one value trace, each exact procedure selector is attempted at most once,
|
||||
including failed analyses. Failures remain unknown observations and do not
|
||||
establish canonical function identity. A new trace can retry those selectors.
|
||||
including failed analyses. A failed seed returns the provider's original typed
|
||||
failure and corrective guidance; it cannot establish a trace or target identity.
|
||||
Failures while expanding a validated seed remain unknown observations and do not
|
||||
establish canonical function identity. A successful seed without a native def-use
|
||||
model still returns its procedure and an explicit coverage unknown. A new trace
|
||||
can retry failed selectors.
|
||||
|
||||
The raw Java `is_dead` flag can remain set after Ghidra decodes a live block;
|
||||
`block_membership` reports actual syntax-tree membership separately.
|
||||
@@ -321,6 +415,41 @@ Mach-O fixtures check each case against source-owned return values.
|
||||
|
||||
## Static inspection
|
||||
|
||||
Thin Mach-O admission and each admitted FAT slice walk declared load-command headers, checking
|
||||
width-specific alignment, command sizes and exact `ncmds`/`sizeofcmds` agreement
|
||||
before opening a provider. Headers beyond the initial probe use another
|
||||
4096-byte window; admission skips command payloads. Native image readers use
|
||||
the same framing owner and perform their own payload checks. This validation
|
||||
does not add a global command-table byte cap.
|
||||
|
||||
Native target admission checks Mach-O slice headers before assigning FAT
|
||||
identity. A shared `CAFEBABE` prefix whose declarations do not identify Mach-O
|
||||
images receives the generic unsupported-format refusal, including JVM class
|
||||
files larger than the bounded admission probe. This does not identify a partial
|
||||
prefix as a validated JVM class. For files sharing `CAFEBABE`, target admission
|
||||
checks complete JVM framing within the same 16 MiB class identity bound as the
|
||||
Mach-O image reader. A complete class remains unsupported even if its constant
|
||||
pool resembles a known FAT CPU declaration. Above that bound, an ambiguous
|
||||
malformed FAT interpretation remains unresolved. Malformed recognized FAT
|
||||
declarations retain their format-specific reasons.
|
||||
|
||||
`inspect_artifact` and `extract_artifact` retain universal Mach-O slices under
|
||||
their exact `lipo` names, including 32-bit ARM implementations such as `armv7`,
|
||||
`armv7s`, and `armv7k`. These names are reconciled with the FAT CPU subtype;
|
||||
the structural reader's `arm` family label alone does not identify a slice.
|
||||
Available CPU type, subtype/capability bits, byte range, and alignment remain
|
||||
checked against the source bytes. Inventory records source ranges and hashes, and
|
||||
extraction copies those ranges without running the target. This does not
|
||||
expand any analysis provider's supported architectures.
|
||||
Malformed successful `lipo` output reports a format failure with its parse
|
||||
diagnostic.
|
||||
Apple lipo's hidden ARM64 compatibility row is retained separately from the
|
||||
raw `nfat_arch` count and admitted only from its extra FAT table slot when its
|
||||
in-range Mach-O header matches the row. `list_architectures` preserves the
|
||||
per-row `hidden` marker from lipo output. Mach-O image and Electron fuse
|
||||
inspection include the row in architecture selection while retaining that
|
||||
marker in their reported slices.
|
||||
|
||||
`inspect_plist.format` describes the selected source, not plutil's conversion
|
||||
format. Sources explicitly identified as XML, binary or JSON retain those
|
||||
labels. For generic text, a bounded source-prefix probe recognizes leading XML
|
||||
@@ -328,17 +457,37 @@ markup, including UTF-8 and BOM-marked UTF-16 sources. Unidentified text and
|
||||
OpenStep plists, including a data-only value such as `<deadbeef>`, report
|
||||
`unknown` even when plutil decodes their values through an XML conversion.
|
||||
|
||||
Binary plist inspection reads the original bytes through an owned file descriptor,
|
||||
so conversion cannot erase control characters, fractional dates, negative zero,
|
||||
or the high word of a 128-bit integer. It accepts at most 64 MiB of input and
|
||||
reserves an estimated 64 MiB decode budget before parsing; this estimate is not
|
||||
a hard process-memory bound. A refused input reports non-retryable
|
||||
`resource_constraint` with `reported_limits.maximum_input_bytes`,
|
||||
`maximum_decoded_bytes` or, for nesting deeper than 128 levels,
|
||||
`maximum_nesting_depth`. These are fixed allowances: worker limits and host
|
||||
memory do not raise them, and the unchanged request fails the same way.
|
||||
Binary-source provenance records the `file` classification without claiming a
|
||||
`plutil` conversion.
|
||||
Plist inspection reads bounded original bytes through an owned file descriptor.
|
||||
Binary and UTF-8/BOM-marked UTF-16 XML sources decode directly, retaining scalar
|
||||
facts without a mandatory `plutil` conversion. UTF-8 JSON containers also decode
|
||||
directly when their number facts need no further classification; explicitly
|
||||
identified JSON sources can have scalar roots. Unidentified primitive text keeps
|
||||
its existing conversion route, so OpenStep `123` remains a string. OpenStep,
|
||||
unsupported XML encodings such as UTF-32 and declared legacy encodings, and JSON
|
||||
numbers requiring XML type facts retain the `plutil` route. Invalid UTF-8 and
|
||||
encoding-declaration mismatches remain explicit failures. Direct-source
|
||||
provenance records `file` without claiming conversion; conversion limitations
|
||||
and invocations appear only when conversion actually ran.
|
||||
XML sources must also pass native format validation, with native refusals retaining
|
||||
their diagnostics and successful validation recorded in provenance. Original
|
||||
unknown scalar text, reasons and limitations survive successful validation;
|
||||
output-budget omissions remain omissions.
|
||||
|
||||
Source input is limited to 64 MiB. Binary and direct XML decoding reserve an
|
||||
estimated 64 MiB decode budget before parsing; this is not a hard process-memory
|
||||
bound. JSON decode memory has not been measured and has no equivalent decode
|
||||
estimate. A refused input reports non-retryable `resource_constraint` with
|
||||
`reported_limits.maximum_input_bytes` or `maximum_decoded_bytes` as applicable.
|
||||
Ordinary direct-source decoding has a ceiling of 998 source nesting levels,
|
||||
derived from the existing 1000-level JSON result capacity by reserving one level
|
||||
for typed scalar fields and one for the result's `value` field. Deeper input reports
|
||||
`resource_constraint` with `reported_limits.maximum_nesting_depth: 998` and names
|
||||
the fixed nesting allowance. XML admission also requires native validation, which
|
||||
can refuse input below this decoding ceiling. Compatibility conversions can
|
||||
likewise encounter an earlier native utility refusal; both retain native
|
||||
diagnostics. Archive and Interface Builder decoding retain their separate
|
||||
128-level allowance. Worker limits and host memory do not raise these fixed
|
||||
allowances, and the unchanged request fails the same way.
|
||||
|
||||
A plist explicitly selected through the path input or as the active plist target
|
||||
reports an unreadable or malformed selection as `invalid_request`. A missing or
|
||||
@@ -346,19 +495,41 @@ unreadable default plist of another target reports `capability_unavailable`.
|
||||
Permission failures remain `access_denied`; output and cleanup failures retain
|
||||
their own error categories.
|
||||
|
||||
Selected plist paths preserve filesystem resolution, including `..` after a
|
||||
directory symlink and trailing directory separators. Resource-only bundle
|
||||
sessions resolve relative selections from the bundle root with the same path
|
||||
semantics as executable sessions; the reported source path retains those
|
||||
selected segments.
|
||||
|
||||
A conversion refusal includes plutil's diagnostic from either output stream.
|
||||
`details.captured_output` retains bounded stdout/stderr excerpts, observed byte
|
||||
totals and exit status. `truncated: true` means those excerpts are incomplete.
|
||||
|
||||
A dSYM companion that `dyld_info` refuses for an unbacked text section reports
|
||||
`unsupported_target`, retaining the exact diagnostic and directing inspection
|
||||
to the linked Mach-O. This classification requires both the recognized native
|
||||
refusal and an independently parsed dSYM Mach header; other tool failures keep
|
||||
their failure outcome.
|
||||
|
||||
Observed `dyld_info` diagnostics include `unknown mh.filetype 4` and
|
||||
`unknown filetype 4` for `MH_CORE`, and the corresponding `3` spellings for
|
||||
`MH_FVMLIB`. These refusals report `unsupported_target` when the independently
|
||||
parsed Mach header confirms the reported file type. The error retains the exact
|
||||
native diagnostic and recommends a linked Mach-O executable or relocatable
|
||||
object. This classifies the selected target's refusal; it does not establish
|
||||
support for core-dump analysis, historical FVMLIB layouts, or unobserved native
|
||||
diagnostic spellings. Unknown or contradictory header values and signalled
|
||||
commands remain failures.
|
||||
|
||||
`inspect_macho.selected_architecture` identifies the exact selected lipo slice
|
||||
and its `container_file_offset` in the input file. A thin Mach-O has a known
|
||||
offset of zero; an unknown universal-slice offset stays null. The Evidence
|
||||
subject format describes the input container: `mach-o-universal` for FAT files,
|
||||
including those with one slice. The normalized result describes the selected
|
||||
Mach-O image. Architecture
|
||||
`file_offset` values are container-relative. Entrypoint, segment and section
|
||||
`file_offset` values retain their source meaning and are relative to the
|
||||
selected Mach-O slice. Raw load-command fields retain their original values.
|
||||
including those with one slice. The normalized result also reports the container
|
||||
in `format`; `selected_architecture` identifies the selected Mach-O image.
|
||||
Architecture `file_offset` values are container-relative. Entrypoint, segment
|
||||
and section `file_offset` values retain their source meaning and are relative
|
||||
to the selected Mach-O slice. Raw load-command fields retain their original values.
|
||||
A field otool prints more than once, such as each `LC_BUILD_VERSION` tool, is
|
||||
keyed `name #n` in print order. Each `LC_LINKER_OPTION` string is keyed
|
||||
`string #n` and checked against the declared `count`. Evidence locations map the LC_MAIN entrypoint and nonempty file-backed segment
|
||||
@@ -395,10 +566,36 @@ complete observation.
|
||||
its outcome. Paths outside the analyzed root, including shared-cache system
|
||||
libraries, stay undetermined. See
|
||||
[Apple application analysis](apple-application-analysis.md#dylib-load-resolution).
|
||||
ARM64 slice labels distinguish `arm64v8`, `arm64e`, and `arm64e.x1` while
|
||||
retaining raw CPU subtype and capability bits. The `arm64` and `arm64e`
|
||||
request filters select their respective families; a shared family label
|
||||
does not establish dependency compatibility across different subtypes.
|
||||
Executable-root discovery preserves traversal uncertainty, including
|
||||
undecodable filenames and skipped symlinks, as partial coverage. Known
|
||||
executable roots remain available; unexamined names and symlink targets
|
||||
cannot establish an empty root set or the absence of other roots.
|
||||
- `inspect_asset_catalog` / `rea inspect-asset-catalog <app>` reads compiled
|
||||
`Assets.car` metadata through macOS `assetutil --info`. Catalog digests, raw
|
||||
rendition fields, pagination and exact UI resource-name matches are returned.
|
||||
Image extraction and undocumented rendition interpretation are unsupported.
|
||||
`inventory_coverage` describes traversal of source entries and remains
|
||||
`unknown` when the reader cannot represent a filename. Represented symlinks
|
||||
are inventoried but their targets remain uninspected, with a limitation.
|
||||
`catalog_coverage` and `resource_key_coverage` separately describe examined
|
||||
catalog metadata and the UI join; an unmatched key cannot establish absence
|
||||
when either source is incomplete. Resource wrappers retain their serialized
|
||||
class and authored runtime class, including AppKit `NSCustomResource` names.
|
||||
Invalid producer JSON and subprocess failures retain captured output with
|
||||
their typed failure reason and selected catalog path. A complete normal
|
||||
assetutil exit declaring that exact catalog path `is not a valid AssetCatalog`,
|
||||
or its observed `Undefined error: 0` / `invalid file` rejection, reports an
|
||||
invalid-artifact failure. Other tool failures retain their own
|
||||
classification; unrecognized diagnostics do not establish malformed bytes.
|
||||
Catalog-reader cleanup retains the primary
|
||||
integrity, utility or cancellation failure and its cleanup resources; the
|
||||
provider retains catalog, Interface Builder and keyed-archive readers until
|
||||
cleanup is confirmed. A catalog failure also retains cleanup failures from
|
||||
its parallel Interface Builder join. Image extraction and undocumented rendition
|
||||
interpretation are unsupported.
|
||||
- `inspect_keyed_archive` / `rea inspect-keyed-archive <plist>` reads XML or
|
||||
binary Foundation keyed archives without instantiating classes. For an active
|
||||
app, provide its relative archive path as the second CLI argument or MCP
|
||||
@@ -406,7 +603,12 @@ complete observation.
|
||||
shared/cyclic references, class descriptors, raw serialized fields and
|
||||
malformed/unresolved/nil references. Missing fields stay absent; unknown
|
||||
classes are preserved. Select `root`, `offset` and `limit` explicitly.
|
||||
Object `kind` describes the decoded archive value: data, dates and numbers
|
||||
Binary references retain their serialized type-8 UID identity; ordinary
|
||||
dictionaries with `UID` or `CF$UID` keys remain authored data. XML uses
|
||||
Foundation's single numeric `CF$UID` dictionary convention for references;
|
||||
a numeric `UID` dictionary remains authored data. Reference identity follows
|
||||
the parsed format, rather than the displayed key alone. Object `kind`
|
||||
describes the decoded archive value: data, dates and numbers
|
||||
remain `scalar` when their JSON value uses a `$plist_type` wrapper. An actual
|
||||
dictionary with the same fields remains `dictionary`.
|
||||
XML must be well-formed. Data literals use
|
||||
@@ -415,6 +617,19 @@ complete observation.
|
||||
emits no bytes. Character references retain their source spelling for data
|
||||
decoding. Non-ASCII content, child markup and self-closing `<data/>` are format
|
||||
failures; explicit `<data></data>` represents empty data.
|
||||
Each XML dictionary key must have a following value element; a final key
|
||||
without one is malformed. An explicit empty string remains a value.
|
||||
Binary UID payloads may use nonminimal byte widths. REA normalizes them only
|
||||
in a private decoder copy; archive digests still identify the original bytes.
|
||||
Positive binary-plist offset and object-reference table widths are read from
|
||||
the source; nonstandard widths are normalized only in that private copy for
|
||||
the pinned decoder. [Foundation's table checks](https://github.com/swiftlang/swift-corelibs-foundation/blob/swift-6.2.3-RELEASE/Sources/CoreFoundation/CFBinaryPList.c#L759-L789)
|
||||
accept positive widths subject to extent/reference bounds, and its
|
||||
[sized-integer reader](https://github.com/swiftlang/swift-corelibs-foundation/blob/swift-6.2.3-RELEASE/Sources/CoreFoundation/CFBinaryPList.c#L707-L730)
|
||||
handles non-power sizes. Widths over eight bytes follow its unsigned 64-bit
|
||||
accumulator semantics.
|
||||
UID values above JavaScript's exact integer range return an unsupported-target
|
||||
outcome instead of rounded reference identities.
|
||||
UID 0 cannot distinguish conditional nil from ordinary nil. Limits are
|
||||
64 MiB of input, 200,000 objects/references and 128 nesting levels.
|
||||
XML dictionary entries decoded as `__proto__` are omitted with an explicit
|
||||
@@ -422,6 +637,12 @@ complete observation.
|
||||
comments do not change that handling. XML key names combine their text and
|
||||
CDATA segments without trimming or expanding literal CDATA entities. Other
|
||||
values and the original archive digest are preserved.
|
||||
Symbol fallback accepts complete Objective-C class/metaclass symbol leaves,
|
||||
including qualified names. Pointer/string labels that merely contain a class
|
||||
marker do not establish classes. Provider namespace method labels are not
|
||||
interpreted as Objective-C declarations; class definition versus import origin
|
||||
remains unsupported coverage rather than an inferred classification.
|
||||
|
||||
- `decode_interface_builder` / `rea decode-interface-builder <app>` reads
|
||||
compiled storyboard and nib resources into objects, outlets, actions,
|
||||
connections and controller/class names. A full app graph is often several
|
||||
@@ -445,7 +666,11 @@ complete observation.
|
||||
still identify the original serialized bytes. Bundle resolution reports
|
||||
malformed bytes and unsupported encodings against the original plist path
|
||||
before looking up the executable. Interface Builder decoding
|
||||
limits aggregate input archives to 32 MiB.
|
||||
accepts recognized NSKeyedArchiver or ibtool document structures and native
|
||||
NIBArchive records; an ordinary plist renamed `.nib` remains undecoded with
|
||||
partial `archive_decode` coverage. An explicitly empty ibtool object document
|
||||
remains a recognized empty document. The decoder limits aggregate input
|
||||
archives to 32 MiB.
|
||||
Before decoding, it also bounds the serialized structure: expanded binary
|
||||
plist references, NIB table records, XML elements and text, plus their
|
||||
projected representations must fit the 256 MiB aggregate decode budget.
|
||||
@@ -458,15 +683,45 @@ complete observation.
|
||||
Archives beyond either aggregate budget are omitted with their paths and an
|
||||
explicitly partial `archive_decode` facet; previously decoded documents and
|
||||
their identities remain available.
|
||||
The same facet is partial when source traversal cannot represent a filename
|
||||
or an archive-shaped symlink was skipped. Unknown filename coverage has no
|
||||
invented document count; known skipped nib and storyboard links are counted
|
||||
as omitted. A readable empty bundle can report complete zero-document
|
||||
coverage. Source I/O and identity failures retain their acquisition error
|
||||
instead of being labeled invalid archive content. Rejected plist decodes
|
||||
release their representation reservation for later documents.
|
||||
Repeated NIBArchive fields retain all serialized values in order. Array
|
||||
outlet connections resolve each serialized member and retain the collection
|
||||
object ID and member position on the outlet observation. AppKit object-parent
|
||||
registries use paired keys and parents; objects paired with `NSRoot` are
|
||||
top-level, while nested objects keep their recorded parent. Conflicting or
|
||||
unreadable registry pairs remain incomplete hierarchy evidence.
|
||||
`NSString` objects with the [recognized `NS.bytes` representation](https://github.com/swiftlang/swift-corelibs-foundation/blob/main/Sources/Foundation/NSString.swift#L201-L215) are decoded
|
||||
as UTF-8 without substituting malformed bytes. A failed conversion retains
|
||||
the source string object ID and `nibValues.NS.bytes` base64 data; dependent
|
||||
authored classes, titles and selectors remain unknown, and object, class,
|
||||
connection and archive-decode coverage is partial. Other decoded objects and
|
||||
connections remain available. Genuine U+FFFD and leading U+FEFF characters in
|
||||
valid UTF-8 retain their identity; opaque data fields are not interpreted as
|
||||
strings. This does not establish support for other NSString representations.
|
||||
Embedded `UINib` archive data remains retained and undecoded; affected
|
||||
object, connection, class and hierarchy coverage is partial. Recognized
|
||||
`UIStoryboardShowSegueTemplate` source records similarly qualify connection
|
||||
coverage until their navigation semantics are decoded.
|
||||
|
||||
- `inspect_native_dispatch_metadata` /
|
||||
`rea inspect-native-dispatch-metadata <app-or-binary>` prefers a validated macOS Mach-O byte reader. It decodes
|
||||
64-bit little-endian Objective-C class/metaclass records, superclass pointers,
|
||||
absolute/relative method entries, ivar offsets/sizes/alignment and protocol
|
||||
declarations. It also decodes simple Swift conformances, static synchronous
|
||||
witness slots, signed-relative pointers and non-generic, non-resilient class
|
||||
vtable descriptors. Records include exact virtual addresses/file offsets and
|
||||
declarations. It also decodes Swift nominal type declarations, simple Swift
|
||||
conformances, static synchronous witness slots, signed-relative pointers and
|
||||
non-generic, non-resilient class vtable descriptors. Records include exact virtual addresses/file offsets and
|
||||
artifact evidence. Vtable indexes are metadata word offsets; witness indexes
|
||||
start after the conformance header. Names unavailable in metadata remain null.
|
||||
The selected Mach-O header must match the requested CPU family for both thin
|
||||
files and universal slices before metadata decoding. CPU subtypes and
|
||||
capability bits remain source facts; an `arm64` request may select an
|
||||
`arm64e` image under the existing slice preference policy.
|
||||
The byte reader holds the whole selected file in memory, so it admits files up
|
||||
to 64 MiB / 67108864 bytes by default. Select a larger budget with
|
||||
`max_input_bytes` (CLI `--max-input-bytes`), at most 1 GiB / 1073741824
|
||||
@@ -475,6 +730,29 @@ complete observation.
|
||||
(`default` or `caller`) and the bytes retained. A refusal names the observed
|
||||
file size, the effective budget and its source, and either the budget to
|
||||
select or, above 1 GiB, a deep analysis provider.
|
||||
`swift_decls` lists each `__swift5_types` nominal type descriptor, which
|
||||
survives stripping: `kind` (`class`, `struct`, `enum`, or `actor` from the
|
||||
class actor flag), the descriptor's own `name`, `module`, `qualified_name`
|
||||
(`Outer.Inner`), `generic`, the metadata `accessor_address`, the descriptor
|
||||
`location`, and evidence citing its `__swift5_types` record.
|
||||
Private and local types set `anonymous_context` because their anonymous
|
||||
context is omitted from `qualified_name`. When a parent context cannot be
|
||||
resolved, such as an extension of a type named only by a mangled name, a
|
||||
missing module, or a cyclic chain, `qualified_name` is null and the entry is
|
||||
partial with a reason. Access level, superclass, conformances, and fields are
|
||||
not reported by this facet. The `swift_declarations` coverage counters count
|
||||
examined type records and fully decoded declarations. Class vtable recovery
|
||||
reads the same descriptors, so an unreadable type record also keeps
|
||||
`swift_class_vtable_descriptors` partial.
|
||||
Nominal declarations, conformance type and protocol references, and class
|
||||
vtables share one snapshot-scoped name/context reader. Conformance `type_name`,
|
||||
`protocol_name`, and dispatch `owner` retain enclosing named types;
|
||||
conformances carry the known module.
|
||||
Unresolved parents preserve the leaf name and a partial identity reason,
|
||||
while independently readable witness implementations remain available.
|
||||
Protocol instance properties use the same property-list reader as classes.
|
||||
Unreadable instance properties and unimplemented class-property lists keep
|
||||
the protocol's methods and record with partial protocol coverage.
|
||||
The `swift_class_vtable_descriptors` coverage counters count examined vtable
|
||||
slots and successfully decoded slots, rather than type-section entries.
|
||||
The `objc_class_method_ivar_metadata` counters count admitted class and
|
||||
@@ -484,6 +762,21 @@ complete observation.
|
||||
arm64e formats;
|
||||
- legacy `LC_DYLD_INFO` rebase and bind opcodes.
|
||||
|
||||
[dyld symbol names are byte strings](https://github.com/apple-oss-distributions/dyld/blob/dyld-1330/mach_o/BindOpcodes.cpp#L203-L208),
|
||||
compared with [`strcmp`](https://github.com/apple-oss-distributions/dyld/blob/dyld-1330/mach_o/BindOpcodes.cpp#L42-L56).
|
||||
Dependent library install names likewise originate as
|
||||
[load-command byte strings](https://github.com/apple-oss-distributions/dyld/blob/dyld-1330/mach_o/Header.cpp#L1278-L1298)
|
||||
and are [compared byte for byte](https://github.com/apple-oss-distributions/dyld/blob/dyld-1330/dyld/DyldAPIs.cpp#L607-L618).
|
||||
When a symbol or applicable library name has no lossless UTF-8 text
|
||||
projection, the affected binding remains unresolved and pointer-fixup
|
||||
coverage is partial; later readable bindings and other metadata survive.
|
||||
The internal fixup reader retains the exact name bytes and parsed binding
|
||||
fields, while public Objective-C and Swift results retain the unresolved
|
||||
reason instead of publishing a guessed symbol, library or class name. A literal
|
||||
U+FFFD or leading BOM in valid UTF-8 remains part of the name. Legacy
|
||||
weak-bind lookup has no library-name scope, so an opaque dependency name
|
||||
does not prevent decoding an otherwise readable weak binding.
|
||||
|
||||
A Swift conformance whose type or protocol reference is an external bind
|
||||
keeps its name as `null` and carries the raw dyld identity in
|
||||
`type_binding` or `protocol_binding`; that record and its facet coverage are
|
||||
@@ -505,7 +798,8 @@ complete observation.
|
||||
So an external superclass resolves from its `_OBJC_CLASS_$_` bind (for
|
||||
example `NSObject`). The `pointer_fixups` coverage facet names the mechanism.
|
||||
Class properties preserve each encoded attribute as a raw `{name, value}`
|
||||
pair, for example `{ name: "N", value: "" }`, and report atomicity on the
|
||||
pair, including commas inside C++ template arguments in the `T` type, for
|
||||
example `{ name: "N", value: "" }`, and report atomicity on the
|
||||
property: `N` means `nonatomic`; a complete typed attribute string without
|
||||
`N` means the Objective-C default `atomic`;
|
||||
missing or incomplete attribute metadata leaves `atomicity` and any
|
||||
@@ -563,6 +857,19 @@ capture or automatic permission prompts. Executable bytes and process launch
|
||||
time guard against a different target or PID reuse. AX selection requires one
|
||||
unique geometry match; ambiguity fails closed.
|
||||
|
||||
Accessibility node values preserve strings, finite numbers and booleans as their
|
||||
original JSON scalar types. Values that JSON cannot represent faithfully,
|
||||
including unsafe integers, negative zero and non-finite numbers, remain null
|
||||
with a path-specific gap and incomplete capture coverage. Null alone does not
|
||||
establish that an accessibility value was successfully read or absent.
|
||||
|
||||
An initial PID or window mismatch is `invalid_request` on `pid` or
|
||||
`window_id` and leaves provider availability intact. An unavailable executable
|
||||
URL or unresolved executable path leaves identity unknown and reports
|
||||
`capability_unavailable`. Identity or window loss
|
||||
after the initial capture remains an observation failure with collected facts.
|
||||
Permission and other helper failures retain their specific remediation text.
|
||||
|
||||
`capture_native_ui_scenario` takes AX child-index paths for press, increment/
|
||||
decrement scrolling and text-value entry, or bounded waits. No global event
|
||||
injection is used. Unsupported AX actions fail explicitly. The result preserves
|
||||
@@ -594,10 +901,26 @@ caller-selected entries, records them and continues:
|
||||
- Objective-C methods by selector, optionally limited to one class and to
|
||||
instance or class methods.
|
||||
|
||||
LLDB discovery uses a separate 60000 ms `xcrun` deadline with no runtime
|
||||
override. Discovery timeouts report `provider_timeout` and retain timeout
|
||||
recovery, captured diagnostics and cleanup state. Output-budget and I/O failures
|
||||
retain their own error categories; only actual discovery unavailability reports
|
||||
missing LLDB. `duration_ms` controls the later target observation window.
|
||||
|
||||
```sh
|
||||
rea observe-native-calls /Applications/Example.app '{"breakpoints":[{"kind":"objc-method","class_name":"NSURLSession","selector":"dataTaskWithRequest:completionHandler:"},{"kind":"function","name":"open","module":"libsystem_kernel.dylib"}],"duration_ms":5000,"backtrace_frames":4}' --json
|
||||
```
|
||||
|
||||
Function names select LLDB runtime symbols. On Darwin, a C function reported as
|
||||
`_main` by `inspect_macho` or `nm` can resolve as `main` in LLDB. REA preserves the
|
||||
exact selected name; it does not remove underscores or translate static names.
|
||||
Use `{"kind":"function","name":"main","module":"Example"}` to limit that
|
||||
runtime symbol to the executable image named `Example`, then inspect the reported
|
||||
locations and image paths. Omitting `module` deliberately matches every loaded
|
||||
image, including system libraries. A match outside the executable is valid for
|
||||
that request and does not by itself make coverage partial. This C naming example
|
||||
does not establish a translation rule for Swift, C++ or other mangled symbols.
|
||||
|
||||
Each event is an observation: thread, image, symbol, load and file address,
|
||||
the raw integer argument registers, optional caller frames and, for an
|
||||
Objective-C method, the selector read from `_cmd` and the receiver's dynamic
|
||||
@@ -614,6 +937,9 @@ verified, the result or error preserves that uncertainty. The result reports:
|
||||
Launch errors keep the fact that LLDB created a process separate from whether
|
||||
it remains alive. An exited `SBProcess` is reported as exited only after LLDB
|
||||
confirms that state; unresolved ownership remains retained for cleanup retry.
|
||||
An invalid process returned by `Launch`, including a rejected working directory,
|
||||
reports explicit no-process creation. With no target PID or other retained
|
||||
resource, REA releases that runtime and admits the next observation.
|
||||
Cancellation observed while output and Evidence are finalized returns partial
|
||||
cancellation Evidence, even if the target has already exited.
|
||||
|
||||
@@ -621,7 +947,7 @@ cancellation Evidence, even if the target has already exited.
|
||||
while excess bytes are drained and counted without growing capture files).
|
||||
Each stream reports `complete`; when draining could not finish, its byte
|
||||
count is an observed lower bound and truncation is explicit;
|
||||
- caller-selected arguments, working directory, and environment overrides;
|
||||
- caller-selected arguments, working directory, and environment overrides, including literal JSON environment names such as `__proto__`;
|
||||
- `selected_file_sha256` and LLDB module identity observations. These checks do
|
||||
not pin the executable against concurrent mutation; `loaded_image_sha256`
|
||||
remains unknown;
|
||||
@@ -712,8 +1038,30 @@ macOS ARM64 is the real host verified during this implementation. Admission of
|
||||
macOS Intel does not claim an Intel verification run. Unsupported metadata and
|
||||
unresolved runtime/value semantics remain visible in results.
|
||||
|
||||
Signature inspection requests the selected architecture explicitly for identity,
|
||||
hashes, designated requirements, entitlements and runtime flags. For universal
|
||||
inputs, other slices receive signing-state probes; their detailed metadata is
|
||||
not included in the selected result, and coverage states that boundary. The
|
||||
digest-verified temporary snapshot writes to the filesystem; discovery declares
|
||||
that effect even though the selected artifact is unchanged.
|
||||
|
||||
## Interface Builder hierarchy coverage
|
||||
|
||||
Bundle archive selection preserves directory traversal uncertainty, including
|
||||
filenames that cannot be decoded as UTF-8. These names are not guessed or
|
||||
counted as known archive omissions. Recognized symlinked `.nib` entries are
|
||||
not followed; they retain their paths and reader limitations as counted archive
|
||||
omissions. Either gap makes `archive_decode` partial and the graph truncated
|
||||
while preserving decoded documents. Its `examined` count remains actual decode
|
||||
attempts, and asset-catalog UI joins retain their existing incomplete-graph
|
||||
qualification.
|
||||
|
||||
UI dispatch joins use the authored receiver's class independently of its
|
||||
presentation kind (controller, view, control or another object). An unresolved
|
||||
action receiver remains an examined, unresolved dispatch relationship and makes
|
||||
`ui_to_objc_dispatch` partial. Graph object limits retain their own cause in
|
||||
connection coverage, rather than being attributed to a connection budget.
|
||||
|
||||
A document's `hierarchy_complete` flag follows its own hierarchy coverage.
|
||||
If graph limits omit a hierarchy source or destination, or prevent a hierarchy
|
||||
edge or traversal from being retained, that document reports partial hierarchy
|
||||
@@ -762,8 +1110,31 @@ attributes; exact and ambiguous projection counts cover emitted attribute values
|
||||
Integral reals stay numbers, and ambiguous or incompletely observed values retain
|
||||
explicit precision limitations. Before plist decoding, the existing aggregate
|
||||
decode budget reserves estimated room for number metadata and attribute
|
||||
projection; these estimates are not a hard process-memory bound. Native
|
||||
NIBArchive decoding is unchanged by this plist-specific representation.
|
||||
projection; these estimates are not a hard process-memory bound. Keyed-archive
|
||||
object identities always come from their original `$objects` table indices.
|
||||
Authored `objectID`, `object-id`, `archiveUID` and `id` fields remain ordinary
|
||||
object data and do not change inventory, reference, connector or hierarchy
|
||||
identity. This changes keyed-archive graph IDs that previously used authored
|
||||
`objectID` or `object-id` values: `interface_builder_object_id` and linked node
|
||||
IDs now use the decimal table UID, while the authored values remain in attributes.
|
||||
For example, an authored `objectID: "2"` on table entry 1 now identifies object
|
||||
`"1"`; its graph node ends in `object:["1"]`. Nonkeyed ibtool dictionary IDs
|
||||
and native NIBArchive IDs keep their existing source-specific meaning.
|
||||
Connector endpoints require encoded UID references to archived objects; strings
|
||||
and dictionaries that merely resemble resolved reference stubs do not establish
|
||||
identity. Inline hierarchy dictionaries have no table identity and count as
|
||||
omitted references, while independently referenced children remain available.
|
||||
The UID identity rule follows the
|
||||
[Foundation unarchiver's table lookup](https://github.com/swiftlang/swift-corelibs-foundation/blob/28249ced079d8e7ec03b821c039bf9868738c3fc/Sources/Foundation/NSKeyedUnarchiver.swift#L213-L223).
|
||||
UIKit keyed nibs select their explicitly named
|
||||
`UINibTopLevelObjectsKey` collection; generic `root` is used when that named key
|
||||
is absent. Unsupported or dangling named roots remain incomplete rather than
|
||||
falling back to another collection. The key spelling follows the independent
|
||||
[WinObjC UINib reader](https://github.com/microsoft/WinObjC/blob/94f6b5bfdc09d945e30607b56124b6f94a5b9d7b/Frameworks/UIKit/UINib.mm#L104-L109).
|
||||
|
||||
NIBArchive float32 and float64 values retain non-finite values and negative zero
|
||||
through projection as the same typed real wrappers used for plist values; per-document limitations
|
||||
count those values. NIBArchive nil remains JSON null.
|
||||
|
||||
Dates are reported as `{ "$plist_type": "date", "iso8601": "<timestamp>" }`.
|
||||
XML dates require a complete UTC spelling with fixed two-digit month, day and
|
||||
@@ -824,3 +1195,21 @@ skipped uninitialized blocks. A missing match does not establish absence from
|
||||
uninitialized memory or across block boundaries. See
|
||||
[Ghidra byte search](ghidra-provider.md#masked-byte-search) for mask and coverage
|
||||
semantics. This query never executes or modifies target bytes.
|
||||
|
||||
Exact-name xref lookup reports distinct candidate addresses as an input ambiguity;
|
||||
inventory order cannot select one silently. Duplicate rows at one address remain
|
||||
a singular match, and a missing name remains an unresolved observation.
|
||||
|
||||
Hopper procedure names must identify a procedure entry. A non-entry symbol returns
|
||||
an input diagnostic with its coordinate and containing procedure; an explicit
|
||||
numeric address still selects its containing procedure. Use
|
||||
`resolve_containing_procedure` to request containment directly. IDA and Hopper
|
||||
reject reserved `file-offset:` selectors before name lookup; source-file offset
|
||||
selection belongs to the Ghidra import mapping.
|
||||
|
||||
Native function names carry observed symbol metadata where available. Ghidra uses
|
||||
its function symbol's source and dynamic flags; Hopper and IDA leave origin
|
||||
unknown because this adapter has no verified origin API. Comparison and matching
|
||||
cannot use unknown labels as cross-artifact identity, even when their spelling
|
||||
looks meaningful. Original labels remain in Evidence. This qualification does not
|
||||
change address equality in a known common native coordinate context.
|
||||
+11
-2
@@ -10,10 +10,17 @@ Supply an existing TShark 4.x installation **on Linux**, with util-linux
|
||||
`prlimit` available at `/usr/bin/prlimit`. Override its absolute path with
|
||||
`REA_TSHARK_PRLIMIT_COMMAND` if required. Unsupported hosts, including macOS
|
||||
and Windows, are refused before inspection because this integration requires
|
||||
an enforced address-space limit:
|
||||
an enforced address-space limit.
|
||||
|
||||
TShark selection follows the same rule as [`inspect_pcap_capture`](pcap-analysis.md):
|
||||
an absolute `REA_TSHARK_COMMAND`, otherwise `tshark` from the first absolute
|
||||
`PATH` entry that holds an executable file. A relative `REA_TSHARK_COMMAND` is
|
||||
refused rather than looked up. Either way the selected executable is resolved,
|
||||
hashed and rechecked after each run, and Evidence records its
|
||||
`selection_source` (`environment` or `path`):
|
||||
|
||||
```sh
|
||||
export REA_TSHARK_COMMAND=/absolute/path/to/tshark
|
||||
export REA_TSHARK_COMMAND=/absolute/path/to/tshark # optional when tshark is on PATH
|
||||
rea inspect-packet-capture /artifacts/device.pcapng \
|
||||
--fields ip.addr --fields udp.port --fields data.data \
|
||||
--display_filter 'udp' --format json
|
||||
@@ -88,6 +95,8 @@ for field names, display filters, decode-as syntax and pass semantics. Invalid
|
||||
fields or filters return producer diagnostics. An empty result does not prove
|
||||
absence of traffic outside the selected recording, filter or read range.
|
||||
Dissector results are observations, not independently verified device behavior.
|
||||
TShark may resolve a selected alias to its canonical field name in packet output;
|
||||
the report preserves the exact requested selection and the producer's output key.
|
||||
|
||||
Evidence includes source path/digest/size, exact selections, typed packet fields,
|
||||
configured and resolved executable paths/digests/full version banner, arguments and stderr. Warnings remain
|
||||
|
||||
+31
-4
@@ -17,10 +17,23 @@ rea inspect-pcap-capture /captures/session.pcapng --json
|
||||
```
|
||||
|
||||
Without explicit tool commands, this family resolves them through PATH and
|
||||
records that selection source. Client inspection reads no capture file. Capture
|
||||
records that selection source. On Linux and macOS it runs the tool found in the
|
||||
first absolute PATH entry holding an executable file, the same selection the
|
||||
[packet capture tools](packet-captures.md) make; relative PATH entries are
|
||||
skipped. Windows keeps PATH/PATHEXT resolution in the process launcher. Client
|
||||
inspection reads no capture file; on hosts where PATH/PATHEXT does not expose
|
||||
an absolute candidate, its executable digest and resolved path are reported as
|
||||
unresolved. Otherwise the canonical executable path and SHA-256 are reported.
|
||||
The pcap provider and packet tools share the selected-tool identity check, and
|
||||
changes to a resolved executable during a run fail with artifact-changed
|
||||
Evidence. Capture
|
||||
inspection requires Linux with util-linux prlimit or Windows with owned Job
|
||||
Objects. macOS capture inspection returns a capability gap because the new
|
||||
native memory boundary is unverified; its version probes remain available.
|
||||
`binary_session` advertises those operations separately: unsupported capture
|
||||
hosts do not disable the client probe or remove either tool from the catalog.
|
||||
A selected tool directory or unreadable tool is a configuration capability
|
||||
refusal naming its selected path, before any capture bytes are opened.
|
||||
Tools and installed libraries/plugins are caller-trusted. An empty private
|
||||
personal Wireshark configuration is used; this is not an OS sandbox.
|
||||
|
||||
@@ -63,6 +76,10 @@ associations are derivations. Capture Evidence has historical-reference authorit
|
||||
nanoseconds). Empty captures, malformed timestamps and instants outside years
|
||||
0000–9999 remain `null`; host-local timestamps without a zone are not inferred.
|
||||
|
||||
`snapshot_length` is the file-header packet size limit reported by `capinfos`.
|
||||
Its separate inferred truncation lengths do not replace that limit; an unset
|
||||
file-header limit remains `null`.
|
||||
|
||||
Addresses count once per packet, including loopback and nested IP occurrences.
|
||||
DNS addresses are associated only with one question in the same response packet;
|
||||
additional records may be included, so this does not prove final resolution.
|
||||
@@ -77,6 +94,13 @@ not process identity or proof of the destination's authenticity.
|
||||
All examined inventories are retained within the resource budget; no fixed
|
||||
200/100/50-item truncation is applied. HTTP transport credentials are excluded
|
||||
without rewriting other URI bytes; ambiguous credential spans cause omission.
|
||||
The URI inventory includes absolute-form HTTP request targets when TShark cannot
|
||||
construct `http.request.full_uri` because no Host header was reported; relative
|
||||
request targets are not promoted to absolute URIs. The inventory also consumes
|
||||
`http2.request.full_uri`; a decoded HTTP/2 `:path` without a corresponding full
|
||||
URI leaves coverage partial. The HTTP URI profile covers HTTP/1 and HTTP/2;
|
||||
HTTP/3 URI semantics are unexamined. All admitted URI fields share the same
|
||||
credential exclusion and source-value retention rules.
|
||||
Counts preserve distinct observed source URIs before omission or coalescing.
|
||||
`coverage: "partial"` records unresolved DNS attribution or URI item loss;
|
||||
component credential exclusion is also counted explicitly. Diagnostics retain
|
||||
@@ -100,9 +124,12 @@ wall deadlines are 300 s for dissection, 60 s for metadata and 20 s for probes.
|
||||
Native ownership preparation has its own bounded lifecycle.
|
||||
|
||||
Malformed/oversized output returns no partial success. Unvalidated partial JSON
|
||||
is not returned as facts. Cancellation reaches probes, acquisition, commands and
|
||||
projection. Cleanup failure returns a typed error, retains completed Evidence
|
||||
when available, and retains owned resources for retry on close.
|
||||
is not returned as facts. Invalid UTF-8 in either `capinfos` or TShark stdout or
|
||||
stderr is an unreadable-output error; replacement-decoded text is not parsed as
|
||||
producer evidence. A literal U+FFFD character in valid UTF-8 remains text.
|
||||
Cancellation reaches probes, acquisition, commands and projection. Cleanup
|
||||
failure returns a typed error, retains completed Evidence when available, and
|
||||
retains owned resources for retry on close.
|
||||
|
||||
## Verification scope
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# PE Authenticode certificate tables
|
||||
|
||||
`inspect_pe_certificates` / `inspect-pe-certificates` answers whether one
|
||||
selected local PE32/PE32+ image declares an `IMAGE_DIRECTORY_ENTRY_SECURITY`
|
||||
table, which `WIN_CERTIFICATE` entries that table stores, and which PKCS#7
|
||||
signer and X.509 fields those entries carry. It reads the file without
|
||||
executing it and does not require a Windows SDK, catalog file, or disassembler.
|
||||
|
||||
```bash
|
||||
rea inspect-pe-certificates /absolute/path/to/application.exe --json
|
||||
```
|
||||
|
||||
The CLI resolves relative paths against the caller's working directory. MCP
|
||||
and the advertised input schema require an absolute filesystem path and
|
||||
preserve that exact selection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_pe_certificates",
|
||||
"arguments": { "path": "/artifacts/application.exe" }
|
||||
}
|
||||
```
|
||||
|
||||
## Observations
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
`table` is the file-offset security directory, or `null` when the directory is
|
||||
absent or zero. Each `entries[]` item reports `WIN_CERTIFICATE` length,
|
||||
revision, type, and the payload SHA-256.
|
||||
|
||||
Type-2 entries may count their zero alignment padding in `dwLength`. The PE
|
||||
entry owner accepts only the exact gap to the next eight-byte boundary after
|
||||
the parsed ContentInfo; extra data or nonzero counted padding fails. Entry
|
||||
ranges retain all declared bytes, including counted padding. CMS payload
|
||||
ranges and hashes exclude counted padding.
|
||||
|
||||
For `WIN_CERTIFICATE` type 2 (PKCS#7 signedData) `pkcs7` contains digest
|
||||
algorithm OIDs, embedded X.509 serial/issuer/subject/validity fields, and
|
||||
signer identifier facts. A signer identified by subject key identifier reports
|
||||
that value as `subject_key_identifier_hex`. Other certificate types keep
|
||||
`pkcs7` null.
|
||||
|
||||
A missing table is observed absence of that directory. It is not proof the
|
||||
image is unsigned in every catalog or overlay sense.
|
||||
|
||||
## Limits and failures
|
||||
|
||||
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
|
||||
eight `WIN_CERTIFICATE` entries are examined by default. Malformed PE headers,
|
||||
truncated certificate entries, and PKCS#7 that is not structurally signedData
|
||||
fail without partial success. A security directory that ends in fewer than
|
||||
eight header bytes, including zeros, is a truncated `WIN_CERTIFICATE` and
|
||||
fails the same way. SignedData must contain at least one `SignerInfo`, and
|
||||
each `SignerInfo` must carry a non-empty signature value. Each embedded X.509
|
||||
certificate must include `subjectPublicKeyInfo` and `signatureValue`. Decoded
|
||||
certificate name text, serials, validity spellings, and subject key identifiers
|
||||
share a 64 KiB budget per PKCS#7 payload. Unsupported certificate name
|
||||
encodings, including GeneralString, fail explicitly. TeletexString (T.61)
|
||||
values are rendered with the ISO-8859-1 compatibility mapping used by OpenSSL;
|
||||
this is not a full T.61 decoder, so the result includes a limitation whenever
|
||||
that mapping is used. RFC 5280 retains TeletexString for backward compatibility
|
||||
and describes its repertoire as ASCII-like Latin with additional characters.
|
||||
|
||||
The structural CMS reader accepts definite lengths and BER indefinite lengths
|
||||
for constructed values, including the outer `ContentInfo` sequence. Primitive
|
||||
values must have definite lengths. Embedded X.509 certificates and signed
|
||||
attributes must also use definite length encoding, as required by their DER
|
||||
profiles. This is a bounded structural subset: it does not implement every BER
|
||||
string form or validate DER canonical encoding, and it does not verify
|
||||
signatures.
|
||||
|
||||
This workflow does not verify signatures, certificate chains, timestamps, or
|
||||
Authenticode digest coverage of image bytes.
|
||||
@@ -0,0 +1,80 @@
|
||||
# PE import and delay-load tables
|
||||
|
||||
`inspect_pe_imports` / `inspect-pe-imports` answers whether one selected local
|
||||
PE32/PE32+ image declares an `IMAGE_DIRECTORY_ENTRY_IMPORT` table, which DLLs
|
||||
and functions that table names, which delay-load descriptors are present, and
|
||||
the pefile-compatible import hash of the standard import table. It reads the
|
||||
file without executing it and does not require a Windows SDK or disassembler.
|
||||
|
||||
```bash
|
||||
rea inspect-pe-imports /absolute/path/to/application.exe --json
|
||||
```
|
||||
|
||||
The CLI resolves relative paths against the caller's working directory. MCP
|
||||
and the advertised input schema require an absolute filesystem path and
|
||||
preserve that exact selection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_pe_imports",
|
||||
"arguments": { "path": "/artifacts/application.exe" }
|
||||
}
|
||||
```
|
||||
|
||||
## Observations
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
`import_directory` and `delay_import_directory` report the data directory RVA
|
||||
and size, or `null` when a directory is absent or zero. Their `locations` are
|
||||
the file spans actually read for the descriptor array and its terminator. When
|
||||
consecutive descriptors map to discontiguous file bytes (for example across a
|
||||
section boundary), each span is reported separately, and each becomes its own
|
||||
Evidence file-offset range.
|
||||
If the first descriptor cannot be examined, the directory declaration remains
|
||||
present with no consumed `locations`, and coverage is partial. Descriptors,
|
||||
thunks and NUL-terminated names stay within PE's 32-bit RVA space, including
|
||||
PE32+ images; earlier valid rows survive an overflowing descriptor walk.
|
||||
|
||||
`machine` retains the original unsigned COFF value. `architecture` decodes
|
||||
known machines, including ReadyToRun OS overrides when a CLI directory is
|
||||
declared; that declaration does not establish valid CLR metadata or runtime
|
||||
compatibility. Unrecognized machines retain `pe-machine-<decimal>` while their
|
||||
import tables remain inspectable. A recognized machine whose width contradicts
|
||||
the optional header is malformed.
|
||||
|
||||
Each `imports[]` item reports the DLL name, descriptor RVAs, and named or
|
||||
ordinal functions with ILT/IAT RVAs. `delay_imports[]` reports RVA-based
|
||||
delay-load descriptors separately. `imphash` is pefile's `get_imphash()` over
|
||||
the standard import table only; delay-load rows do not participate. Ordinal
|
||||
imports use pefile's ordinal name tables and otherwise its `ord<decimal>`
|
||||
fallback.
|
||||
|
||||
`imphash.md5` is `null` with a `reason` whenever the value would not be
|
||||
pefile's hash of this image: the standard import table is partial, or pefile
|
||||
would read a different row sequence (names it treats as invalid or truncates,
|
||||
ordinal 0, descriptors without functions, or tables its bounds and import
|
||||
symbol cap truncate). Delay-load issues do not withhold the hash.
|
||||
|
||||
A missing import directory is observed absence of that table. It is not proof
|
||||
the image never imported APIs through another table or overlay.
|
||||
|
||||
## Limits and failures
|
||||
|
||||
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
|
||||
256 import and delay-load descriptors are examined by default (4096 maximum),
|
||||
and at most 8192 functions across every table (262144 maximum). Malformed PE
|
||||
headers and exhausted DLL or function budgets fail without partial success.
|
||||
Decoded DLL and function names are limited to a fixed 16 MiB of retained text
|
||||
with no runtime override; `max_file_bytes`, `max_dlls` and `max_functions` do
|
||||
not raise it. Resource failures report the exhausted allowance as `limit`
|
||||
(`dlls`, `functions` or `retained_text`) with matching recovery.
|
||||
|
||||
Unreadable DLL names, hint/name entries, or thunks are reported as partial
|
||||
coverage with `issues`. A descriptor whose required table RVA is zero (a
|
||||
standard `FirstThunk`, or a delay-load import address or name table) is
|
||||
reported as `malformed_entry` with no functions, never as a complete empty
|
||||
inventory. Delay-load descriptors that store virtual addresses rather than RVAs
|
||||
are recorded as `unsupported_form` and are not decoded.
|
||||
|
||||
This workflow does not resolve bound imports, loaded IAT contents, or
|
||||
export-forward names in another DLL.
|
||||
+86
-9
@@ -12,6 +12,11 @@ sandbox. Filesystem observation paths select what REA snapshots; they do not
|
||||
restrict what the process can read or write. The inherited environment is not
|
||||
recorded, so its influence may remain unknown.
|
||||
|
||||
The installed PTY backend cannot preserve an explicit environment name
|
||||
`__proto__`. REA rejects that override before launch; use `/usr/bin/env` with
|
||||
`__proto__=VALUE` as an argument when that literal variable is required. The
|
||||
reserved `REA_PROCESS_RUN_ID` name also remains unavailable to scenario overrides.
|
||||
|
||||
## Host support
|
||||
|
||||
Capturing a new scenario currently requires Linux or macOS and a working
|
||||
@@ -241,10 +246,24 @@ A missing entry proves creation or deletion only when the corresponding
|
||||
observation root's path enumeration was exhausted. Otherwise its effect has
|
||||
`status: "unknown"`, the available before/after state, and a reason; a null
|
||||
state in this variant means unobserved. Paths below symlinks that REA did not
|
||||
follow also remain unknown. Partial enumeration of one root does not erase
|
||||
follow also remain unknown. Each observed symlink records a
|
||||
`symlink_not_followed` enumeration omission with a null system code, including
|
||||
a stable selected root. The link's own metadata can remain unchanged while
|
||||
descendant coverage and filesystem comparison remain unknown. Physical
|
||||
parent-directory enumeration still proves unrelated sibling absence.
|
||||
Partial enumeration of one root does not erase
|
||||
known effects in another, and unavailable content hashes do not by themselves
|
||||
make path absence unknown.
|
||||
|
||||
Filesystem coverage records `unobserved_subtrees` separately from metadata
|
||||
enumeration. `symlink_not_followed` identifies a link whose own metadata was
|
||||
observed while its descendants were outside the snapshot. `observer_owned`
|
||||
identifies the exact directory allocated for this capture's bookkeeping; it
|
||||
is created before the baseline and excluded from both snapshots and file-entry
|
||||
capacity. Target activity inside that excluded directory remains unobserved.
|
||||
Matching filenames elsewhere are included normally. Historical captures that
|
||||
omit subtree facts do not establish descendant absence under that scope.
|
||||
|
||||
When a follow-up read fails after an entry's metadata was observed, the
|
||||
snapshot keeps unaffected observations and records an `entry_unavailable`
|
||||
enumeration omission with the root-aliased path and system error code.
|
||||
@@ -252,7 +271,20 @@ Successfully observed identity replacements use `identity_changed` with a null
|
||||
system code. Unstable symlink targets are omitted; already observed directory
|
||||
metadata remains available. The affected root stays incomplete, so omitted
|
||||
entries do not prove deletion. A path missing at its initial lookup remains an
|
||||
observed absence. Cancellation and unexpected I/O failures still stop capture.
|
||||
observed absence.
|
||||
|
||||
Before launch, a selected root below a non-directory, or one whose lookup is
|
||||
denied or otherwise unavailable, fails capture. After admission, a root or descendant lookup that
|
||||
reports a missing path or a non-directory ancestor is an observed absence. A
|
||||
denied or otherwise unavailable lookup records an `entry_unavailable` omission
|
||||
for that path instead, and its root stays incomplete. Cancellation and
|
||||
unexpected I/O failures still stop capture.
|
||||
|
||||
An initial selected-root permission denial returns `access_denied` with the
|
||||
selected path, native `EACCES` or `EPERM`, and filesystem-read recovery. Capture
|
||||
does not launch the target. Earlier snapshot facts and the denied root's
|
||||
omission remain in the partial observation; unresolved read cleanup retains its
|
||||
own classification until its exact resources are released.
|
||||
|
||||
Terminal frame `data` is the comparison text after the selected normalization.
|
||||
New captures also preserve `raw_data` when normalization changes a PTY chunk;
|
||||
@@ -262,6 +294,39 @@ while comparisons and trace assertions use the normalized text. Retaining the
|
||||
original adds at most `limits.output_bytes` of text, because it comes from the
|
||||
same admitted PTY chunks rather than a second unbounded stream.
|
||||
|
||||
When PID normalization is enabled, the root and identifiers in retained process
|
||||
samples use the same identity set in terminal text and sampled commands. Raw
|
||||
PTY chunks emitted before a child was sampled are projected again from their
|
||||
retained source after sampling stops; custom patterns still run after PID
|
||||
replacement. Earlier declared normalization omissions remain explicit; omitted
|
||||
chunks are not reconstructed. Numbers not in that observed set remain literal.
|
||||
Captures commit
|
||||
`pid_normalization_version: "sampled-identifiers-v2"`, so older root-only terminal
|
||||
normalization is not silently compared as equivalent.
|
||||
|
||||
Headless rendering runs once at terminal finalization from the already retained,
|
||||
byte-bounded PTY chunks, preserving their elapsed times and resize order. Parsing
|
||||
latency therefore occurs during finalization; rendered journal entries record
|
||||
that derived observation. A corrected raw-text projection that exceeds
|
||||
`limits.output_bytes` returns a typed capacity failure with collected facts in
|
||||
partial observations. No additional raw-screen copy or replay is retained.
|
||||
|
||||
Before failure cleanup renders retained observations, REA cancels scheduled
|
||||
interactions, waits for sampling to stop, and stops the owned process group.
|
||||
The terminal finalizer then freezes PTY input, retains normalized and rendered
|
||||
facts, and releases the renderer. Cleanup retry reuses those completed facts.
|
||||
All new captures commit
|
||||
`terminal_observation_version: "deferred-rendered-journal-v1"`, including when
|
||||
PID and port normalization are disabled. Historical Evidence remains readable;
|
||||
recapture both sides with the same observation version before comparison.
|
||||
|
||||
When path normalization is enabled, aliases apply to selected path identities
|
||||
at clear text delimiters. A selected executable path does not alias a longer
|
||||
sibling filename such as `tool-helper`; selected parent directories still
|
||||
normalize within descendant paths. The comparison contract records these rules as
|
||||
`path-component-boundaries-v2`, so captures made with the earlier prefix rules
|
||||
cannot be compared as if the text had the same meaning.
|
||||
|
||||
Rendered frame `lines` hold the visible rows after normalization, with trailing
|
||||
U+0020 spaces removed. Blank rows and internal spaces remain. `columns` records
|
||||
terminal cell width; normalization and Unicode can make string lengths differ
|
||||
@@ -328,6 +393,10 @@ validation is recorded as `unverified` with its reason. Cancellation before any
|
||||
deadline keeps the existing terminal kill. Descendants are not signalled during
|
||||
finalization; owned-process cleanup handles them afterwards.
|
||||
|
||||
For an `exited` capture, `exit.code` is the target's normal exit code. It is
|
||||
`null` when the target ended on a signal, such as one sent by a scenario
|
||||
`signal` event, and the observed signal stays in `exit.signal`.
|
||||
|
||||
`exit.reason` keeps the initiating deadline (`timeout` or `idle_timeout`).
|
||||
`exit.code` is `null` for every deadline exit; the code of an exit observed
|
||||
during finalization is `exit.finalization.exit_code`, and the observed signal
|
||||
@@ -410,10 +479,12 @@ Every capture requires `truncation_details`, with separate accounting for:
|
||||
bytes, plus observed/retained frame counts. This is independent of the raw
|
||||
chunk budget and does not count JSON encoding. Repeated screen snapshots can
|
||||
exhaust it even when all raw output fits. Rendered states use retained PTY
|
||||
input, so raw omissions also limit rendered coverage. When bounded
|
||||
normalization omits a rendered frame before its final size is known,
|
||||
`unknown_size_frames` reports that count; `observed_bytes` includes only
|
||||
rendered frame sizes that were determined exactly.
|
||||
input, so raw omissions also limit rendered coverage. `unknown_size_frames`
|
||||
counts frames whose complete size was not calculated; it does not by itself
|
||||
identify normalization as the cause. `normalization_over_budget_frames`
|
||||
counts the subset of unknown-size frames stopped by an ordered normalization
|
||||
stage; it cannot exceed `unknown_size_frames`.
|
||||
`observed_bytes` includes only rendered frame sizes determined exactly.
|
||||
- `filesystem_before` and `filesystem_after`: file-count/depth limits,
|
||||
enumeration failures, whole-file hash budget and bytes successfully hashed.
|
||||
Directory names are read incrementally within the remaining entry capacity.
|
||||
@@ -439,7 +510,9 @@ Every capture requires `truncation_details`, with separate accounting for:
|
||||
Denied or failed procfs reads mark
|
||||
sampling partial while preserving readable process rows and thread children;
|
||||
a process with unreadable identity is not attributed. Expected process-exit
|
||||
races are separate from failed observations. Shutdown counts only when it
|
||||
races are separate from failed observations. A native token reader's confirmed
|
||||
process absence during initialization is an expected exit; token-read denial
|
||||
and unavailable snapshots still count as failed observations. Shutdown counts only when it
|
||||
interrupts a read or leaves traversal unfinished; a completed pass does not
|
||||
become partial merely because the sampler stops. Completed rows
|
||||
remain available and issued reads settle before final coverage is returned.
|
||||
@@ -451,7 +524,11 @@ Trace assertions use coverage for the sources they select, so an assertion
|
||||
about complete raw terminal output need not fail because rendered snapshots
|
||||
were omitted. A trace difference applies to the dimensions identified by its
|
||||
diagnostic; unrelated dimensions retain their own comparison, including unknown
|
||||
coverage. Captures missing `truncation_details` are rejected on import;
|
||||
coverage. A finite-variant omission stays nonconforming to that variant, but does
|
||||
not mark a shared successor changed merely because its journal position shifted.
|
||||
Observed ordering violations remain changes. When a requested trace differs or
|
||||
its journal is unavailable and per-source values cannot locate the difference,
|
||||
`first_divergence` is unknown rather than evidence that no divergence occurred. Captures missing `truncation_details` are rejected on import;
|
||||
preserve them as historical files and recapture for current comparisons.
|
||||
`limits.file_bytes` is already the cumulative whole-file hashing budget,
|
||||
independent of `limits.output_bytes`, which bounds retained terminal content.
|
||||
@@ -493,7 +570,7 @@ current token proof is still required before signaling. Swept cleanup verifies t
|
||||
remaining family after its group signals. Coverage is bounded by the native
|
||||
snapshots and generations inspected; sampled group IDs select candidates.
|
||||
|
||||
When the host withholds an unrelated process’s ownership token, REA leaves that
|
||||
When the host withholds an unrelated process's ownership token, REA leaves that
|
||||
process untouched and records its PID and reason in `cleanup.unverified_processes`
|
||||
and process residual unknowns. Successful cleanup verifies the owned group;
|
||||
it does not attribute those unrelated processes. Related or otherwise unexplained
|
||||
|
||||
@@ -6,23 +6,47 @@ MCP. They read archive bytes without launching the application. Bytecode
|
||||
source decompilation, encrypted PYZ recovery, zipapp and PEX analysis are outside
|
||||
this capability.
|
||||
|
||||
`extract_artifact.coverage` preserves the inventory producer's source traversal
|
||||
scope and certainty. Successful publication proves that the selected inventoried
|
||||
regular files were written and verified; it does not prove that every source
|
||||
entry was representable or every embedded payload was decoded. For example, a
|
||||
directory containing an invalid UTF-8 filename can publish its representable
|
||||
files successfully while coverage remains `unknown`. Extraction retains the
|
||||
inventory's command provenance and limitations, plus the selected occurrences'
|
||||
path-qualified derivation notices. A reconstructed `.pyc` header therefore stays
|
||||
identified as derived bytes in the extraction result.
|
||||
|
||||
## Facts and focused queries
|
||||
|
||||
The root occurrence's `format_metadata` records the CArchive layout (2.0 or
|
||||
2.1), archive and cookie offsets, table extent, entry count, raw Python version,
|
||||
normalized major/minor when known, library name and encryption marker. Member
|
||||
metadata retains the original name, table index, type, compression flag,
|
||||
relative offset and known directory record extent. Logical paths use archive
|
||||
path rules; the source name retains its spelling, including backslashes.
|
||||
relative offset and known directory record extent. Logical paths follow the
|
||||
carrier's path dialect: backslashes separate components for PE carriers, while
|
||||
they remain literal filename characters for POSIX carriers. The source name
|
||||
always retains its stored spelling. If the carrier dialect is unconfirmed, REA
|
||||
preserves backslashes verbatim instead of inferring POSIX semantics.
|
||||
`relative_offset` is relative to the CArchive start for CArchive rows, or to
|
||||
the decoded PYZ image for PYZ rows. A CArchive directory record starts at
|
||||
`archive_start + toc_offset + directory_record_offset`; its size is
|
||||
`directory_record_size`. PYZ row directory extents remain unknown.
|
||||
CArchive type `b` retains PyInstaller's executable-member declaration in the
|
||||
generic `executable` fact; type `x` remains non-executable. This comes from the
|
||||
archive type code, not the filename or member contents.
|
||||
Distinct CArchive rows retain their table-index identities even when they
|
||||
share a logical path. Inventory can preserve both a file and a decoded PYZ
|
||||
directory at that path, including the PYZ children; extraction refuses a
|
||||
destination conflict instead of silently omitting those children.
|
||||
|
||||
Namespace packages remain directories. Their `relative_offset` and
|
||||
Namespace packages remain metadata declarations at their projected query path;
|
||||
they do not claim a destination directory. Their `relative_offset` and
|
||||
`compressed_size` preserve any payload declared by older producers, while
|
||||
`declared_size` is zero because no bytecode is materialized for a namespace.
|
||||
Every declared span must end before the PYZ table of contents.
|
||||
Malformed CArchive TOC rows identify their zero-based table index and byte offset
|
||||
within the TOC, including truncated headers, invalid names, compression flags,
|
||||
and type codes. The failure retains its original format reason.
|
||||
PYZ `compression_flag` is derived from storage: nonempty payloads are compressed,
|
||||
including legacy namespaces; an empty namespace has no compressed payload.
|
||||
|
||||
@@ -49,6 +73,15 @@ table index; original names remain available in the supplied artifact.
|
||||
Path failures use bounded escaped previews and report the original length;
|
||||
accepted paths and retained source names are not shortened.
|
||||
|
||||
`compare_artifacts` compares these node-less records as declarations using the
|
||||
container, container name, source name and type code. Equal option or dependency
|
||||
declarations can therefore compare unchanged without implying that an external
|
||||
dependency's bytes were found. For materialized PyInstaller members, the same
|
||||
fields participate in the metadata delta independently of content identity;
|
||||
archive offsets and table positions remain location facts, not declaration
|
||||
identity. Missing declaration facts and unavailable non-metadata content remain
|
||||
unknown.
|
||||
|
||||
Request `detail: "summary"` for a compact inspection, then query retained
|
||||
Evidence with `inspect_analysis_view`. An inventory view can select
|
||||
`collection: "occurrences"`, `entry_kinds: ["metadata"]`, exact logical paths or
|
||||
@@ -87,6 +120,10 @@ Outer-compressed PYZ images have a separate per-reader capacity of 128 private
|
||||
files, bounding retained inodes and image identities. The complete image count
|
||||
is admitted before any temporary file is created; excess images return a
|
||||
capacity failure. This is a resource policy, not a PyInstaller format limit.
|
||||
Artifact errors retain this distinction as `details.boundary: "capacity"`, with
|
||||
the actual allowance in the diagnosis and `details.detail`. Changing output
|
||||
detail does not reduce that allowance's input scope; these operations do not
|
||||
select a narrower member path during inventory.
|
||||
Only a zero-byte read establishes EOF; short successful descriptor reads fill
|
||||
the bounded range. Compressed members must consume their complete declared
|
||||
input span; trailing junk and concatenated streams are refused.
|
||||
@@ -95,8 +132,15 @@ returns a resource failure.
|
||||
|
||||
Supported final Python bytecode magic values cover Python 3.7 through 3.14.
|
||||
Supported marshal directory values are lists, tuples, strings, integers and
|
||||
bytes. An unsupported magic or directory stays an opaque member with a
|
||||
limitation and `pyz_status: "opaque"`; its module contents remain unknown.
|
||||
bytes, including interned Unicode strings used for module names. Unicode strings
|
||||
use CPython's UTF-8 `surrogatepass` encoding, preserving lone surrogate code
|
||||
points in decoded module names. Directory values follow the identified CPython marshal semantics; for
|
||||
example, a long integer with a zero most-significant digit is malformed and is
|
||||
not normalized into a valid table value. An unsupported magic or malformed or
|
||||
unsupported directory stays an opaque member with a limitation and
|
||||
`pyz_status: "opaque"`; no child entries from that table are projected, and
|
||||
their module contents remain unknown. The original PYZ member and its digest
|
||||
remain available independently. Healthy CArchive siblings are still inventoried.
|
||||
Any opaque PYZ makes producer traversal coverage unknown, including retained
|
||||
summaries and exhausted queries. Per-archive reasons remain on the opaque
|
||||
occurrences; the coverage observation summarizes their count without repeating
|
||||
@@ -107,11 +151,15 @@ The directory declares compressed module lengths, so uncompressed declared
|
||||
sizes and source spans inside decoded data remain unknown.
|
||||
|
||||
PYZ modules receive a derived 16-byte `.pyc` header from their own supported
|
||||
magic. CArchive bytecode receives that header only when every sibling PYZ magic
|
||||
and the cookie's Python version agree. Otherwise it retains its original bytes
|
||||
and `bytecode_header: "unknown"`. `bytecode_magic` and graph derivation edges
|
||||
identify synthesized headers; member digests cover the resulting derived files.
|
||||
These headers do not establish successful execution or decompilation.
|
||||
magic. CArchive scripts receive that header only when every sibling PYZ magic
|
||||
and the cookie's Python version agree. Under the same agreement, CArchive
|
||||
modules (`m`/`M`) receive a header only when their decompressed prefix identifies
|
||||
headerless marshal code. Legacy modules that retain a `.pyc` header, and modules
|
||||
whose prefix is unrecognized, preserve their original bytes and report
|
||||
`bytecode_header: "unknown"`. A header prefix does not establish that the entire
|
||||
file is valid. `bytecode_magic` and graph derivation edges identify synthesized
|
||||
headers; member digests cover the resulting derived files. These headers do not
|
||||
establish successful execution or decompilation.
|
||||
|
||||
The nonempty CArchive Python module `pyimod00_crypto_key` (`m`/`M`) is recorded
|
||||
as an encryption marker; a same-named data file or script is not. Affected
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
# CPython pyc inspection
|
||||
|
||||
`inspect_python_pyc` / `inspect-python-pyc` reports the supported CPython format
|
||||
and version declared by one selected local `.pyc` file's magic, how its 16-byte
|
||||
header records source identity, and which nested code objects its decoded
|
||||
marshal graph contains. It reads the file without running bytecode and requires
|
||||
no Python installation, active binary target, Hopper, Ghidra, or IDA.
|
||||
|
||||
```bash
|
||||
rea inspect-python-pyc /absolute/path/to/module.pyc --json
|
||||
```
|
||||
|
||||
The CLI resolves relative paths against the caller's working directory. MCP
|
||||
requires an absolute filesystem path and preserves that exact selection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_python_pyc",
|
||||
"arguments": { "path": "/artifacts/module.pyc" }
|
||||
}
|
||||
```
|
||||
|
||||
## Selected code objects
|
||||
|
||||
Complete output remains the default. To read selected code objects from a large
|
||||
report, request MCP `detail: "summary"`. The summary contains artifact/header
|
||||
identity, source coverage and a limitation count; its
|
||||
`normalized_result.parent_evidence_id` identifies the complete retained report.
|
||||
Use that parent ID with `inspect_analysis_view` and:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "python-pyc",
|
||||
"collection": "code_objects",
|
||||
"names": ["parse_args", "parse_known_args"],
|
||||
"fields": [
|
||||
"name",
|
||||
"qualname",
|
||||
"filename",
|
||||
"firstlineno",
|
||||
"bytecode",
|
||||
"nested_code"
|
||||
],
|
||||
"offset": 0,
|
||||
"limit": 64
|
||||
}
|
||||
```
|
||||
|
||||
This is the request's `view` field. To receive these selected facts in the first
|
||||
call, include the same `view` directly in `inspect_python_pyc` alongside
|
||||
`detail: "summary"`. Later pages use the returned parent ID with
|
||||
`inspect_analysis_view`. `view` requires summary delivery; complete delivery
|
||||
rejects a view instead of ignoring it. `names` matches retained display strings and
|
||||
returns every same-name object. `indices` selects original `code_objects`
|
||||
indexes; combining names and indices intersects those selectors. Omitted
|
||||
selectors include all rows, while `[]` selects none. Each row always includes
|
||||
`source_index`, scoped to its parent Evidence; `fields: []` returns only that
|
||||
index. Filtering preserves source order, and paging counts matching rows.
|
||||
The view's `pyc.coverage` preserves source coverage independently of page
|
||||
coverage. Views preserve `nested_code` indexes and their code-constant semantics.
|
||||
|
||||
CLI summary delivery requires a durable destination for the complete Evidence:
|
||||
|
||||
```bash
|
||||
rea inspect-python-pyc module.pyc --detail summary --evidence_output module.evidence.json --json
|
||||
```
|
||||
|
||||
Add `--view '{"kind":"python-pyc","collection":"code_objects","names":["parse_args"],"fields":["name","bytecode"]}'`
|
||||
to select facts in that initial CLI call.
|
||||
|
||||
The output's `summary` is derived Evidence. Pass its parent ID and the saved
|
||||
bundle to `inspect-analysis-view --evidence-bundle module.evidence.json` for
|
||||
offline queries. MCP views read retained Evidence, and offline views read the
|
||||
bundle; neither requires the original pyc to remain present. Parse budgets and
|
||||
complete-source retention still apply to summary delivery.
|
||||
|
||||
## Observations
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
`header` contains:
|
||||
|
||||
- `magic`, the 16-bit little-endian field;
|
||||
- `python_version` and `code_layout`, mapped from the pinned CPython 3.7–3.13
|
||||
table (`py37`, `py38`, or `py311`); 3.11 alpha magics before 3494 used
|
||||
other code layouts and are unsupported. The 3.7.0a1/a2 magics 3390/3391
|
||||
used 12-byte headers and are outside this reader's supported format;
|
||||
- `flags`, `hash_based`, and `check_source` from PEP 552;
|
||||
- `source_hash_hex` for hash-based pycs, or `source_mtime` and `source_size`
|
||||
for timestamp pycs.
|
||||
|
||||
`code_objects` lists each code object reachable in the decoded marshal graph
|
||||
once, the top-level module code first. This follows CPython container behavior;
|
||||
it is not an inventory of raw serialized code-object records. `nested_code`
|
||||
holds the indexes of the distinct code objects reachable from that object's
|
||||
constants, including code nested in tuples and other containers and the owner
|
||||
itself when a constant container reaches it.
|
||||
A function shared through a marshal reference is named by index once per owner,
|
||||
even when that owner reaches it again through the same container. Relationships
|
||||
are collected after the marshal graph is complete, so a container that a nested
|
||||
code object also references is not summarized while its items are still missing.
|
||||
Each object reports name, optional qualname,
|
||||
filename, the signed 32-bit `co_firstlineno` value, argument counts, flags,
|
||||
names, locals (or `localsplusnames` on 3.11+), const
|
||||
summaries, and bytecode length plus SHA-256.
|
||||
|
||||
Code-object equality used by sets and dictionaries follows the selected CPython format:
|
||||
3.7–3.10 derive `CO_NOFREE` from the free/cell tables for equality, while
|
||||
3.11+ compare raw flags; raw `co_flags` remain in reported facts. The older
|
||||
layouts ignore line tables, while 3.11+ include line and exception tables and
|
||||
skip inline-cache code units according to the cache table for the exact magic.
|
||||
Reported bytecode length and SHA-256 still describe the original code bytes.
|
||||
This does not emulate CPython's deoptimization of specialized or instrumented
|
||||
opcode bytes in handcrafted pyc files.
|
||||
The artifact SHA-256 identifies the exact selected `.pyc` bytes independently
|
||||
of this decoded object graph. Reference slots follow each tag's CPython behavior:
|
||||
singleton values and `TYPE_REF` do not reserve new slots even when their tag
|
||||
has `FLAG_REF` set.
|
||||
|
||||
Reported names are JavaScript display strings; distinct CPython Unicode names
|
||||
can therefore have the same displayed value and cannot always be reconstructed
|
||||
from that field alone. Code equality uses the retained marshal string bytes,
|
||||
while the artifact SHA-256 continues to identify the original file bytes.
|
||||
|
||||
Bytecode is not decompiled. Const strings may be truncated in the summary while
|
||||
the marshal walk still consumed the original bytes; a string summary's `bytes`
|
||||
is that marshal length. Integer consts report `bits`; their decimal `value` is
|
||||
`null` above 256 bits. Non-finite float and complex parts use CPython's `inf`,
|
||||
`-inf`, and `nan` spellings; negative zero uses `"-0.0"` so JSON preserves its
|
||||
sign. String previews preserve source lone surrogates but never split a valid
|
||||
surrogate pair at the preview boundary. Dictionary and set lengths follow CPython:
|
||||
unhashable keys and members fail the walk, equal set members count once, equal
|
||||
dictionary keys keep the first key object and replace only its value, code
|
||||
objects are valid keys and members only when all of their consts are hashable,
|
||||
and a null dictionary value ends the dict without that entry.
|
||||
|
||||
## Limits and failures
|
||||
|
||||
The selected file is limited to 16 MiB by default (64 MiB maximum). Marshal
|
||||
object count, nesting depth, and per-string size are bounded; exceeding a
|
||||
budget fails without partial success. Decode-limit failures are deterministic:
|
||||
the recovery action names the selected field and CLI option, its effective
|
||||
value, and its accepted maximum. Raise that field on the next call when the
|
||||
maximum permits it; waiting for another analysis or increasing host memory does
|
||||
not raise these controls. Argument counts, positional-only counts,
|
||||
keyword-only counts, and local counts must be non-negative. `co_stacksize` and
|
||||
`co_flags` are retained as signed 32-bit source fields when the selected CPython
|
||||
constructor accepted them. CPython 3.7 and 3.8 alpha constructors through
|
||||
3.8.0a4 did not reject negative stack sizes or flags; the check was added by
|
||||
[CPython commit a9f05d69](https://github.com/python/cpython/commit/a9f05d69ccbf3c75cdd604c25094282697789a62).
|
||||
Magic 3410 is ambiguous: 3.8.0a4 and 3.8.0b1 both use it, but they are on
|
||||
opposite sides of that constructor change. Negative values under magic 3410
|
||||
are preserved with an explicit limitation because the header cannot identify
|
||||
which constructor revision produced them. Magic 3411 and later 3.8 magics were
|
||||
introduced after that change, so their constructors reject negative values.
|
||||
The pinned magic entries distinguish these cases; see the CPython
|
||||
[3.7.0 constructor](https://github.com/python/cpython/blob/v3.7.0/Objects/codeobject.c),
|
||||
[3.8.0a4 constructor](https://github.com/python/cpython/blob/v3.8.0a4/Objects/codeobject.c),
|
||||
[3.8.0b1 constructor](https://github.com/python/cpython/blob/v3.8.0b1/Objects/codeobject.c),
|
||||
and [3.8.0b2 magic table](https://github.com/python/cpython/blob/v3.8.0b2/Lib/importlib/_bootstrap_external.py).
|
||||
Complete marshal coverage means all fields in the selected file were decoded;
|
||||
it does not establish that CPython will accept or execute the code object. On
|
||||
the 3.11+ layout, `co_code` must cover a
|
||||
whole number of code units. Every layout rejects argument counts that do not
|
||||
fit the local table CPython checks (`co_varnames`, including `*args` /
|
||||
`**kwargs`, or fast locals on 3.11+). Those files are malformed, so the
|
||||
workflow does not report complete coverage. 3.7–3.10 still accept an odd
|
||||
`co_code`, matching those constructors. Nested code collection walks reference
|
||||
chains iteratively and keeps one shared descendant list for a container that
|
||||
merely wraps another, instead of copying that list onto every wrapper. Set and
|
||||
dictionary keys walk tuple and frozenset reference chains the same way, so a
|
||||
long `TYPE_REF` chain stays within the call stack. A shared child stored twice
|
||||
still counts its expanded key against the string budget. Code-object equality
|
||||
keys record each name table's length and each entry's boundaries, so `()` and
|
||||
`("",)` stay distinct, and `co_varnames`, `co_freevars`, and `co_cellvars` stay
|
||||
separate tables. Names repeated through a marshal reference, including each
|
||||
code-constant name the report copies, count against the file budget once per
|
||||
copy. Empty strings add no bytes, so every copied
|
||||
name-table entry (`co_names`, varnames, freevars, cellvars, and
|
||||
`localsplusnames`) counts against the object budget before its array is built.
|
||||
Copied constant tables and each reported nested code relationship count
|
||||
against that same object budget. Integer digits and expanded
|
||||
set or dictionary keys, including a shared tuple repeated at each level, count
|
||||
against the string budget. Magics outside the pinned 3.7–3.13 table,
|
||||
truncated headers, trailing marshal bytes, and a non-code top-level object are
|
||||
typed failures.
|
||||
|
||||
A decoded header does not prove the matching `.py` is present. `python_version`
|
||||
is the magic table, not a running interpreter. This workflow does not recover
|
||||
source.
|
||||
+59
-32
@@ -1,7 +1,7 @@
|
||||
<div align="center">
|
||||
|
||||
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · **العربية** · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
|
||||
|
||||
# REA: هندسة عكسية لأي شيء
|
||||
|
||||
@@ -60,12 +60,14 @@
|
||||
|
||||
يربط REA وكيلك بأدوات لفحص الملفات الثنائية للشيفرة الأصلية، وتطبيقات JavaScript وElectron، وتجميعات .NET، والمواقع. يمكنك أيضًا استخدام الأدوات نفسها من الطرفية. يجري التحليل محليًا، وتتضمن النتائج الأدلة والقيود التي تستند إليها كل خلاصة.
|
||||
|
||||
يسجّل الإعداد REA لدى وكيلك ويثبّت تعليمات سير العمل المطابقة. يمكن لتحليل الشيفرة الأصلية استخدام تثبيت موجود من Hopper أو Ghidra؛ ويمكن للإعداد أيضًا تثبيت Hopper اختياريًا بعد موافقتك. لا يحتاج التحليل الثابت لـ JavaScript إلى أي من المحركين.
|
||||
يسجّل الإعداد REA لدى وكيلك ويثبّت تعليمات العمل المطابقة. يمكن للتحليل الأصلي استخدام Hopper أو Ghidra أو IDA المثبّت مسبقًا؛ ويمكن تثبيت Hopper بعد موافقتك. لا يحتاج التحليل الثابت لـ JavaScript إلى محرك تحليل أصلي.
|
||||
|
||||
> **[زر موقع REA](https://rea.tools/ar/)** للاطلاع على تعليمات الإعداد والأدلة المصوّرة ودراسات الحالة الفعلية.
|
||||
|
||||
## البدء السريع
|
||||
|
||||
على Linux، يتطلب تشغيل Ghidra وJADX وأدوات البرامج الثابتة والتحليل الثابت لـ JavaScript/Electron وجود `ps` متوافق مع procps في `PATH` الخاص بـ REA للتحقق من ملكية العمليات. راجع متطلب Linux لخطوات المعالجة.
|
||||
|
||||
### إعداد وكيلك
|
||||
|
||||
بعد تثبيت Node.js وnpm، نفّذ:
|
||||
@@ -78,6 +80,8 @@ npx rea-agents setup
|
||||
|
||||
يدعم الإعداد Claude Code وCodex وCursor وGemini CLI وGrok Build و[وكلاء آخرين](../installation.md#supported-agents). راجع [التثبيت والإعداد](../installation.md) لتهيئة موفّري التحليل وتسجيل MCP يدويًا.
|
||||
|
||||
يستخدم MCP مخططات إدخال مختصرة افتراضيًا. للحصول على إرشادات الحقول الكاملة وتفاصيل التحقق المتداخل، اضبط `REA_MCP_INPUT_SCHEMA_PROFILE=full` في تسجيل MCP. يحتفظ الخياران بجميع الأدوات والتحقق القياسي نفسه على الخادم؛ راجع دليل عقود MCP.
|
||||
|
||||
### اطلب من وكيلك
|
||||
|
||||
```text
|
||||
@@ -109,12 +113,16 @@ rea --help
|
||||
|
||||
يتطور REA بسرعة، وتتضمن الإصدارات الجديدة إصلاحات متكررة للأخطاء. حافظ على تحديث نسختك.
|
||||
|
||||
لواجهة CLI المثبّتة عبر npm:
|
||||
لواجهة CLI المثبّتة عبر `npm install --global` / `bun add --global`:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
`rea --update` اسم بديل لـ `rea update` يقبل خيارات الإخراج العامة نفسها. تحتفظ أوامر التحديث بإجمالي 4 MiB كحد أقصى من stdout/stderr. تُرفض بيانات التعريف الأكبر بعد انتهاء الأمر؛ وتحتفظ عمليات التثبيت المطوّلة بتشخيص محدود. يحترم عرض التقدم الضغط العكسي للطرفية وينتظر اكتمال الكتابات المرسلة.
|
||||
|
||||
تتطلب التحديثات العامة عبر Bun الإصدار Bun 1.2.17 أو أحدث. حدّث الإصدارات الأقدم يدويًا قبل تشغيل `rea update`.
|
||||
|
||||
لتحديث تسجيلات الوكلاء والمهارة، نفّذ أمر الإعداد الذي يعرضه التحديث.
|
||||
|
||||
إذا كنت تستخدم `npx`، فحدّث إعداد وكيلك بالأمر التالي:
|
||||
@@ -125,6 +133,8 @@ npx rea-agents@latest setup
|
||||
|
||||
راجع تغييرات الإعداد وأعد تشغيل وكيلك. لأوامر CLI التي تنفّذها مرة واحدة، استخدم `npx rea-agents@latest` متبوعًا بالأمر المطلوب.
|
||||
|
||||
مع Bun، استخدم `bunx rea-agents@latest` بدلًا من `npx rea-agents@latest`.
|
||||
|
||||
## كيف يعمل REA
|
||||
|
||||
يستدعي وكيلك REA عبر MCP لفحص الهدف وتتبع الشيفرة ذات الصلة. يعيد REA النتائج مع أدلتها. يستخدمها الوكيل لطرح أسئلة متابعة، أو شرح السلوك، أو كتابة تنفيذ واختباره. تستخدم أوامر CLI سير العمل نفسه.
|
||||
@@ -137,43 +147,62 @@ npx rea-agents@latest setup
|
||||
|
||||
## ما الذي يمكنك تحليله
|
||||
|
||||
يتطلب REA إصدار Node.js 22.x (>=22.19) أو 24.x (>=24.11) أو 26+، إضافة إلى npm. تعتمد الأدوات الإضافية والأنظمة المضيفة المدعومة على الهدف:
|
||||
يتطلب REA إصدار Node.js 22.x (>=22.23.4) أو 24.x (>=24.22.1) أو 26+ (>=26.12.1)، إضافة إلى npm. تعتمد الأدوات الإضافية والأنظمة المضيفة المدعومة على الهدف:
|
||||
|
||||
| الهدف | ما يعيده REA | المتطلبات والدليل |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| الملفات الثنائية للشيفرة الأصلية | شيفرة شبه برمجية، وتعليمات تجميع، وسلاسل نصية، ورموز، واستدعاءات، ومراجع | Hopper أو Ghidra أو IDA؛ [تحليل الشيفرة الأصلية](../native-investigation.md) |
|
||||
| بنية ELF دون تشغيل | الأقسام والمقاطع والرموز ومعلومات إعادة التموضع الأصلية، وآليات الحماية المحتملة المستدلّ عليها بالفحص الثابت | pwntools يوفّره المستدعي على Linux x64؛ [تشخيص الملفات الثنائية](../binary-diagnostics.md) |
|
||||
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا على Linux x64؛ [دليل EVM دون تشغيل](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| أعطال Linux المسجّلة | سجلات note الخام، ومسجّلات المعالج/الإشارات لكل خيط مسجّل، وترشيحات اختيارية لخرائط الذاكرة | pwntools يوفّره المستدعي على Linux x64؛ GDB/pwndbg اختياريان؛ [الأعطال المسجّلة](../recorded-crashes.md) |
|
||||
| تثبيتات سطح المكتب | عائلات وقت التشغيل، والملفات الداعمة، وعلامات المكونات، والخطوات التالية | فحص محدود للملفات والترويسات;[تطبيقات سطح المكتب](../desktop-applications.md) |
|
||||
| JavaScript / Electron | الوحدات، وعمليات الاستيراد، وخرائط المصدر، والمسارات، وIPC، وعلاقات إضافات الشيفرة الأصلية | Node.js وnpm؛ [تحليل التطبيقات](../javascript-application-workflows.md) |
|
||||
| المواقع | بنية الصفحة، والسكربتات، وملاحظات الشبكة، ولقطات الشاشة المطلوبة | متصفح من عائلة Chrome؛ [تحليل المتصفح](../browser-observation.md) |
|
||||
| تسجيلات الشبكة المحفوظة | الطلبات، والاستجابات، ومحتويات الحمولة المتاحة، ومواضعها في المصدر | HAR؛ وmitmdump على Linux لتسجيلات mitmproxy بصيغتها الأصلية؛ [دليل تسجيلات الشبكة](../web-network-captures.md) |
|
||||
| التقاط الحزم | نقاط النهاية وDNS وTLS SNI وتوزيع البروتوكولات وطلبات HTTP | tshark وcapinfos يوفّرهما المُستدعي؛ [دليل pcap](../pcap-analysis.md) |
|
||||
| تجميعات .NET | البيانات الوصفية، وتعليمات CIL، واعتماديات الشيفرة الأصلية المعلنة، ومقارنات البناء | فحص ثابت؛ [دليل الشيفرة المُدارة](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| حزم Android APK | تصريحات ملف manifest، والأصناف، والدوال الناتجة عن فك الترجمة، والمراجع | JADX دون واجهة رسومية وJDK كامل على Linux/macOS/Windows x64؛ [دليل Android](../android-analysis.md) |
|
||||
| أجهزة Android | الأجهزة والحزم والعمليات والسجلات والدامبات ولقطات الشاشة ونقل APK/الملفات | adb يوفّره المُستدعي (محاكي أو جهاز)؛ [دليل ADB](../adb-device-analysis.md) |
|
||||
| مشاريع JEB | وحدات المشروع من المحرك وبصمات المصنّفات والكود الزائف للأنواع/الدوال | عميل JEB يبدأه المُستدعي ويقدّم MCP، ويُختار عبر REA_JEB_MCP_URL؛ [دليل JEB](../jeb-analysis.md) |
|
||||
| موارد Android | البيان المفكوك الترميز وجداول السلاسل واللغات وحقائق الإصدار | مُشغّل Apktool يوفّره المُستدعي؛ [دليل الموارد](../apktool-resource-analysis.md) |
|
||||
| تطبيقات Flutter | بصمة لقطة Dart وهشوش المكتبات لكل ABI وأصل بناء المحرك | لا شيء (تحليل APK ساكن)؛ [دليل Flutter](../flutter-analysis.md) |
|
||||
| البرامج الثابتة | المناطق، ونتائج الاستخراج، وما يُمرّر إلى تحليل الشيفرة الأصلية | Binwalk / Unblob على Linux؛ [دليل البرامج الثابتة](../firmware-analysis.md) |
|
||||
| الحزم المسجلة | حقول مختارة وقيم متكررة ومرشحات وقواعد فك الترميز | Linux: TShark 4.x + prlimit; [دليل التقاط الحزم](../packet-captures.md) |
|
||||
| الإشارات المنطقية المسجلة | تعليقات مفكك الترميز ونطاقات العينات ومعلومات التشخيص | Linux: sigrok-cli 0.7.2 + prlimit; [دليل الإشارات](../signal-recordings.md) |
|
||||
| الحزم والموارد | قوائم الملفات، والبصمات، وملفات plist، وبنية حزم Apple، والموارد المستخرجة | [دليل تحليل الملفات وJavaScript](../javascript-artifact-reconstruction.md)، [تطبيقات Apple](../apple-application-analysis.md) |
|
||||
| سلوك العمليات | مخرجات الطرفية، والتفاعلات، وملاحظات الخروج ونظام الملفات، ومقارنات التشغيل | Linux/macOS مع PTY أصلي؛ [تسجيل سلوك العمليات](../process-capture.md) |
|
||||
| الهدف | ما يعيده REA | المتطلبات والدليل |
|
||||
| ------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| الملفات الثنائية للشيفرة الأصلية | شيفرة شبه برمجية، وتعليمات تجميع، وسلاسل نصية، ورموز، واستدعاءات، ومراجع | Hopper أو Ghidra أو IDA؛ [تحليل الشيفرة الأصلية](../native-investigation.md) |
|
||||
| بنية ELF دون تشغيل | الأقسام والمقاطع والرموز ومعلومات إعادة التموضع الأصلية، وآليات الحماية المحتملة المستدلّ عليها بالفحص الثابت | pwntools يوفّره المستدعي على Linux x64؛ [تشخيص الملفات الثنائية](../binary-diagnostics.md) |
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا على Linux x64؛ [دليل EVM دون تشغيل](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| أعطال Linux المسجّلة | سجلات note الخام، ومسجّلات المعالج/الإشارات لكل خيط مسجّل، وترشيحات اختيارية لخرائط الذاكرة | pwntools يوفّره المستدعي على Linux x64؛ GDB/pwndbg اختياريان؛ [الأعطال المسجّلة](../recorded-crashes.md) |
|
||||
| تثبيتات سطح المكتب | عائلات وقت التشغيل، والملفات الداعمة، وعلامات المكونات، والخطوات التالية | فحص محدود للملفات والترويسات;[تطبيقات سطح المكتب](../desktop-applications.md) |
|
||||
| JavaScript / Electron | الوحدات، وعمليات الاستيراد، وخرائط المصدر، والمسارات، وIPC، وعلاقات إضافات الشيفرة الأصلية | Node.js وnpm؛ [تحليل التطبيقات](../javascript-application-workflows.md); Linux: procps `ps` |
|
||||
| المواقع | بنية الصفحة، والسكربتات، وملاحظات الشبكة، ولقطات الشاشة المطلوبة | متصفح من عائلة Chrome؛ [تحليل المتصفح](../browser-observation.md) |
|
||||
| تسجيلات الشبكة المحفوظة | الطلبات، والاستجابات، ومحتويات الحمولة المتاحة، ومواضعها في المصدر | HAR / Fiddler Everywhere SAZ؛ وmitmdump على Linux لتسجيلات mitmproxy بصيغتها الأصلية؛ [دليل تسجيلات الشبكة](../web-network-captures.md) |
|
||||
| التقاط الحزم | نقاط النهاية وDNS وTLS SNI وتوزيع البروتوكولات وطلبات HTTP | tshark وcapinfos يوفّرهما المُستدعي؛ [دليل pcap](../pcap-analysis.md) |
|
||||
| تجميعات .NET | البيانات الوصفية، وتعليمات CIL، واعتماديات الشيفرة الأصلية المعلنة، ومقارنات البناء | فحص ثابت؛ [دليل الشيفرة المُدارة](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| حزم Android APK | تصريحات ملف manifest، والأصناف، والدوال الناتجة عن فك الترجمة، والمراجع | JADX دون واجهة رسومية وJDK كامل على Linux/macOS/Windows x64؛ [دليل Android](../android-analysis.md) |
|
||||
| أجهزة Android | الأجهزة والحزم والعمليات والسجلات والدامبات ولقطات الشاشة ونقل APK/الملفات | adb يوفّره المُستدعي (محاكي أو جهاز)؛ [دليل ADB](../adb-device-analysis.md) |
|
||||
| مشاريع JEB | وحدات المشروع من المحرك وبصمات المصنّفات والكود الزائف للأنواع/الدوال | عميل JEB يبدأه المُستدعي ويقدّم MCP، ويُختار عبر REA_JEB_MCP_URL؛ [دليل JEB](../jeb-analysis.md) |
|
||||
| موارد Android | البيان المفكوك الترميز وجداول السلاسل واللغات وحقائق الإصدار | مُشغّل Apktool يوفّره المُستدعي؛ [دليل الموارد](../apktool-resource-analysis.md) |
|
||||
| تطبيقات Flutter | بصمة لقطة Dart وهشوش المكتبات لكل ABI وأصل بناء المحرك | لا شيء (تحليل APK ساكن)؛ [دليل Flutter](../flutter-analysis.md) |
|
||||
| البرامج الثابتة | المناطق، ونتائج الاستخراج، وما يُمرّر إلى تحليل الشيفرة الأصلية | Binwalk / Unblob على Linux؛ [دليل البرامج الثابتة](../firmware-analysis.md) |
|
||||
| الحزم المسجلة | حقول مختارة وقيم متكررة ومرشحات وقواعد فك الترميز | Linux: TShark 4.x + prlimit; [دليل التقاط الحزم](../packet-captures.md) |
|
||||
| الإشارات المنطقية المسجلة | تعليقات مفكك الترميز ونطاقات العينات ومعلومات التشخيص | Linux: sigrok-cli 0.7.2 + prlimit; [دليل الإشارات](../signal-recordings.md) |
|
||||
| الحزم والموارد | قوائم الملفات، والبصمات، وملفات plist، وبنية حزم Apple، والموارد المستخرجة | [دليل تحليل الملفات وJavaScript](../javascript-artifact-reconstruction.md)، [تطبيقات Apple](../apple-application-analysis.md) |
|
||||
| سلوك العمليات | مخرجات الطرفية، والتفاعلات، وملاحظات الخروج ونظام الملفات، ومقارنات التشغيل | Linux/macOS مع PTY أصلي؛ [تسجيل سلوك العمليات](../process-capture.md) |
|
||||
| ملفات Go الثنائية | إصدار المترجم المضمّن والوحدات والاستبدالات وإعدادات البناء | فحص ثابت مدمج ELF/PE/thin Mach-O; لا يتطلب تثبيت Go |
|
||||
| قواعد بيانات SQLite | الجداول والأعمدة والفهارس والعروض والمشغلات وصفوف مختارة من الجداول العادية | لقطة محلية لقاعدة البيانات/WAL؛ Node.js 24.x >=24.15 أو 26+ مع حدود SQLite الأصلية |
|
||||
| كائنات شيفرة GPU | أهداف CUDA/HIP ومعاملات وموارد النوى المختارة ونص PTX المحفوظ | فحص ثابت مدمج ELF/PE/fatbin/CUBIN/PTX; [GPU](../gpu-code-objects.md) |
|
||||
| ملفات النماذج | بيانات GGUF / safetensors الوصفية وتصريحات الموترات والمجاميع المشتقة | فحص الترويسات؛ بصمة الملف الكامل اختيارية; [GGUF / safetensors](../model-artifacts.md) |
|
||||
| بيانات IL2CPP الوصفية | صور التجميعات وأسماء الأنواع والدوال ونطاقات الفهارس في Unity global-metadata.dat | فك ثابت للإصدارات 24 و27 و29 و31 و39؛ لا يتطلب Unity |
|
||||
| Godot PCK | جرد الأرشيف واستخراجه وحقائق الترويسات والأدلة الأصلية وهوية المحتوى المختار | الصيغ الثابتة 2 و3 و4؛ لا يتطلب Godot |
|
||||
| ملفات WASM | التحقق والأقسام وأشكال WAT الدقيقة للاستيراد والتصدير وWAT المفكوك | يوفّره المستخدم WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| توقيعات APK | الموقّعون v1/v2/v3/v3.1 وبصمات الشهادات وتدوير المفاتيح واتساق الملخصات داخليًا | فحص ثابت مدمج; `inspect_apk_signature` |
|
||||
|
||||
يقرأ الفحص الثابت لـ JavaScript و.NET الملفات المقدّمة دون تشغيل التطبيق. يشغّل التسجيل أثناء التنفيذ الهدف المحدد أو يتفاعل معه بصلاحيات مستخدمك؛ ويشرح كل دليل للتسجيل أثناء التنفيذ آثاره.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
للملفات الثنائية الكبيرة، ارفع مهلة بدء الموفّر باستخدام `REA_GHIDRA_STARTUP_TIMEOUT_MS` أو `REA_HOPPER_STARTUP_TIMEOUT_MS`.
|
||||
|
||||
تختلف صيغ ملفات الشيفرة الأصلية والأنظمة المضيفة المدعومة باختلاف الموفّر. راجع [إعداد Hopper وGhidra](../installation.md#hopper)، و[دليل IDA](../ida-provider.md)، [Kuna](../kuna-provider.md)، و[دعم Ghidra التجريبي على Windows](../windows-ghidra-p0.md). يدعم Ghidra أيضًا [تحليل DOS ذي 16 بت](../ghidra-dos.md). لاختيار الموفّر، راجع [دليل CLI](../cli.md#choose-a-provider). تحقّق من [توفر الميزات في الإصدارات](../installation.md#released-package-and-main) للميزات المضافة بعد أحدث إصدار على npm.
|
||||
|
||||
## دراسات الحالة
|
||||
|
||||
[](https://rea.tools/ar/showcase/)
|
||||
<a href="https://rea.tools/ar/showcase/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="../assets/rea-showcases-dark.svg" />
|
||||
<img alt="رسوم توضيحية لأمثلة توزيع الصوت في DX-Ball وجسر الحافظة في Notion وحلقة الطلقات في TH04" src="../assets/rea-showcases.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
### DX-Ball: إعادة بناء حساب توزيع الصوت بين القناتين
|
||||
|
||||
@@ -268,8 +297,6 @@ npx rea-agents@latest setup
|
||||
|
||||
- [التثبيت والإعداد](../installation.md): تسجيل الوكلاء، وتهيئة الموفّرين، والتحديثات، وإلغاء التثبيت.
|
||||
- [الجاهزية واستكشاف الأخطاء](../installation.md#check-readiness-for-your-task): تشخيص وكيل أو محرك تحليل محدد.
|
||||
- [CUDA / HIP](../gpu-code-objects.md)
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI وEvidence](../cli.md): الأوامر، واختيار الموفّر، واللقطات، والاستيراد/التصدير، وحالات الخروج.
|
||||
- [عقود MCP](../mcp-contracts.md) و[تعليمات الوكلاء](../mcp-prompts.md): نتائج الأدوات، والجلسات، والاستقصاءات الموجّهة.
|
||||
- [كتالوج الأدوات](../mcp-contracts.md#generated-catalog): قائمة الأدوات والموفّرين وأوامر CLI المولّدة أثناء البناء.
|
||||
|
||||
+59
-32
@@ -1,7 +1,7 @@
|
||||
<div align="center">
|
||||
|
||||
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · **Deutsch** · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
|
||||
|
||||
# REA: Alles reverse-engineeren
|
||||
|
||||
@@ -60,12 +60,14 @@ Du hast in einer App eine Funktion entdeckt, die du in dein eigenes Produkt übe
|
||||
|
||||
REA verbindet deinen Agenten mit Werkzeugen zum Untersuchen nativer Binärdateien, von JavaScript- und Electron-Apps, .NET-Assemblies und Websites. Dieselben Werkzeuge kannst du auch im Terminal nutzen. Die Analyse läuft lokal; die Ergebnisse enthalten die Belege und Einschränkungen hinter jeder Schlussfolgerung.
|
||||
|
||||
Die Einrichtung registriert REA bei deinem Agenten und installiert passende Workflow-Anweisungen. Für native Analysen kannst du eine vorhandene Hopper- oder Ghidra-Installation verwenden. Mit deiner Zustimmung kann die Einrichtung auch Hopper installieren. Statische JavaScript-Analysen benötigen keines der beiden Programme.
|
||||
Die Einrichtung registriert REA bei deinem Agenten und installiert passende Workflow-Anweisungen. Native Analysen nutzen eine vorhandene Installation von Hopper, Ghidra oder IDA; nach Zustimmung kann die Einrichtung Hopper installieren. Statische JavaScript-Analysen benötigen keine native Analyse-Engine.
|
||||
|
||||
> **[Besuche die REA-Website](https://rea.tools/de/)** für Einrichtungshinweise, bebilderte Anleitungen und echte Fallstudien.
|
||||
|
||||
## Schnellstart
|
||||
|
||||
Unter Linux benötigen Starts von Ghidra, JADX und Firmware-Werkzeugen sowie statische JavaScript/Electron-Analysen ein procps-kompatibles `ps` in REAs `PATH`, um die Prozesseigentümerschaft zu prüfen. Abhilfe beschreibt die Linux-Voraussetzung.
|
||||
|
||||
### Deinen Agenten einrichten
|
||||
|
||||
Wenn Node.js und npm installiert sind, führe diesen Befehl aus:
|
||||
@@ -78,6 +80,8 @@ Wähle deine Agenten aus, prüfe die geplanten Änderungen und bestätige sie. D
|
||||
|
||||
Die Einrichtung unterstützt Claude Code, Codex, Cursor, Gemini CLI, Grok Build und [weitere Agenten](../installation.md#supported-agents). Informationen zur Provider-Konfiguration und manuellen MCP-Registrierung findest du unter [Installation und Einrichtung](../installation.md).
|
||||
|
||||
MCP verwendet standardmäßig kompakte Eingabeschemas. Für vollständige Feldhinweise und Details zur verschachtelten Validierung setze `REA_MCP_INPUT_SCHEMA_PROFILE=full` in der MCP-Registrierung. Beide Profile behalten alle Tools und dieselbe kanonische Servervalidierung; siehe MCP-Vertragsleitfaden.
|
||||
|
||||
### Deinen Agenten fragen
|
||||
|
||||
```text
|
||||
@@ -109,12 +113,16 @@ Konfiguriere für native Analysen zuerst einen Provider. Native Befehle, Provide
|
||||
|
||||
REA entwickelt sich schnell weiter; neue Versionen enthalten häufig Fehlerbehebungen. Halte deine Installation aktuell.
|
||||
|
||||
Für eine über npm installierte CLI:
|
||||
Für eine über `npm install --global` / `bun add --global` installierte CLI:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
`rea --update` ist ein Alias für `rea update` mit denselben globalen Ausgabeoptionen. Update-Befehle behalten insgesamt höchstens 4 MiB stdout/stderr. Zu große Metadatenantworten werden nach Befehlsende abgelehnt; ausführliche Installationen behalten begrenzte Diagnosen. Fortschrittsausgabe berücksichtigt Terminal-Rückstau und wartet auf bereits übergebene Schreibvorgänge.
|
||||
|
||||
Globale Bun-Updates erfordern Bun 1.2.17 oder neuer. Aktualisiere ältere Bun-Versionen manuell, bevor du `rea update` ausführst.
|
||||
|
||||
Um die Agentenregistrierungen und den Skill zu aktualisieren, führe den Einrichtungsbefehl aus, den das Update ausgibt.
|
||||
|
||||
Wenn du `npx` verwendest, aktualisiere die Agenteneinrichtung mit:
|
||||
@@ -125,6 +133,8 @@ npx rea-agents@latest setup
|
||||
|
||||
Prüfe die Änderungen und starte deinen Agenten neu. Für einzelne CLI-Aufrufe verwende `npx rea-agents@latest`, gefolgt vom gewünschten Befehl.
|
||||
|
||||
Mit Bun verwendest du `bunx rea-agents@latest` statt `npx rea-agents@latest`.
|
||||
|
||||
## So funktioniert REA
|
||||
|
||||
Dein Agent ruft REA über MCP auf, um das Ziel zu untersuchen und relevanten Code zu verfolgen. REA liefert Ergebnisse mit ihren Belegen zurück. Der Agent nutzt sie für weitere Fragen, Erklärungen oder zum Schreiben und Testen einer Implementierung. CLI-Befehle verwenden dieselben Workflows.
|
||||
@@ -137,43 +147,62 @@ Dein Agent ruft REA über MCP auf, um das Ziel zu untersuchen und relevanten Cod
|
||||
|
||||
## Was du analysieren kannst
|
||||
|
||||
REA benötigt Node.js 22.x (>=22.19), 24.x (>=24.11) oder 26+ sowie npm. Zusätzliche Werkzeuge und unterstützte Hostsysteme hängen vom Ziel ab:
|
||||
REA benötigt Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) oder 26+ (>=26.12.1) sowie npm. Zusätzliche Werkzeuge und unterstützte Hostsysteme hängen vom Ziel ab:
|
||||
|
||||
| Ziel | Was REA zurückgibt | Voraussetzungen und Anleitung |
|
||||
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native Binärdateien | Pseudocode, Assemblercode, Zeichenketten, Symbole, Aufrufe und Verweise | Hopper, Ghidra oder IDA; [native Analyse](../native-investigation.md) |
|
||||
| Offline-ELF-Struktur | Sektionen, Segmente, ursprüngliche Symbole/Relokationen und mögliche Schutzmaßnahmen aus statischer Analyse | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; [Binärdiagnostik](../binary-diagnostics.md) |
|
||||
| EVM-Bytecode | Dispatch-Selektoren, Byte-Offsets, abgeleitete Argumente und Veränderbarkeit des Zustands | Lokale Eingabe mit Rohbytes oder Hexdarstellung unter Linux x64; [Offline-EVM-Leitfaden](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Aufgezeichnete Linux-Abstürze | Unverarbeitete Note-Einträge, Register/Signale aller aufgezeichneten Threads und optionale Zuordnungskandidaten | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; optional GDB/pwndbg; [aufgezeichnete Abstürze](../recorded-crashes.md) |
|
||||
| Desktop-Installationen | Laufzeitkandidaten, Quelldateien, Komponenten und nächste Schritte | Begrenzte Verzeichnis- und Headerprüfung;[Desktop-Anwendungen](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Module, Imports, Source Maps, Routen, IPC und Beziehungen zu nativen Add-ons | Node.js und npm; [Anwendungsanalyse](../javascript-application-workflows.md) |
|
||||
| Websites | Seitenstruktur, Skripte, Netzwerkbeobachtungen und angeforderte Screenshots | Ein Browser aus der Chrome-Familie; [Browseranalyse](../browser-observation.md) |
|
||||
| Gespeicherte Netzwerkaufzeichnungen | Anfragen, Antworten, zugängliche Nutzdaten und Quellpositionen | HAR; mitmdump unter Linux für Aufzeichnungen im nativen mitmproxy-Format; [Leitfaden zu Netzwerkaufzeichnungen](../web-network-captures.md) |
|
||||
| Paketerfassungen | Endpunkte, DNS, TLS SNI, Protokollverteilung und HTTP-Anfragen | Vom Aufrufer bereitgestellte tshark- und capinfos-Binaries; [pcap-Leitfaden](../pcap-analysis.md) |
|
||||
| .NET-Assemblies | Metadaten, CIL-Anweisungen, deklarierte native Abhängigkeiten und Build-Vergleiche | Statische Untersuchung; [Leitfaden für verwalteten Code](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| Android-APKs | Manifest-Deklarationen, Klassen, dekompilierte Methoden und Verweise | JADX ohne grafische Oberfläche und ein vollständiges JDK unter Linux/macOS/Windows x64; [Android-Leitfaden](../android-analysis.md) |
|
||||
| Android-Geräte | Geräte, Pakete, Prozesse, Logs, Dumps, Bildschirmaufnahmen sowie APK- und Dateiübertragungen | Vom Aufrufer bereitgestelltes adb (Emulator oder Gerät); [ADB-Leitfaden](../adb-device-analysis.md) |
|
||||
| JEB-Projekte | Engine-gestützte Projekteinheiten, Artefakt-Prüfsummen und Pseudokode von Typen/Methoden | Ein vom Aufrufer gestarteter JEB-Client mit MCP, ausgewählt über REA_JEB_MCP_URL; [JEB-Leitfaden](../jeb-analysis.md) |
|
||||
| Android-Ressourcen | Dekodiertes Manifest, String-Tabellen, Sprachen und Versionsfakten | Vom Aufrufer bereitgestellter Apktool-Starter; [Ressourcen-Leitfaden](../apktool-resource-analysis.md) |
|
||||
| Flutter-Apps | Dart-Snapshot-Hash, Per-ABI-Bibliotheksprüfsummen und Engine-Build-Provenienz | Keine (statische APK-Analyse); [Flutter-Leitfaden](../flutter-analysis.md) |
|
||||
| Firmware | Bereiche, Extraktionsergebnisse und Übergaben an die native Analyse | Binwalk / Unblob unter Linux; [Firmware-Leitfaden](../firmware-analysis.md) |
|
||||
| Aufgezeichnete Pakete | Ausgewählte Felder, wiederholte Werte, Filter und Decode-as-Regeln | Linux: TShark 4.x + prlimit; [Mitschnitt-Anleitung](../packet-captures.md) |
|
||||
| Aufgezeichnete Logiksignale | Decoder-Anmerkungen, Sample-Bereiche und Diagnosen | Linux: sigrok-cli 0.7.2 + prlimit; [Signalaufzeichnungen](../signal-recordings.md) |
|
||||
| Pakete und Ressourcen | Dateiinventare, Hashwerte, plists, Aufbau von Apple-Bundles und extrahierte Ressourcen | [Artefakt- und JavaScript-Leitfaden](../javascript-artifact-reconstruction.md), [Apple-Anwendungen](../apple-application-analysis.md) |
|
||||
| Prozessverhalten | Terminalausgabe, Interaktionen, Beobachtungen zu Prozessende und Dateisystem sowie Laufvergleiche | Linux/macOS mit nativer PTY; [Prozessaufzeichnung](../process-capture.md) |
|
||||
| Ziel | Was REA zurückgibt | Voraussetzungen und Anleitung |
|
||||
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native Binärdateien | Pseudocode, Assemblercode, Zeichenketten, Symbole, Aufrufe und Verweise | Hopper, Ghidra oder IDA; [native Analyse](../native-investigation.md) |
|
||||
| Offline-ELF-Struktur | Sektionen, Segmente, ursprüngliche Symbole/Relokationen und mögliche Schutzmaßnahmen aus statischer Analyse | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; [Binärdiagnostik](../binary-diagnostics.md) |
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| EVM-Bytecode | Dispatch-Selektoren, Byte-Offsets, abgeleitete Argumente und Veränderbarkeit des Zustands | Lokale Eingabe mit Rohbytes oder Hexdarstellung unter Linux x64; [Offline-EVM-Leitfaden](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Aufgezeichnete Linux-Abstürze | Unverarbeitete Note-Einträge, Register/Signale aller aufgezeichneten Threads und optionale Zuordnungskandidaten | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; optional GDB/pwndbg; [aufgezeichnete Abstürze](../recorded-crashes.md) |
|
||||
| Desktop-Installationen | Laufzeitkandidaten, Quelldateien, Komponenten und nächste Schritte | Begrenzte Verzeichnis- und Headerprüfung;[Desktop-Anwendungen](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Module, Imports, Source Maps, Routen, IPC und Beziehungen zu nativen Add-ons | Node.js und npm; [Anwendungsanalyse](../javascript-application-workflows.md); Linux: procps `ps` |
|
||||
| Websites | Seitenstruktur, Skripte, Netzwerkbeobachtungen und angeforderte Screenshots | Ein Browser aus der Chrome-Familie; [Browseranalyse](../browser-observation.md) |
|
||||
| Gespeicherte Netzwerkaufzeichnungen | Anfragen, Antworten, zugängliche Nutzdaten und Quellpositionen | HAR / Fiddler Everywhere SAZ; mitmdump unter Linux für Aufzeichnungen im nativen mitmproxy-Format; [Leitfaden zu Netzwerkaufzeichnungen](../web-network-captures.md) |
|
||||
| Paketerfassungen | Endpunkte, DNS, TLS SNI, Protokollverteilung und HTTP-Anfragen | Vom Aufrufer bereitgestellte tshark- und capinfos-Binaries; [pcap-Leitfaden](../pcap-analysis.md) |
|
||||
| .NET-Assemblies | Metadaten, CIL-Anweisungen, deklarierte native Abhängigkeiten und Build-Vergleiche | Statische Untersuchung; [Leitfaden für verwalteten Code](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| Android-APKs | Manifest-Deklarationen, Klassen, dekompilierte Methoden und Verweise | JADX ohne grafische Oberfläche und ein vollständiges JDK unter Linux/macOS/Windows x64; [Android-Leitfaden](../android-analysis.md) |
|
||||
| Android-Geräte | Geräte, Pakete, Prozesse, Logs, Dumps, Bildschirmaufnahmen sowie APK- und Dateiübertragungen | Vom Aufrufer bereitgestelltes adb (Emulator oder Gerät); [ADB-Leitfaden](../adb-device-analysis.md) |
|
||||
| JEB-Projekte | Engine-gestützte Projekteinheiten, Artefakt-Prüfsummen und Pseudokode von Typen/Methoden | Ein vom Aufrufer gestarteter JEB-Client mit MCP, ausgewählt über REA_JEB_MCP_URL; [JEB-Leitfaden](../jeb-analysis.md) |
|
||||
| Android-Ressourcen | Dekodiertes Manifest, String-Tabellen, Sprachen und Versionsfakten | Vom Aufrufer bereitgestellter Apktool-Starter; [Ressourcen-Leitfaden](../apktool-resource-analysis.md) |
|
||||
| Flutter-Apps | Dart-Snapshot-Hash, Per-ABI-Bibliotheksprüfsummen und Engine-Build-Provenienz | Keine (statische APK-Analyse); [Flutter-Leitfaden](../flutter-analysis.md) |
|
||||
| Firmware | Bereiche, Extraktionsergebnisse und Übergaben an die native Analyse | Binwalk / Unblob unter Linux; [Firmware-Leitfaden](../firmware-analysis.md) |
|
||||
| Aufgezeichnete Pakete | Ausgewählte Felder, wiederholte Werte, Filter und Decode-as-Regeln | Linux: TShark 4.x + prlimit; [Mitschnitt-Anleitung](../packet-captures.md) |
|
||||
| Aufgezeichnete Logiksignale | Decoder-Anmerkungen, Sample-Bereiche und Diagnosen | Linux: sigrok-cli 0.7.2 + prlimit; [Signalaufzeichnungen](../signal-recordings.md) |
|
||||
| Pakete und Ressourcen | Dateiinventare, Hashwerte, plists, Aufbau von Apple-Bundles und extrahierte Ressourcen | [Artefakt- und JavaScript-Leitfaden](../javascript-artifact-reconstruction.md), [Apple-Anwendungen](../apple-application-analysis.md) |
|
||||
| Prozessverhalten | Terminalausgabe, Interaktionen, Beobachtungen zu Prozessende und Dateisystem sowie Laufvergleiche | Linux/macOS mit nativer PTY; [Prozessaufzeichnung](../process-capture.md) |
|
||||
| Go-Binärdateien | Eingebettete Compilerversion, Module, Ersetzungen und Build-Einstellungen | Integrierte statische Prüfung ELF/PE/thin Mach-O; Keine Go-Installation nötig |
|
||||
| SQLite-Datenbanken | Tabellen, Spalten, Indizes, Views, Trigger und ausgewählte Zeilen gewöhnlicher Tabellen | Lokaler Datenbank/WAL-Snapshot; Node.js 24.x >=24.15 oder 26+ mit nativen SQLite-Grenzen |
|
||||
| GPU-Codeobjekte | CUDA/HIP-Ziele, ausgewählte Kernelparameter und Ressourcen, gespeicherter PTX-Text | Integrierte statische Prüfung ELF/PE/fatbin/CUBIN/PTX; [GPU](../gpu-code-objects.md) |
|
||||
| Modelldateien | GGUF-/safetensors-Metadaten, Tensordeklarationen und abgeleitete Summen | Headerprüfung; optionaler Hash der gesamten Datei; [GGUF / safetensors](../model-artifacts.md) |
|
||||
| IL2CPP-Metadaten | Unity global-metadata.dat: Assembly-Images, Typ-/Methodennamen und Indexbereiche | Statische Dekodierung der Versionen 24, 27, 29, 31, 39; kein Unity nötig |
|
||||
| Godot PCK | Archivinventar und Extraktion, originale Header-/Verzeichnisdaten und ausgewählte Inhaltsidentitäten | Statische Formate 2, 3, 4; kein Godot nötig |
|
||||
| WASM-Artefakte | Validierung, Sektionen, exakte Import-/Export-WAT-Formen und dekodiertes WAT | Vom Aufrufer bereitgestellt WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| APK-Signaturen | v1/v2/v3/v3.1-Signer, Zertifikatfingerabdrücke, Schlüsselrotation und Digest-Selbstkonsistenz | Integrierte statische Prüfung; `inspect_apk_signature` |
|
||||
|
||||
Statische JavaScript- und .NET-Untersuchungen lesen die bereitgestellten Dateien, ohne die Anwendung auszuführen. Laufzeitaufzeichnungen führen das gewählte Ziel mit deinen Benutzerrechten aus oder interagieren damit; die jeweiligen Anleitungen beschreiben die Auswirkungen.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Bei großen Binärdateien kannst du die Startfrist mit `REA_GHIDRA_STARTUP_TIMEOUT_MS` oder `REA_HOPPER_STARTUP_TIMEOUT_MS` erhöhen.
|
||||
|
||||
Native Formate und unterstützte Hostsysteme unterscheiden sich je nach Provider. Siehe [Hopper- und Ghidra-Einrichtung](../installation.md#hopper), den [IDA-Leitfaden](../ida-provider.md), [Kuna](../kuna-provider.md) und die [experimentelle Ghidra-Unterstützung unter Windows](../windows-ghidra-p0.md). Ghidra unterstützt auch [16-Bit-DOS-Analyse](../ghidra-dos.md). Die Provider-Auswahl beschreibt der [CLI-Leitfaden](../cli.md#choose-a-provider). Prüfe die [Verfügbarkeit in Releases](../installation.md#released-package-and-main) für Funktionen, die seit der neuesten npm-Version hinzugekommen sind.
|
||||
|
||||
## Fallstudien
|
||||
|
||||
[](https://rea.tools/de/showcase/)
|
||||
<a href="https://rea.tools/de/showcase/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="../assets/rea-showcases-dark.svg" />
|
||||
<img alt="Illustrationen der Fallstudien zu DX-Balls Klangbalance, Notions Zwischenablage-Brücke und TH04s Geschossring" src="../assets/rea-showcases.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
### DX-Ball: Eine Stereo-Panning-Berechnung rekonstruieren
|
||||
|
||||
@@ -266,8 +295,6 @@ Beginne mit den [praxisnahen Anleitungen](https://rea.tools/de/guides/) auf der
|
||||
|
||||
- [Installation und Einrichtung](../installation.md): Agentenregistrierung, Provider-Konfiguration, Updates und Deinstallation.
|
||||
- [Bereitschaft und Fehlerbehebung](../installation.md#check-readiness-for-your-task): Diagnose eines bestimmten Agenten oder einer Analyse-Engine.
|
||||
- [CUDA / HIP](../gpu-code-objects.md)
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI und Evidence](../cli.md): Befehle, Provider-Auswahl, Snapshots, Import/Export und Exit-Status.
|
||||
- [MCP-Kontrakte](../mcp-contracts.md) und [Agenten-Prompts](../mcp-prompts.md): Werkzeugergebnisse, Sitzungen und geführte Untersuchungen.
|
||||
- [Werkzeugkatalog](../mcp-contracts.md#generated-catalog): Beim Build erzeugtes Inventar von Werkzeugen, Providern und CLI-Befehlen.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div align="center">
|
||||
|
||||
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md) · **Ελληνικά** · [മലയാളം](README_ml.md)
|
||||
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md) · **Ελληνικά** · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
|
||||
|
||||
# REA: Reverse Engineer Anything
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
|
||||
## Γρήγορη εκκίνηση
|
||||
|
||||
Στο Linux, η εκκίνηση Ghidra, JADX και εργαλείων firmware και η στατική ανάλυση JavaScript/Electron απαιτούν `ps` συμβατό με procps στο `PATH` του REA για έλεγχο ιδιοκτησίας διεργασιών. Δες την προϋπόθεση Linux.
|
||||
|
||||
### Ρύθμιση του agent σου
|
||||
|
||||
Αφού εγκαταστήσεις το Node.js και το npm, εκτέλεσε:
|
||||
@@ -80,6 +82,8 @@ npx rea-agents setup
|
||||
|
||||
Υποστηρίζονται τα Claude Code, Codex, Cursor, Gemini CLI, Grok Build και [άλλοι agents](../installation.md#supported-agents). Για τη ρύθμιση των παρόχων ανάλυσης και τη μη αυτόματη καταχώριση MCP, δες τον οδηγό [εγκατάστασης και ρύθμισης](../installation.md).
|
||||
|
||||
Το MCP χρησιμοποιεί συμπαγή σχήματα εισόδου από προεπιλογή. Για πλήρεις οδηγίες πεδίων και ένθετης επικύρωσης, όρισε `REA_MCP_INPUT_SCHEMA_PROFILE=full` στην καταχώριση MCP. Και τα δύο προφίλ διατηρούν όλα τα εργαλεία και την ίδια κανονική επικύρωση διακομιστή· δες τον οδηγό συμβολαίων MCP.
|
||||
|
||||
### Ζήτησε από τον agent σου
|
||||
|
||||
```text
|
||||
@@ -117,6 +121,8 @@ rea --help
|
||||
rea update
|
||||
```
|
||||
|
||||
Το `rea --update` είναι συνώνυμο του `rea update` με τις ίδιες καθολικές επιλογές εξόδου. Οι εντολές ενημέρωσης διατηρούν συνολικά έως 4 MiB stdout/stderr. Υπερμεγέθη μεταδεδομένα απορρίπτονται μετά την έξοδο· οι αναλυτικές εγκαταστάσεις κρατούν περιορισμένα διαγνωστικά. Η πρόοδος σέβεται την αντίθλιψη του τερματικού και περιμένει τις υποβληθείσες εγγραφές.
|
||||
|
||||
Οι ενημερώσεις καθολικής εγκατάστασης με Bun απαιτούν Bun 1.2.17 ή νεότερο. Αν χρησιμοποιείς παλαιότερη έκδοση, αναβάθμισε το Bun χειροκίνητα πριν εκτελέσεις το `rea update`.
|
||||
|
||||
Για να ανανεώσεις τις καταχωρίσεις των agents και το skill, εκτέλεσε την εντολή ρύθμισης που εμφανίζει η ενημέρωση.
|
||||
@@ -141,7 +147,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## Τι μπορείς να αναλύσεις
|
||||
|
||||
Το REA απαιτεί Node.js 22.x (>=22.19), 24.x (>=24.11) ή 26+, καθώς και npm. Τα πρόσθετα εργαλεία και τα υποστηριζόμενα συστήματα εξαρτώνται από τον στόχο:
|
||||
Το REA απαιτεί Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ή 26+ (>=26.12.1), καθώς και npm. Τα πρόσθετα εργαλεία και τα υποστηριζόμενα συστήματα εξαρτώνται από τον στόχο:
|
||||
|
||||
| Στόχος | Τι επιστρέφει το REA | Απαιτήσεις και οδηγός |
|
||||
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -149,13 +155,17 @@ npx rea-agents@latest setup
|
||||
| Δομή ELF χωρίς εκτέλεση | Ενότητες, τμήματα, αρχικά σύμβολα/εγγραφές μετεγκατάστασης και ενδείξεις πιθανών μέτρων προστασίας από στατική ανάλυση | pwntools που παρέχει ο χρήστης σε Linux x64· [διαγνωστικά δυαδικών αρχείων](../binary-diagnostics.md) |
|
||||
| Δυαδικά αρχεία Go | Ενσωματωμένη έκδοση μεταγλωττιστή, modules, αντικαταστάσεις και ρυθμίσεις μεταγλώττισης | Ενσωματωμένη στατική εξέταση ELF/PE/thin Mach-O· δεν απαιτείται εγκατάσταση του Go |
|
||||
| Δυαδικά αρχεία Rust | Ενσωματωμένα ονόματα, εκδόσεις, πηγές και σχέσεις εξάρτησης των crates από το cargo-auditable | Ενσωματωμένη στατική εξέταση ELF/PE/thin Mach-O· [δυαδικά αρχεία Rust](../rust-binaries.md) |
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Αρχείο WASM | Έλεγχο εγκυρότητας, ενότητες, ακριβείς μορφές εισαγωγών/εξαγωγών σε WAT και αποκωδικοποιημένο WAT | WABT 1.0.42 που παρέχει ο χρήστης· `inspect_wasm_artifact` |
|
||||
| Bytecode EVM | Selectors επιλογής συνάρτησης, μετατοπίσεις σε bytes, εκτιμώμενα ορίσματα και δυνατότητα μεταβολής της κατάστασης | Τοπική είσοδος σε ακατέργαστα bytes ή δεκαεξαδική μορφή σε Linux x64· [οδηγός EVM χωρίς εκτέλεση](../evm-bytecode.md) |
|
||||
| Βάσεις δεδομένων SQLite | Πίνακες, στήλες, ευρετήρια, όψεις, triggers και επιλεγμένες γραμμές κανονικών πινάκων | Τοπικό στιγμιότυπο βάσης δεδομένων/WAL· Node.js 24.x >=24.15 ή 26+ με εγγενή όρια SQLite |
|
||||
| Καταγεγραμμένες καταρρεύσεις στο Linux | Ακατέργαστες εγγραφές notes, καταχωρητές/σήματα κάθε καταγεγραμμένου νήματος και προαιρετικές πιθανές αντιστοιχίσεις μνήμης | pwntools που παρέχει ο χρήστης σε Linux x64· προαιρετικά GDB/pwndbg· [καταγεγραμμένες καταρρεύσεις](../recorded-crashes.md) |
|
||||
| Εγκαταστάσεις επιφάνειας εργασίας | Υποψήφια περιβάλλοντα εκτέλεσης, αρχεία τεκμηρίωσης, στοιχεία και επόμενα βήματα | Περιορισμένη επιθεώρηση καταλόγων και κεφαλίδων;[Εφαρμογές επιφάνειας εργασίας](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Modules, εισαγωγές, source maps, διαδρομές, IPC και σχέσεις με πρόσθετα κώδικα μηχανής | Node.js και npm· [ανάλυση εφαρμογών](../javascript-application-workflows.md) |
|
||||
| JavaScript / Electron | Modules, εισαγωγές, source maps, διαδρομές, IPC και σχέσεις με πρόσθετα κώδικα μηχανής | Node.js και npm· [ανάλυση εφαρμογών](../javascript-application-workflows.md); Linux: procps `ps` |
|
||||
| Ιστότοποι | Δομή σελίδων, scripts, παρατηρήσεις δικτύου και ζητούμενα στιγμιότυπα οθόνης | Πρόγραμμα περιήγησης της οικογένειας Chrome· [ανάλυση μέσω προγράμματος περιήγησης](../browser-observation.md) |
|
||||
| Αποθηκευμένες καταγραφές δικτύου | Αιτήματα, αποκρίσεις, διαθέσιμα δεδομένα φορτίου και θέσεις στην πηγή | HAR ή SAZ του Fiddler Everywhere· mitmdump σε Linux για την εγγενή μορφή mitmproxy· [οδηγός καταγραφών](../web-network-captures.md) |
|
||||
| Assemblies .NET | Μεταδεδομένα, εντολές CIL, δηλωμένες εξαρτήσεις από κώδικα μηχανής και συγκρίσεις builds | Καμία πρόσθετη απαίτηση (μόνο στατική ανάλυση)· [οδηγός διαχειριζόμενου κώδικα](../managed-code-analysis.md) |
|
||||
@@ -171,6 +181,11 @@ npx rea-agents@latest setup
|
||||
| Καταγεγραμμένα λογικά σήματα | Σχολιασμούς αποκωδικοποιητή, εύρη δειγμάτων και διαγνωστικά | Linux: sigrok-cli 0.7.2 + prlimit· [καταγραφές σημάτων](../signal-recordings.md) |
|
||||
| Πακέτα και πόροι | Καταλόγους αρχείων, ψηφιακά αποτυπώματα, plists, δομή bundles της Apple και εξαγόμενους πόρους | [Οδηγός αρχείων και JavaScript](../javascript-artifact-reconstruction.md), [εφαρμογές Apple](../apple-application-analysis.md) |
|
||||
| Συμπεριφορά διεργασιών | Έξοδο τερματικού, αλληλεπιδράσεις, παρατηρήσεις τερματισμού και συστήματος αρχείων, καθώς και συγκρίσεις εκτελέσεων | Linux/macOS με εγγενές PTY· [καταγραφή διεργασιών](../process-capture.md) |
|
||||
| Αντικείμενα κώδικα GPU | Στόχοι CUDA/HIP, παράμετροι και πόροι επιλεγμένων kernels, διατηρημένο κείμενο PTX | Ενσωματωμένη στατική επιθεώρηση ELF/PE/fatbin/CUBIN/PTX; [GPU](../gpu-code-objects.md) |
|
||||
| Αρχεία μοντέλων | Μεταδεδομένα GGUF / safetensors, δηλώσεις tensors και παράγωγα σύνολα | Επιθεώρηση κεφαλίδων· προαιρετικό hash ολόκληρου αρχείου; [GGUF / safetensors](../model-artifacts.md) |
|
||||
| Μεταδεδομένα IL2CPP | Εικόνες assemblies Unity global-metadata.dat, ονόματα τύπων/μεθόδων και εύρη δεικτών | Στατική αποκωδικοποίηση εκδόσεων 24, 27, 29, 31, 39· χωρίς Unity |
|
||||
| Godot PCK | Απογραφή και εξαγωγή αρχείου, αρχικές κεφαλίδες/κατάλογοι και ταυτότητες επιλεγμένου περιεχομένου | Στατικές μορφές 2, 3, 4· χωρίς Godot |
|
||||
| Υπογραφές APK | Υπογράφοντες v1/v2/v3/v3.1, αποτυπώματα πιστοποιητικών, εναλλαγή κλειδιών και εσωτερική συνέπεια digests | Ενσωματωμένη στατική επιθεώρηση; `inspect_apk_signature` |
|
||||
|
||||
Για εξέταση δυαδικών αρχείων με κώδικα μηχανής χωρίς εκτέλεση, το REA υποστηρίζει
|
||||
επίσης GNU objdump και Rizin που παρέχει ο χρήστης. Δες τον
|
||||
@@ -186,7 +201,12 @@ npx rea-agents@latest setup
|
||||
|
||||
## Μελέτες περίπτωσης
|
||||
|
||||
[](https://rea.tools/showcase/)
|
||||
<a href="https://rea.tools/showcase/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="../assets/rea-showcases-dark.svg" />
|
||||
<img alt="Εικονογραφήσεις των μελετών περίπτωσης για τη στερεοφωνική τοποθέτηση ήχου στο DX-Ball, τη γέφυρα προχείρου του Notion και τον δακτύλιο βλημάτων του TH04" src="../assets/rea-showcases.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
### DX-Ball: ανακατασκευή υπολογισμού στερεοφωνικής τοποθέτησης ήχου
|
||||
|
||||
@@ -279,8 +299,6 @@ npx rea-agents@latest setup
|
||||
|
||||
- [Εγκατάσταση και ρύθμιση](../installation.md): καταχώριση agents, ρύθμιση παρόχων ανάλυσης, ενημερώσεις και απεγκατάσταση.
|
||||
- [Έλεγχος ετοιμότητας και αντιμετώπιση προβλημάτων](../installation.md#check-readiness-for-your-task): διάγνωση ενός agent ή μιας μηχανής ανάλυσης.
|
||||
- [CUDA / HIP](../gpu-code-objects.md)
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI και Evidence](../cli.md): εντολές, επιλογή παρόχου, στιγμιότυπα, εισαγωγή/εξαγωγή και κωδικοί εξόδου.
|
||||
- [Προδιαγραφές MCP](../mcp-contracts.md) και [prompts για agents](../mcp-prompts.md): αποτελέσματα εργαλείων, συνεδρίες και καθοδηγούμενες διερευνήσεις.
|
||||
- [Κατάλογος εργαλείων](../mcp-contracts.md#generated-catalog): αυτόματα παραγόμενος κατάλογος εργαλείων, παρόχων και εντολών CLI.
|
||||
|
||||
+59
-32
@@ -1,7 +1,7 @@
|
||||
<div align="center">
|
||||
|
||||
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · **Español** · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
|
||||
|
||||
# REA: Ingeniería inversa de cualquier cosa
|
||||
|
||||
@@ -60,12 +60,14 @@
|
||||
|
||||
REA conecta tu agente con herramientas para inspeccionar binarios nativos, aplicaciones JavaScript y Electron, ensamblados .NET y sitios web. También puedes usar las mismas herramientas desde la terminal. El análisis se ejecuta localmente y los resultados incluyen las pruebas y limitaciones que sustentan cada conclusión.
|
||||
|
||||
La configuración registra REA en tu agente e instala las instrucciones de trabajo correspondientes. El análisis nativo puede usar una instalación existente de Hopper o Ghidra; la configuración también puede instalar Hopper, si lo autorizas. El análisis estático de JavaScript no necesita ninguno de los dos motores.
|
||||
La configuración registra REA con tu agente e instala las instrucciones de trabajo correspondientes. El análisis nativo puede usar Hopper, Ghidra o IDA ya instalados; la configuración puede instalar Hopper con tu aprobación. El análisis estático de JavaScript no requiere un motor de análisis nativo.
|
||||
|
||||
> **[Visita el sitio web de REA](https://rea.tools/es/)** para encontrar instrucciones de configuración, guías ilustradas y casos reales.
|
||||
|
||||
## Inicio rápido
|
||||
|
||||
En Linux, iniciar Ghidra, JADX y herramientas de firmware, y analizar JavaScript/Electron estáticamente, requiere un `ps` compatible con procps en el `PATH` de REA para comprobar la propiedad de los procesos. Consulta el requisito de Linux.
|
||||
|
||||
### Configura tu agente
|
||||
|
||||
Con Node.js y npm instalados, ejecuta:
|
||||
@@ -78,6 +80,8 @@ Elige tus agentes, revisa los cambios propuestos y apruébalos. La configuració
|
||||
|
||||
La configuración admite Claude Code, Codex, Cursor, Gemini CLI, Grok Build y [otros agentes](../installation.md#supported-agents). Consulta [instalación y configuración](../installation.md) para configurar proveedores y registrar MCP manualmente.
|
||||
|
||||
MCP usa esquemas de entrada compactos por defecto. Para ver todas las instrucciones de campos y la validación anidada, establece `REA_MCP_INPUT_SCHEMA_PROFILE=full` en el registro MCP. Ambos perfiles conservan todas las herramientas y la misma validación canónica del servidor; consulta la guía de contratos MCP.
|
||||
|
||||
### Pregunta a tu agente
|
||||
|
||||
```text
|
||||
@@ -109,12 +113,16 @@ Para el análisis nativo, configura primero un proveedor. Consulta la [guía de
|
||||
|
||||
REA cambia rápidamente y las nuevas versiones incluyen correcciones frecuentes. Mantén tu instalación actualizada.
|
||||
|
||||
Para una CLI instalada mediante npm:
|
||||
Para una CLI instalada mediante `npm install --global` / `bun add --global`:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
`rea --update` es un alias de `rea update` con las mismas opciones globales de salida. Los comandos de actualización conservan como máximo 4 MiB de stdout/stderr combinados. Los metadatos demasiado grandes se rechazan al terminar; las instalaciones detalladas conservan diagnósticos acotados. El progreso respeta la contrapresión del terminal y espera las escrituras enviadas.
|
||||
|
||||
Las actualizaciones globales con Bun requieren Bun 1.2.17 o posterior. Actualiza manualmente las versiones anteriores antes de ejecutar `rea update`.
|
||||
|
||||
Para actualizar los registros de tus agentes y la skill, ejecuta el comando de configuración que muestra la actualización.
|
||||
|
||||
Si usas `npx`, actualiza la configuración de tu agente con:
|
||||
@@ -125,6 +133,8 @@ npx rea-agents@latest setup
|
||||
|
||||
Revisa los cambios de configuración y reinicia tu agente. Para ejecutar comandos de CLI puntuales, usa `npx rea-agents@latest` seguido del comando.
|
||||
|
||||
Con Bun, usa `bunx rea-agents@latest` en lugar de `npx rea-agents@latest`.
|
||||
|
||||
## Cómo funciona REA
|
||||
|
||||
Tu agente llama a REA mediante MCP para inspeccionar el objetivo y seguir el código relevante. REA devuelve los hallazgos junto con sus pruebas. El agente los utiliza para hacer preguntas de seguimiento, explicar el comportamiento o escribir y probar una implementación. Los comandos de CLI usan los mismos flujos de trabajo.
|
||||
@@ -137,43 +147,62 @@ Tu agente llama a REA mediante MCP para inspeccionar el objetivo y seguir el có
|
||||
|
||||
## Qué puedes analizar
|
||||
|
||||
REA requiere Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, además de npm. Las herramientas adicionales y los sistemas anfitriones compatibles dependen del objetivo:
|
||||
REA requiere Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) o 26+ (>=26.12.1), además de npm. Las herramientas adicionales y los sistemas anfitriones compatibles dependen del objetivo:
|
||||
|
||||
| Objetivo | Qué devuelve REA | Requisitos y guía |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Binarios nativos | Pseudocódigo, ensamblador, cadenas, símbolos, llamadas y referencias | Hopper, Ghidra o IDA; [análisis nativo](../native-investigation.md) |
|
||||
| Estructura ELF sin ejecutar | Secciones, segmentos, símbolos/reubicaciones originales y posibles medidas de mitigación detectadas mediante análisis estático | pwntools proporcionado por quien llama, en Linux x64; [diagnóstico de binarios](../binary-diagnostics.md) |
|
||||
| Bytecode EVM | Selectores de despacho, desplazamientos en bytes, argumentos inferidos y mutabilidad | Entrada local con bytes sin procesar o representación hexadecimal, en Linux x64; [guía de EVM sin ejecutar](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Fallos de Linux registrados | Registros note sin procesar, registros/señales de cada hilo registrado y posibles correspondencias de memoria opcionales | pwntools proporcionado por quien llama, en Linux x64; GDB/pwndbg opcionales; [fallos registrados](../recorded-crashes.md) |
|
||||
| Instalaciones de escritorio | Posibles entornos de ejecución, archivos de evidencia, componentes y siguientes pasos | Inspección acotada de directorios y cabeceras;[Aplicaciones de escritorio](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Módulos, importaciones, mapas de código fuente, rutas, IPC y relaciones con complementos nativos | Node.js y npm; [análisis de aplicaciones](../javascript-application-workflows.md) |
|
||||
| Sitios web | Estructura de la página, scripts, observaciones de red y capturas de pantalla solicitadas | Un navegador de la familia Chrome; [análisis del navegador](../browser-observation.md) |
|
||||
| Capturas de red guardadas | Solicitudes, respuestas, contenido accesible de las cargas útiles y ubicaciones de origen | HAR; mitmdump en Linux para capturas en formato nativo de mitmproxy; [guía de capturas](../web-network-captures.md) |
|
||||
| Capturas de paquetes | Endpoints, DNS, TLS SNI, distribución de protocolos y solicitudes HTTP | tshark y capinfos suministrados por el autor de la llamada; [guía pcap](../pcap-analysis.md) |
|
||||
| Ensamblados .NET | Metadatos, instrucciones CIL, dependencias nativas declaradas y comparaciones de compilaciones | Inspección estática; [guía de código administrado](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK de Android | Declaraciones del manifiesto, clases, métodos descompilados y referencias | JADX sin interfaz gráfica y un JDK completo en Linux/macOS/Windows x64; [guía de Android](../android-analysis.md) |
|
||||
| Dispositivos Android | Dispositivos, paquetes, procesos, registros, volcados, capturas de pantalla y transferencias de APK/archivos | adb suministrado por el autor de la llamada (emulador o dispositivo); [guía de ADB](../adb-device-analysis.md) |
|
||||
| Proyectos JEB | Unidades de proyecto del motor, resúmenes de artefactos y pseudocódigo de tipos/métodos | Un cliente JEB iniciado por el autor de la llamada que sirve MCP, seleccionado con REA_JEB_MCP_URL; [guía de JEB](../jeb-analysis.md) |
|
||||
| Recursos de Android | Manifiesto decodificado, tablas de cadenas, idiomas y datos de versión | Lanzador de Apktool suministrado por el autor de la llamada; [guía de recursos](../apktool-resource-analysis.md) |
|
||||
| Aplicaciones Flutter | Hash de snapshot de Dart, resúmenes de bibliotecas por ABI y procedencia de compilación del motor | Ninguna (análisis APK estático); [guía de Flutter](../flutter-analysis.md) |
|
||||
| Firmware | Regiones, resultados de extracción y derivaciones al análisis nativo | Binwalk / Unblob en Linux; [guía de firmware](../firmware-analysis.md) |
|
||||
| Paquetes grabados | Campos seleccionados, valores repetidos, filtros y reglas de decodificación | Linux: TShark 4.x + prlimit; [guía de capturas](../packet-captures.md) |
|
||||
| Señales lógicas grabadas | Anotaciones del decodificador, rangos de muestras y diagnósticos | Linux: sigrok-cli 0.7.2 + prlimit; [guía de señales](../signal-recordings.md) |
|
||||
| Paquetes y recursos | Inventarios de archivos, resúmenes criptográficos, plists, estructura de paquetes de Apple y recursos extraídos | [Guía de artefactos y JavaScript](../javascript-artifact-reconstruction.md), [aplicaciones de Apple](../apple-application-analysis.md) |
|
||||
| Comportamiento de procesos | Salida de la terminal, interacciones, observaciones de salida y del sistema de archivos, y comparaciones entre ejecuciones | Linux/macOS con una PTY nativa; [captura de procesos](../process-capture.md) |
|
||||
| Objetivo | Qué devuelve REA | Requisitos y guía |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Binarios nativos | Pseudocódigo, ensamblador, cadenas, símbolos, llamadas y referencias | Hopper, Ghidra o IDA; [análisis nativo](../native-investigation.md) |
|
||||
| Estructura ELF sin ejecutar | Secciones, segmentos, símbolos/reubicaciones originales y posibles medidas de mitigación detectadas mediante análisis estático | pwntools proporcionado por quien llama, en Linux x64; [diagnóstico de binarios](../binary-diagnostics.md) |
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| Bytecode EVM | Selectores de despacho, desplazamientos en bytes, argumentos inferidos y mutabilidad | Entrada local con bytes sin procesar o representación hexadecimal, en Linux x64; [guía de EVM sin ejecutar](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Fallos de Linux registrados | Registros note sin procesar, registros/señales de cada hilo registrado y posibles correspondencias de memoria opcionales | pwntools proporcionado por quien llama, en Linux x64; GDB/pwndbg opcionales; [fallos registrados](../recorded-crashes.md) |
|
||||
| Instalaciones de escritorio | Posibles entornos de ejecución, archivos de evidencia, componentes y siguientes pasos | Inspección acotada de directorios y cabeceras;[Aplicaciones de escritorio](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Módulos, importaciones, mapas de código fuente, rutas, IPC y relaciones con complementos nativos | Node.js y npm; [análisis de aplicaciones](../javascript-application-workflows.md); Linux: procps `ps` |
|
||||
| Sitios web | Estructura de la página, scripts, observaciones de red y capturas de pantalla solicitadas | Un navegador de la familia Chrome; [análisis del navegador](../browser-observation.md) |
|
||||
| Capturas de red guardadas | Solicitudes, respuestas, contenido accesible de las cargas útiles y ubicaciones de origen | HAR / Fiddler Everywhere SAZ; mitmdump en Linux para capturas en formato nativo de mitmproxy; [guía de capturas](../web-network-captures.md) |
|
||||
| Capturas de paquetes | Endpoints, DNS, TLS SNI, distribución de protocolos y solicitudes HTTP | tshark y capinfos suministrados por el autor de la llamada; [guía pcap](../pcap-analysis.md) |
|
||||
| Ensamblados .NET | Metadatos, instrucciones CIL, dependencias nativas declaradas y comparaciones de compilaciones | Inspección estática; [guía de código administrado](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK de Android | Declaraciones del manifiesto, clases, métodos descompilados y referencias | JADX sin interfaz gráfica y un JDK completo en Linux/macOS/Windows x64; [guía de Android](../android-analysis.md) |
|
||||
| Dispositivos Android | Dispositivos, paquetes, procesos, registros, volcados, capturas de pantalla y transferencias de APK/archivos | adb suministrado por el autor de la llamada (emulador o dispositivo); [guía de ADB](../adb-device-analysis.md) |
|
||||
| Proyectos JEB | Unidades de proyecto del motor, resúmenes de artefactos y pseudocódigo de tipos/métodos | Un cliente JEB iniciado por el autor de la llamada que sirve MCP, seleccionado con REA_JEB_MCP_URL; [guía de JEB](../jeb-analysis.md) |
|
||||
| Recursos de Android | Manifiesto decodificado, tablas de cadenas, idiomas y datos de versión | Lanzador de Apktool suministrado por el autor de la llamada; [guía de recursos](../apktool-resource-analysis.md) |
|
||||
| Aplicaciones Flutter | Hash de snapshot de Dart, resúmenes de bibliotecas por ABI y procedencia de compilación del motor | Ninguna (análisis APK estático); [guía de Flutter](../flutter-analysis.md) |
|
||||
| Firmware | Regiones, resultados de extracción y derivaciones al análisis nativo | Binwalk / Unblob en Linux; [guía de firmware](../firmware-analysis.md) |
|
||||
| Paquetes grabados | Campos seleccionados, valores repetidos, filtros y reglas de decodificación | Linux: TShark 4.x + prlimit; [guía de capturas](../packet-captures.md) |
|
||||
| Señales lógicas grabadas | Anotaciones del decodificador, rangos de muestras y diagnósticos | Linux: sigrok-cli 0.7.2 + prlimit; [guía de señales](../signal-recordings.md) |
|
||||
| Paquetes y recursos | Inventarios de archivos, resúmenes criptográficos, plists, estructura de paquetes de Apple y recursos extraídos | [Guía de artefactos y JavaScript](../javascript-artifact-reconstruction.md), [aplicaciones de Apple](../apple-application-analysis.md) |
|
||||
| Comportamiento de procesos | Salida de la terminal, interacciones, observaciones de salida y del sistema de archivos, y comparaciones entre ejecuciones | Linux/macOS con una PTY nativa; [captura de procesos](../process-capture.md) |
|
||||
| Binarios Go | Versión del compilador integrada, módulos, reemplazos y ajustes de compilación | Inspección estática integrada ELF/PE/thin Mach-O; No requiere instalar Go |
|
||||
| Bases de datos SQLite | Tablas, columnas, índices, vistas, disparadores y filas seleccionadas de tablas ordinarias | Instantánea local de base/WAL; Node.js 24.x >=24.15 o 26+ con límites nativos de SQLite |
|
||||
| Objetos de código GPU | Destinos CUDA/HIP, parámetros y recursos de kernels seleccionados, texto PTX conservado | Inspección estática integrada ELF/PE/fatbin/CUBIN/PTX; [GPU](../gpu-code-objects.md) |
|
||||
| Archivos de modelos | Metadatos GGUF / safetensors, declaraciones de tensores y totales derivados | Inspección de cabeceras; hash completo opcional; [GGUF / safetensors](../model-artifacts.md) |
|
||||
| Metadatos IL2CPP | Imágenes de ensamblado Unity global-metadata.dat, nombres de tipos/métodos y rangos de índices | Decodificación estática de versiones 24, 27, 29, 31, 39; sin Unity |
|
||||
| Godot PCK | Inventario y extracción, cabeceras/directorios originales e identidad del contenido seleccionado | Formatos estáticos 2, 3, 4; sin Godot |
|
||||
| Artefactos WASM | Validación, secciones, formas WAT exactas de importación/exportación y WAT decodificado | Aportado por quien llama WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| Firmas APK | Firmantes v1/v2/v3/v3.1, huellas de certificados, rotación de claves y coherencia interna de resúmenes | Inspección estática integrada; `inspect_apk_signature` |
|
||||
|
||||
La inspección estática de JavaScript y .NET lee los archivos suministrados sin ejecutar la aplicación. La captura en ejecución inicia el objetivo seleccionado o interactúa con él con los permisos de tu usuario; cada guía describe sus efectos.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Para binarios grandes, aumenta el plazo de inicio mediante `REA_GHIDRA_STARTUP_TIMEOUT_MS` o `REA_HOPPER_STARTUP_TIMEOUT_MS`.
|
||||
|
||||
Los formatos nativos y los sistemas anfitriones compatibles varían según el proveedor. Consulta [configuración de Hopper y Ghidra](../installation.md#hopper), la [guía de IDA](../ida-provider.md), [Kuna](../kuna-provider.md) y la [compatibilidad experimental de Ghidra en Windows](../windows-ghidra-p0.md). Ghidra también admite [análisis de DOS de 16 bits](../ghidra-dos.md). Para elegir un proveedor, consulta la [guía de CLI](../cli.md#choose-a-provider). Comprueba la [disponibilidad por versión](../installation.md#released-package-and-main) de las funciones añadidas después de la última versión publicada en npm.
|
||||
|
||||
## Casos prácticos
|
||||
|
||||
[](https://rea.tools/es/showcase/)
|
||||
<a href="https://rea.tools/es/showcase/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="../assets/rea-showcases-dark.svg" />
|
||||
<img alt="Ilustraciones de los casos de panoramización de sonido de DX-Ball, el puente del portapapeles de Notion y el anillo de proyectiles de TH04" src="../assets/rea-showcases.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
### DX-Ball: reconstruir un cálculo de panoramización de sonido
|
||||
|
||||
@@ -266,8 +295,6 @@ Empieza con las [guías prácticas](https://rea.tools/es/guides/) del sitio web.
|
||||
|
||||
- [Instalación y configuración](../installation.md): registro de agentes, configuración de proveedores, actualizaciones y desinstalación.
|
||||
- [Preparación y resolución de problemas](../installation.md#check-readiness-for-your-task): diagnóstico de un agente o motor de análisis concreto.
|
||||
- [CUDA / HIP](../gpu-code-objects.md)
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI y Evidence](../cli.md): comandos, selección de proveedores, instantáneas, importación/exportación y estados de salida.
|
||||
- [Contratos de MCP](../mcp-contracts.md) y [prompts para agentes](../mcp-prompts.md): resultados de herramientas, sesiones e investigaciones guiadas.
|
||||
- [Catálogo de herramientas](../mcp-contracts.md#generated-catalog): inventario de herramientas, proveedores y comandos de CLI generado durante la compilación.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div align="center" dir="rtl">
|
||||
|
||||
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · **فارسی** · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
|
||||
|
||||
# REA: مهندسی معکوس هر چیزی
|
||||
|
||||
@@ -52,12 +52,14 @@
|
||||
|
||||
REA ایجنت شما را به ابزارهایی برای بررسی باینریهای بومی، برنامههای JavaScript و Electron، اسمبلیهای .NET و وبسایتها متصل میکند. همین ابزارها از ترمینال نیز قابل استفادهاند. تحلیل بهصورت محلی انجام میشود و نتایج شامل شواهد و محدودیتهای پشت هر نتیجهگیری هستند.
|
||||
|
||||
فرایند راهاندازی، REA را در ایجنت شما ثبت میکند و دستورالعملهای گردشکار متناظر را نصب میکند. تحلیل بومی میتواند از نصب موجود Hopper یا Ghidra استفاده کند؛ همچنین راهاندازی میتواند با تأیید شما Hopper را بهصورت اختیاری نصب کند. تحلیل ایستای JavaScript به هیچیک از این موتورهای تحلیل نیاز ندارد.
|
||||
راهاندازی، REA را در ایجنت ثبت و دستورالعملهای متناظر را نصب میکند. تحلیل بومی میتواند از Hopper، Ghidra یا IDA موجود استفاده کند؛ نصب Hopper پس از تأیید شما ممکن است. تحلیل ایستای JavaScript به موتور تحلیل بومی نیاز ندارد.
|
||||
|
||||
> برای دستورالعملهای راهاندازی، راهنماهای تصویری و مطالعات موردی واقعی، **[به وبسایت REA مراجعه کنید](https://rea.tools/fa/)**.
|
||||
|
||||
## شروع سریع
|
||||
|
||||
در Linux، اجرای Ghidra، JADX و ابزارهای firmware و تحلیل ایستای JavaScript/Electron برای بررسی مالکیت فرایندها به `ps` سازگار با procps در `PATH` مربوط به REA نیاز دارد. راهکارها در پیشنیاز Linux آمدهاند.
|
||||
|
||||
### ایجنت خود را راهاندازی کنید
|
||||
|
||||
پس از نصب Node.js و npm، دستور زیر را اجرا کنید:
|
||||
@@ -70,6 +72,8 @@ npx rea-agents setup
|
||||
|
||||
راهاندازی از Claude Code، Codex، Cursor، Gemini CLI، Grok Build و [ایجنتهای دیگر](../installation.md#supported-agents) پشتیبانی میکند. برای پیکربندی ارائهدهنده و ثبت دستی MCP، [نصب و راهاندازی](../installation.md) را ببینید.
|
||||
|
||||
MCP بهطور پیشفرض از schema ورودی فشرده استفاده میکند. برای راهنمای کامل فیلدها و اعتبارسنجی تودرتو، در ثبت MCP مقدار `REA_MCP_INPUT_SCHEMA_PROFILE=full` را تنظیم کنید. هر دو حالت همهٔ ابزارها و اعتبارسنجی استاندارد یکسان سرور را نگه میدارند؛ راهنمای قراردادهای MCP را ببینید.
|
||||
|
||||
### از ایجنت خود بپرسید
|
||||
|
||||
```text
|
||||
@@ -101,12 +105,16 @@ rea --help
|
||||
|
||||
REA بهسرعت تغییر میکند و نسخههای جدید معمولاً شامل رفع اشکالهای متعددی هستند. نصب خود را بهروز نگه دارید.
|
||||
|
||||
برای CLI نصبشده از طریق npm:
|
||||
برای CLI نصبشده از طریق `npm install --global` / `bun add --global`:
|
||||
|
||||
```bash
|
||||
rea update
|
||||
```
|
||||
|
||||
`rea --update` نام مستعار `rea update` با همان گزینههای عمومی خروجی است. فرمانهای بهروزرسانی در مجموع حداکثر 4 MiB از stdout/stderr را نگه میدارند. پاسخ فرادادهٔ بزرگتر پس از پایان فرمان رد میشود؛ نصبهای پرخروجی تشخیصهای محدود دارند. نمایش پیشرفت فشار برگشتی ترمینال را رعایت و تا پایان نوشتنهای ارسالشده صبر میکند.
|
||||
|
||||
بهروزرسانی سراسری با Bun به Bun 1.2.17 یا جدیدتر نیاز دارد. پیش از اجرای `rea update` نسخههای قدیمیتر Bun را دستی ارتقا دهید.
|
||||
|
||||
برای بهروزرسانی ثبت ایجنتها و skill، دستور setup نمایشدادهشده توسط بهروزرسانی را اجرا کنید.
|
||||
|
||||
اگر از `npx` استفاده میکنید، راهاندازی ایجنت را با دستور زیر بهروز کنید:
|
||||
@@ -117,6 +125,8 @@ npx rea-agents@latest setup
|
||||
|
||||
تغییرات راهاندازی را بررسی کنید و ایجنت را دوباره اجرا کنید. برای اجرای یکبارهٔ دستورات CLI، از `npx rea-agents@latest` و سپس دستور موردنظر استفاده کنید.
|
||||
|
||||
با Bun، بهجای `npx rea-agents@latest` از `bunx rea-agents@latest` استفاده کنید.
|
||||
|
||||
## REA چگونه کار میکند؟
|
||||
|
||||
ایجنت شما از طریق MCP، REA را فراخوانی میکند تا هدف را بررسی و کد مرتبط را ردیابی کند. REA یافتهها را همراه با شواهد برمیگرداند. ایجنت از این نتایج برای طرح پرسشهای تکمیلی، توضیح رفتار یا نوشتن و آزمایش یک پیادهسازی استفاده میکند. دستورات CLI نیز از همین گردشکارها بهره میبرند.
|
||||
@@ -129,20 +139,24 @@ npx rea-agents@latest setup
|
||||
|
||||
## چه چیزهایی را میتوان تحلیل کرد؟
|
||||
|
||||
REA به Node.js 22.x (>=22.19)، 24.x (>=24.11) یا 26+، بههمراه npm نیاز دارد. ابزارهای اضافی و پشتیبانی سیستم میزبان به نوع هدف بستگی دارند:
|
||||
REA به Node.js 22.x (>=22.23.4)، 24.x (>=24.22.1) یا 26+ (>=26.12.1)، بههمراه npm نیاز دارد. ابزارهای اضافی و پشتیبانی سیستم میزبان به نوع هدف بستگی دارند:
|
||||
|
||||
| هدف | خروجی REA | پیشنیازها و راهنما |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| باینریهای بومی | شبهکد، اسمبلی، رشتهها، نمادها، فراخوانیها و ارجاعات | Hopper، Ghidra یا IDA؛ [تحلیل بومی](../native-investigation.md) |
|
||||
| ساختار ELF آفلاین | بخشها، سگمنتها، نمادها و relocationهای اصلی و گزینههای احتمالی کاهش آسیبپذیری ایستا | pwntools تأمینشده توسط فراخواننده روی Linux x64؛ [تشخیص باینری](../binary-diagnostics.md) |
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| بایتکد EVM | selectorهای dispatch، offsetهای بایتی، آرگومانهای استنباطشده و قابلیت تغییر وضعیت | دادهٔ خام/hex محلی روی Linux x64؛ [راهنمای EVM آفلاین](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| کرشهای ثبتشدهٔ لینوکس | یادداشتهای خام، رجیسترها/سیگنالهای تمام threadهای ثبتشده و گزینههای احتمالی نگاشت | pwntools تأمینشده توسط فراخواننده روی Linux x64؛ GDB/pwndbg اختیاری؛ [کرشهای ثبتشده](../recorded-crashes.md) |
|
||||
| نصبهای دسکتاپ | نامزدهای زمان اجرا، فایلهای شاهد، نشانههای مؤلفهها و مراحل بعدی | بررسی محدود پوشهها و سرآیندها;[برنامههای دسکتاپ](../desktop-applications.md) |
|
||||
| JavaScript / Electron | ماژولها، importها، source mapها، مسیرها، IPC و ارتباط افزونههای بومی | Node.js و npm؛ [تحلیل برنامه](../javascript-application-workflows.md) |
|
||||
| JavaScript / Electron | ماژولها، importها، source mapها، مسیرها، IPC و ارتباط افزونههای بومی | Node.js و npm؛ [تحلیل برنامه](../javascript-application-workflows.md); Linux: procps `ps` |
|
||||
| وبسایتها | ساختار صفحه، اسکریپتها، مشاهدات شبکه و اسکرینشاتهای درخواستی | مرورگر مبتنی بر Chrome؛ [تحلیل مرورگر](../browser-observation.md) |
|
||||
| دادههای ضبطشدهٔ شبکه | درخواستها، پاسخها، payloadهای آشکارشده و مکان آنها در منبع | HAR؛ ابزار mitmdump در لینوکس برای ضبطهای بومی mitmproxy؛ [راهنمای ضبط شبکه](../web-network-captures.md) |
|
||||
| دادههای ضبطشدهٔ شبکه | درخواستها، پاسخها، payloadهای آشکارشده و مکان آنها در منبع | HAR / Fiddler Everywhere SAZ؛ ابزار mitmdump در لینوکس برای ضبطهای بومی mitmproxy؛ [راهنمای ضبط شبکه](../web-network-captures.md) |
|
||||
| اسمبلیهای .NET | فراداده، دستورالعملهای CIL، وابستگیهای بومی اعلامشده و مقایسهٔ buildها | بررسی ایستا؛ [راهنمای کد مدیریتشده](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| فایلهای APK اندروید | تعاریف Manifest، کلاسها، متدهای دیکامپایلشده و ارجاعات | JADX بدون رابط گرافیکی و JDK کامل در Linux/macOS/Windows x64؛ [راهنمای اندروید](../android-analysis.md) |
|
||||
@@ -156,16 +170,31 @@ REA به Node.js 22.x (>=22.19)، 24.x (>=24.11) یا 26+، بههمراه np
|
||||
| سیگنالهای منطقی ضبطشده | توضیحات رمزگشا، بازههای نمونه و اطلاعات عیبیابی | Linux: sigrok-cli 0.7.2 + prlimit; [راهنمای سیگنالها](../signal-recordings.md) |
|
||||
| بستهها و منابع | فهرست فایلها، digestها، فایلهای plist، ساختار bundle اپل و منابع استخراجشده | [راهنمای آرتیفکت و JavaScript](../javascript-artifact-reconstruction.md)، [برنامههای اپل](../apple-application-analysis.md) |
|
||||
| رفتار فرایند | خروجی ترمینال، تعاملات، وضعیت خروج، مشاهدات سیستم فایل و مقایسهٔ اجراها | Linux/macOS با PTY بومی؛ [ضبط فرایند](../process-capture.md) |
|
||||
| باینریهای Go | نسخهٔ کامپایلر تعبیهشده، ماژولها، جایگزینیها و تنظیمات ساخت | بررسی ایستای داخلی ELF/PE/thin Mach-O; نصب Go لازم نیست |
|
||||
| پایگاههای SQLite | جدولها، ستونها، ایندکسها، نماها، تریگرها و سطرهای انتخابشدهٔ جدولهای عادی | snapshot محلی پایگاه/WAL؛ Node.js 24.x >=24.15 یا 26+ با محدودیتهای بومی SQLite |
|
||||
| اشیای کد GPU | هدفهای CUDA/HIP، پارامترها و منابع کرنلهای انتخابشده و متن PTX نگهداریشده | بررسی ایستای داخلی ELF/PE/fatbin/CUBIN/PTX; [GPU](../gpu-code-objects.md) |
|
||||
| فایلهای مدل | فرادادهٔ GGUF / safetensors، اعلان تانسورها و مجموعهای مشتقشده | بررسی هدر؛ هش کل فایل اختیاری است; [GGUF / safetensors](../model-artifacts.md) |
|
||||
| فرادادهٔ IL2CPP | تصاویر اسمبلی، نام نوعها/متدها و بازهٔ ایندکسها در Unity global-metadata.dat | رمزگشایی ایستای نسخههای 24، 27، 29، 31 و 39؛ بدون نیاز به Unity |
|
||||
| Godot PCK | فهرست و استخراج آرشیو، اطلاعات اصلی هدر/دایرکتوری و هویت محتوای انتخابشده | قالبهای ایستای 2، 3 و 4؛ بدون نیاز به Godot |
|
||||
| مصنوعات WASM | اعتبارسنجی، بخشها، فرم دقیق WAT ورودی/خروجی و WAT رمزگشاییشده | تهیهشده توسط کاربر WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| امضاهای APK | امضاکنندگان v1/v2/v3/v3.1، اثر انگشت گواهی، چرخش کلید و سازگاری داخلی digest | بررسی ایستای داخلی; `inspect_apk_signature` |
|
||||
|
||||
بررسی ایستای JavaScript و .NET فایلهای ارائهشده را بدون اجرای برنامه میخواند. ضبط زمان اجرا، هدف انتخابشده را با مجوزهای کاربری شما اجرا میکند یا با آن تعامل دارد؛ آثار این عملیات در راهنمای مربوط به هر نوع ضبط توضیح داده شدهاند.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
برای باینریهای بزرگ، مهلت شروع را با `REA_GHIDRA_STARTUP_TIMEOUT_MS` یا `REA_HOPPER_STARTUP_TIMEOUT_MS` افزایش دهید.
|
||||
|
||||
فرمتهای بومی و پشتیبانی سیستم میزبان بر اساس ارائهدهنده متفاوتاند. [راهاندازی Hopper و Ghidra](../installation.md#hopper)، [راهنمای IDA](../ida-provider.md)، [Kuna](../kuna-provider.md) و [پشتیبانی آزمایشی Ghidra در ویندوز](../windows-ghidra-p0.md) را ببینید. Ghidra همچنین از [تحلیل DOS شانزدهبیتی](../ghidra-dos.md) پشتیبانی میکند. برای انتخاب ارائهدهنده، به [راهنمای CLI](../cli.md#choose-a-provider) مراجعه کنید. برای قابلیتهایی که پس از آخرین انتشار npm اضافه شدهاند، [وضعیت انتشار](../installation.md#released-package-and-main) را بررسی کنید.
|
||||
|
||||
## نمونههای عملی
|
||||
|
||||
[](https://rea.tools/fa/showcase/)
|
||||
<a href="https://rea.tools/fa/showcase/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="../assets/rea-showcases-dark.svg" />
|
||||
<img alt="تصویرهایی از نمونههای پنینگ صدای DX-Ball، پل کلیپبورد Notion و حلقهٔ گلولههای TH04" src="../assets/rea-showcases.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
### DX-Ball: بازسازی محاسبهٔ موقعیت استریوی صدا
|
||||
|
||||
@@ -251,8 +280,6 @@ npx rea-agents@latest setup
|
||||
|
||||
- [نصب و راهاندازی](../installation.md): ثبت ایجنت، پیکربندی ارائهدهنده، بهروزرسانی و حذف نصب.
|
||||
- [آمادگی و رفع اشکال](../installation.md#check-readiness-for-your-task): عیبیابی یک ایجنت یا موتور تحلیل.
|
||||
- [CUDA / HIP](../gpu-code-objects.md)
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI و شواهد](../cli.md): دستورات، انتخاب ارائهدهنده، snapshotها، ورود/خروج داده و وضعیتهای خروج.
|
||||
- [قراردادهای MCP](../mcp-contracts.md) و [پرامپتهای ایجنت](../mcp-prompts.md): نتایج ابزارها، نشستها و بررسیهای هدایتشده.
|
||||
- [فهرست ابزارها](../mcp-contracts.md#generated-catalog): فهرست تولیدشده هنگام build از ابزارها، ارائهدهندگان و دستورات CLI.
|
||||
|
||||
+52
-32
@@ -1,7 +1,7 @@
|
||||
<div align="center">
|
||||
|
||||
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · **Français** · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
|
||||
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
|
||||
|
||||
# REA : rétro-ingénierie de tout
|
||||
|
||||
@@ -66,6 +66,8 @@ Setup enregistre REA auprès de votre agent et installe les instructions de work
|
||||
|
||||
## Démarrage rapide
|
||||
|
||||
Sous Linux, les lancements de Ghidra, JADX et des outils de firmware, ainsi que l’analyse statique JavaScript/Electron, nécessitent un `ps` compatible procps dans le `PATH` de REA pour vérifier la propriété des processus. Voir le prérequis Linux.
|
||||
|
||||
### Configurer votre agent
|
||||
|
||||
Une fois Node.js et npm installés, exécutez :
|
||||
@@ -78,6 +80,8 @@ Choisissez vos agents, vérifiez les modifications proposées et approuvez-les.
|
||||
|
||||
Setup prend en charge Claude Code, Codex, Cursor, Gemini CLI, Grok Build et [d'autres agents](../installation.md#supported-agents). Consultez [Installation et configuration](../installation.md) pour la configuration des fournisseurs et l'enregistrement MCP manuel.
|
||||
|
||||
MCP utilise par défaut des schémas d’entrée compacts. Pour les descriptions complètes des champs et de la validation imbriquée, définissez `REA_MCP_INPUT_SCHEMA_PROFILE=full` dans l’enregistrement MCP. Les deux profils conservent tous les outils et la même validation canonique du serveur ; voir le guide des contrats MCP.
|
||||
|
||||
### Interroger votre agent
|
||||
|
||||
```text
|
||||
@@ -116,6 +120,8 @@ Pour une CLI installée avec `npm install --global` ou `bun add --global` :
|
||||
rea update
|
||||
```
|
||||
|
||||
`rea --update` est un alias de `rea update` avec les mêmes options globales de sortie. Les commandes de mise à jour conservent au plus 4 MiB de stdout/stderr au total. Les réponses de métadonnées trop grandes sont refusées à la fin ; les installations détaillées gardent des diagnostics bornés. La progression respecte la contre-pression du terminal et attend la fin des écritures soumises.
|
||||
|
||||
Les mises à jour globales avec Bun nécessitent Bun 1.2.17 ou une version ultérieure. Avec une version plus ancienne de Bun, mettez à jour Bun manuellement avant d'exécuter `rea update`.
|
||||
|
||||
Pour actualiser les enregistrements de vos agents et le skill, exécutez la commande setup affichée par la mise à jour.
|
||||
@@ -140,44 +146,60 @@ Votre agent appelle REA via MCP pour inspecter la cible et suivre le code pertin
|
||||
|
||||
## Ce que vous pouvez analyser
|
||||
|
||||
REA nécessite Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, ainsi que npm. Les outils supplémentaires et les hôtes pris en charge dépendent de la cible :
|
||||
REA nécessite Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ou 26+ (>=26.12.1), ainsi que npm. Les outils supplémentaires et les hôtes pris en charge dépendent de la cible :
|
||||
|
||||
| Cible | Ce que REA renvoie | Prérequis et guide |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Binaires natifs | Pseudo-code, assembleur, chaînes, symboles, appels et références | Hopper, Ghidra ou IDA ; [analyse native](../native-investigation.md) |
|
||||
| Structure ELF hors ligne | Sections, segments, symboles et relocations d'origine, et protections statiques candidates | pwntools fourni par l'utilisateur sous Linux x64 ; [diagnostics binaires](../binary-diagnostics.md) |
|
||||
| Artefact WASM | Validation, sections, formes WAT exactes d'import/export et WAT décodé | WABT 1.0.42 fourni par l'utilisateur ; `inspect_wasm_artifact` |
|
||||
| Bytecode EVM | Sélecteurs de dispatch, offsets en octets, arguments inférés et mutabilité | Fichier local brut ou hexadécimal sous Linux x64 ; [guide EVM hors ligne](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Crashs Linux enregistrés | Notes brutes, registres et signaux de chaque thread enregistré, et correspondances de mappings facultatives | pwntools fourni par l'utilisateur sous Linux x64 ; GDB/pwndbg en option ; [crashs enregistrés](../recorded-crashes.md) |
|
||||
| Installations de bureau | Environnements candidats, fichiers justificatifs, composants et étapes suivantes | Inspection bornée des répertoires et en-têtes;[Applications de bureau](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC et relations avec les modules natifs | Node.js et npm ; [analyse d'applications](../javascript-application-workflows.md) |
|
||||
| Sites web | Structure des pages, scripts, observations réseau et captures d'écran demandées | Un navigateur de la famille Chrome ; [analyse du navigateur](../browser-observation.md) |
|
||||
| Captures réseau enregistrées | Requêtes, réponses, payloads exposés et emplacements dans le code source | HAR ; mitmdump sous Linux pour les captures mitmproxy natives ; [guide des captures](../web-network-captures.md) |
|
||||
| Captures de paquets | Points de terminaison, DNS, TLS SNI, distribution des protocoles et requêtes HTTP | tshark et capinfos fournis par l'appelant ; [guide pcap](../pcap-analysis.md) |
|
||||
| Assemblys .NET | Métadonnées, instructions CIL, dépendances natives déclarées et comparaisons de builds | Inspection statique ; [guide du code managé](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK Android | Déclarations du manifeste, classes, méthodes décompilées et références | JADX headless et un JDK complet sous Linux/macOS/Windows x64 ; [guide Android](../android-analysis.md) |
|
||||
| Appareils Android | Appareils, paquets, processus, journaux, dumps, captures d'écran et transferts d'APK/fichiers | adb fourni par l'appelant (émulateur ou appareil) ; [guide ADB](../adb-device-analysis.md) |
|
||||
| Projets JEB | Unités de projet fournies par le moteur, condensés d'artefacts et pseudo-code de types/méthodes | Un client JEB démarré par l'appelant servant MCP, sélectionné via REA_JEB_MCP_URL ; [guide JEB](../jeb-analysis.md) |
|
||||
| Ressources Android | Manifeste décodé, tables de chaînes, langues et faits de version | Lanceur Apktool fourni par l'appelant ; [guide des ressources](../apktool-resource-analysis.md) |
|
||||
| Applications Flutter | Empreinte de snapshot Dart, condensés de bibliothèques par ABI et provenance de build du moteur | Aucune (analyse APK statique) ; [guide Flutter](../flutter-analysis.md) |
|
||||
| Firmware | Régions, résultats d'extraction et passage de relais vers l'analyse native | Binwalk / Unblob sous Linux ; [guide firmware](../firmware-analysis.md) |
|
||||
| Paquets enregistrés | Champs sélectionnés, valeurs répétées, filtres et règles de décodage | Linux: TShark 4.x + prlimit; [guide des captures](../packet-captures.md) |
|
||||
| Signaux logiques enregistrés | Annotations du décodeur, plages d’échantillons et diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [guide des signaux](../signal-recordings.md) |
|
||||
| Paquets et ressources | Inventaires de fichiers, empreintes, plists, anatomie des bundles Apple et ressources extraites | [Guide artefacts et JavaScript](../javascript-artifact-reconstruction.md), [applications Apple](../apple-application-analysis.md) |
|
||||
| Comportement des processus | Sortie du terminal, interactions, code de sortie, observations du système de fichiers et comparaisons | Linux/macOS avec un PTY natif ; [capture de processus](../process-capture.md) |
|
||||
| Cible | Ce que REA renvoie | Prérequis et guide |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Binaires natifs | Pseudo-code, assembleur, chaînes, symboles, appels et références | Hopper, Ghidra ou IDA ; [analyse native](../native-investigation.md) |
|
||||
| Structure ELF hors ligne | Sections, segments, symboles et relocations d'origine, et protections statiques candidates | pwntools fourni par l'utilisateur sous Linux x64 ; [diagnostics binaires](../binary-diagnostics.md) |
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| Artefact WASM | Validation, sections, formes WAT exactes d'import/export et WAT décodé | WABT 1.0.42 fourni par l'utilisateur ; `inspect_wasm_artifact` |
|
||||
| Bytecode EVM | Sélecteurs de dispatch, offsets en octets, arguments inférés et mutabilité | Fichier local brut ou hexadécimal sous Linux x64 ; [guide EVM hors ligne](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Crashs Linux enregistrés | Notes brutes, registres et signaux de chaque thread enregistré, et correspondances de mappings facultatives | pwntools fourni par l'utilisateur sous Linux x64 ; GDB/pwndbg en option ; [crashs enregistrés](../recorded-crashes.md) |
|
||||
| Installations de bureau | Environnements candidats, fichiers justificatifs, composants et étapes suivantes | Inspection bornée des répertoires et en-têtes;[Applications de bureau](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC et relations avec les modules natifs | Node.js et npm ; [analyse d'applications](../javascript-application-workflows.md); Linux: procps `ps` |
|
||||
| Sites web | Structure des pages, scripts, observations réseau et captures d'écran demandées | Un navigateur de la famille Chrome ; [analyse du navigateur](../browser-observation.md) |
|
||||
| Captures réseau enregistrées | Requêtes, réponses, payloads exposés et emplacements dans le code source | HAR / Fiddler Everywhere SAZ ; mitmdump sous Linux pour les captures mitmproxy natives ; [guide des captures](../web-network-captures.md) |
|
||||
| Captures de paquets | Points de terminaison, DNS, TLS SNI, distribution des protocoles et requêtes HTTP | tshark et capinfos fournis par l'appelant ; [guide pcap](../pcap-analysis.md) |
|
||||
| Assemblys .NET | Métadonnées, instructions CIL, dépendances natives déclarées et comparaisons de builds | Inspection statique ; [guide du code managé](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK Android | Déclarations du manifeste, classes, méthodes décompilées et références | JADX headless et un JDK complet sous Linux/macOS/Windows x64 ; [guide Android](../android-analysis.md) |
|
||||
| Appareils Android | Appareils, paquets, processus, journaux, dumps, captures d'écran et transferts d'APK/fichiers | adb fourni par l'appelant (émulateur ou appareil) ; [guide ADB](../adb-device-analysis.md) |
|
||||
| Projets JEB | Unités de projet fournies par le moteur, condensés d'artefacts et pseudo-code de types/méthodes | Un client JEB démarré par l'appelant servant MCP, sélectionné via REA_JEB_MCP_URL ; [guide JEB](../jeb-analysis.md) |
|
||||
| Ressources Android | Manifeste décodé, tables de chaînes, langues et faits de version | Lanceur Apktool fourni par l'appelant ; [guide des ressources](../apktool-resource-analysis.md) |
|
||||
| Applications Flutter | Empreinte de snapshot Dart, condensés de bibliothèques par ABI et provenance de build du moteur | Aucune (analyse APK statique) ; [guide Flutter](../flutter-analysis.md) |
|
||||
| Firmware | Régions, résultats d'extraction et passage de relais vers l'analyse native | Binwalk / Unblob sous Linux ; [guide firmware](../firmware-analysis.md) |
|
||||
| Paquets enregistrés | Champs sélectionnés, valeurs répétées, filtres et règles de décodage | Linux: TShark 4.x + prlimit; [guide des captures](../packet-captures.md) |
|
||||
| Signaux logiques enregistrés | Annotations du décodeur, plages d’échantillons et diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [guide des signaux](../signal-recordings.md) |
|
||||
| Paquets et ressources | Inventaires de fichiers, empreintes, plists, anatomie des bundles Apple et ressources extraites | [Guide artefacts et JavaScript](../javascript-artifact-reconstruction.md), [applications Apple](../apple-application-analysis.md) |
|
||||
| Comportement des processus | Sortie du terminal, interactions, code de sortie, observations du système de fichiers et comparaisons | Linux/macOS avec un PTY natif ; [capture de processus](../process-capture.md) |
|
||||
| Binaires Go | Version du compilateur embarquée, modules, remplacements et paramètres de compilation | Inspection statique intégrée ELF/PE/thin Mach-O; Aucune installation de Go nécessaire |
|
||||
| Bases SQLite | Tables, colonnes, index, vues, déclencheurs et lignes sélectionnées de tables ordinaires | Instantané local base/WAL ; Node.js 24.x >=24.15 ou 26+ avec limites SQLite natives |
|
||||
| Objets de code GPU | Cibles CUDA/HIP, paramètres et ressources de noyaux sélectionnés, texte PTX conservé | Inspection statique intégrée ELF/PE/fatbin/CUBIN/PTX; [GPU](../gpu-code-objects.md) |
|
||||
| Fichiers de modèles | Métadonnées GGUF / safetensors, déclarations de tenseurs et totaux dérivés | Lecture des en-têtes ; empreinte complète facultative; [GGUF / safetensors](../model-artifacts.md) |
|
||||
| Métadonnées IL2CPP | Images d’assemblages Unity global-metadata.dat, noms de types/méthodes et plages d’index | Décodage statique des versions 24, 27, 29, 31, 39 ; sans Unity |
|
||||
| Godot PCK | Inventaire et extraction d’archive, en-têtes/répertoires originaux et identités des contenus sélectionnés | Formats statiques 2, 3, 4 ; sans Godot |
|
||||
| Signatures APK | Signataires v1/v2/v3/v3.1, empreintes de certificats, rotation des clés et cohérence interne des condensats | Inspection statique intégrée; `inspect_apk_signature` |
|
||||
|
||||
L'inspection statique JavaScript et .NET lit les fichiers fournis sans exécuter l'application. La capture à l'exécution lance la cible choisie ou interagit avec elle avec vos permissions utilisateur ; chaque guide d'exécution décrit ses effets.
|
||||
|
||||
<a id="choosing-a-deep-analysis-provider"></a>
|
||||
|
||||
Les formats natifs et les hôtes pris en charge varient selon le fournisseur. Consultez [la configuration de Hopper et Ghidra](../installation.md#hopper), le [guide IDA](../ida-provider.md), [Kuna](../kuna-provider.md) et [la prise en charge expérimentale de Ghidra sous Windows](../windows-ghidra-p0.md). Ghidra prend aussi en charge [l'analyse DOS 16 bits](../ghidra-dos.md). Pour les gros binaires, augmentez son délai de démarrage avec `REA_GHIDRA_STARTUP_TIMEOUT_MS`. Pour le choix du fournisseur, consultez le [guide CLI](../cli.md#choose-a-provider). Vérifiez la [disponibilité dans les versions publiées](../installation.md#released-package-and-main) pour les fonctionnalités ajoutées depuis la dernière version npm.
|
||||
Les formats natifs et les hôtes pris en charge varient selon le fournisseur. Consultez [la configuration de Hopper et Ghidra](../installation.md#hopper), le [guide IDA](../ida-provider.md), [Kuna](../kuna-provider.md) et [la prise en charge expérimentale de Ghidra sous Windows](../windows-ghidra-p0.md). Ghidra prend aussi en charge [l'analyse DOS 16 bits](../ghidra-dos.md). Pour les gros binaires, augmentez son délai de démarrage avec `REA_GHIDRA_STARTUP_TIMEOUT_MS` / `REA_HOPPER_STARTUP_TIMEOUT_MS`. Pour le choix du fournisseur, consultez le [guide CLI](../cli.md#choose-a-provider). Vérifiez la [disponibilité dans les versions publiées](../installation.md#released-package-and-main) pour les fonctionnalités ajoutées depuis la dernière version npm.
|
||||
|
||||
## Études de cas
|
||||
|
||||
[](https://rea.tools/fr/showcase/)
|
||||
<a href="https://rea.tools/fr/showcase/">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="../assets/rea-showcases-dark.svg" />
|
||||
<img alt="Illustrations des études sur la spatialisation sonore de DX-Ball, le pont du presse-papiers de Notion et le cercle de projectiles de TH04" src="../assets/rea-showcases.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
### DX-Ball : reconstruire un calcul de panoramique sonore
|
||||
|
||||
@@ -270,8 +292,6 @@ Commencez par les [guides pas à pas](https://rea.tools/fr/guides/) du site web.
|
||||
|
||||
- [Installation et configuration](../installation.md) : enregistrement des agents, configuration des fournisseurs, mises à jour et désinstallation.
|
||||
- [Vérification et dépannage](../installation.md#check-readiness-for-your-task) : diagnostiquer un agent ou un moteur d'analyse donné.
|
||||
- [CUDA / HIP](../gpu-code-objects.md)
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI et Evidence](../cli.md) : commandes, choix du fournisseur, snapshots, import/export et codes de sortie.
|
||||
- [Contrats MCP](../mcp-contracts.md) et [prompts pour agents](../mcp-prompts.md) : résultats des outils, sessions et investigations guidées.
|
||||
- [Catalogue des outils](../mcp-contracts.md#generated-catalog) : inventaire des outils, fournisseurs et commandes CLI, généré lors du build.
|
||||
|
||||
Loaded 100 of 3001 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user