Compare commits
607
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b33236ec08 | ||
|
|
fc58fa1eb0 | ||
|
|
49887f0239 | ||
|
|
4a6073810a | ||
|
|
fc4a9a0592 | ||
|
|
abd7a5ed09 | ||
|
|
58381eefa2 | ||
|
|
ebb065e2dc | ||
|
|
2cedde9f1c | ||
|
|
248972f327 | ||
|
|
5047373006 | ||
|
|
7dc40d3d9e | ||
|
|
93aa9eb5c2 | ||
|
|
d76f7d1508 | ||
|
|
9f496ef2d4 | ||
|
|
5065fcd520 | ||
|
|
0a4caf4a0a | ||
|
|
98b2b027af | ||
|
|
8aaf9a1978 | ||
|
|
d5c43d65f0 | ||
|
|
a45fe86a39 | ||
|
|
71ac83f3dd | ||
|
|
fb3749aff6 | ||
|
|
f26c8b5b8c | ||
|
|
ea4db4c9f7 | ||
|
|
f8d84da127 | ||
|
|
33d4761cc7 | ||
|
|
b5afa5f91e | ||
|
|
ef1a0cc65a | ||
|
|
a6ba772c08 | ||
|
|
a65755728f | ||
|
|
7acf4e741a | ||
|
|
c7530f9a81 | ||
|
|
6d74992efe | ||
|
|
3e24a72465 | ||
|
|
dda8e6a728 | ||
|
|
2366bda37b | ||
|
|
e0d76ae80d | ||
|
|
f14b6a8430 | ||
|
|
118d43e787 | ||
|
|
c92a344d47 | ||
|
|
ac3bae3cb9 | ||
|
|
348c65c7ec | ||
|
|
9e39795895 | ||
|
|
2e81e867b2 | ||
|
|
810fe53e79 | ||
|
|
28713f9b4d | ||
|
|
2ccc86cd13 | ||
|
|
dcde958450 | ||
|
|
e6ba9c3af3 | ||
|
|
37d2ebac4c | ||
|
|
45fcd28b40 | ||
|
|
9226ade511 | ||
|
|
aa8082ea6c | ||
|
|
b3f013fa18 | ||
|
|
2ed89e6529 | ||
|
|
88c940b165 | ||
|
|
d93ebec9df | ||
|
|
fd552a8c4b | ||
|
|
be121d87eb | ||
|
|
4f25ee33dc | ||
|
|
b9292516f4 | ||
|
|
f826aa893f | ||
|
|
55249704c5 | ||
|
|
b956bd589f | ||
|
|
62634e4c01 | ||
|
|
28fd49f0f6 | ||
|
|
4f5110306d | ||
|
|
9a15525298 | ||
|
|
27c272bd9a | ||
|
|
dd3e7aef0d | ||
|
|
b72c079a9a | ||
|
|
1d769cd261 | ||
|
|
071cfd1bb6 | ||
|
|
f0a68dce60 | ||
|
|
2db668f6ca | ||
|
|
7a9f587c3a | ||
|
|
900d7c8606 | ||
|
|
62b37111e7 | ||
|
|
5574287101 | ||
|
|
5b5b76a583 | ||
|
|
4d2faef37b | ||
|
|
96a9f32dc1 | ||
|
|
f6a58f1062 | ||
|
|
d5f28fb29c | ||
|
|
15ab5dbcaf | ||
|
|
4e3953cc45 | ||
|
|
8b243877c2 | ||
|
|
418a5562f6 | ||
|
|
7714e763d9 | ||
|
|
df7151ba30 | ||
|
|
53bfa7e3f7 | ||
|
|
e52fe9719f | ||
|
|
d421d3f286 | ||
|
|
aeebba391e | ||
|
|
c78fe6990b | ||
|
|
eab34a9d8c | ||
|
|
73370e1c06 | ||
|
|
c60b737f7d | ||
|
|
b08228d19f | ||
|
|
767c6c9a95 | ||
|
|
f430c416c7 | ||
|
|
4801a24089 | ||
|
|
5afd8a850b | ||
|
|
9f48a76e11 | ||
|
|
2a4667bb79 | ||
|
|
0804722cdf | ||
|
|
898b0f62d7 | ||
|
|
6729ee7636 | ||
|
|
e5ba6934e4 | ||
|
|
faaee693d5 | ||
|
|
05f43ed388 | ||
|
|
76b70203d9 | ||
|
|
b808e2d6bb | ||
|
|
4037c6545d | ||
|
|
d52af3b321 | ||
|
|
d98dcfa8c2 | ||
|
|
dd114d3980 | ||
|
|
ab985c6a10 | ||
|
|
e32028d1b5 | ||
|
|
01be082e9e | ||
|
|
a08be51e9f | ||
|
|
507e8b3c7b | ||
|
|
e3e7664665 | ||
|
|
c371d6e1e9 | ||
|
|
14f9b3d931 | ||
|
|
37fe105ba1 | ||
|
|
9075ce71ad | ||
|
|
00f707b250 | ||
|
|
ae849a7281 | ||
|
|
5738a0cf8c | ||
|
|
895079dd09 | ||
|
|
eb68761eb0 | ||
|
|
602d0021d4 | ||
|
|
0674facd46 | ||
|
|
8e8921d40c | ||
|
|
63e0822cc7 | ||
|
|
cfd2b0c75d | ||
|
|
1be370543f | ||
|
|
dd90965058 | ||
|
|
77cf757103 | ||
|
|
021ba690b0 | ||
|
|
01779a6f61 | ||
|
|
35c82b93eb | ||
|
|
6c88094da4 | ||
|
|
e0fd8207a1 | ||
|
|
da0970672d | ||
|
|
676959ecb5 | ||
|
|
b7df5b7805 | ||
|
|
4aa2b60c1d | ||
|
|
4e17be5705 | ||
|
|
bb19c9477e | ||
|
|
f608127635 | ||
|
|
957069d14d | ||
|
|
93091abb4d | ||
|
|
c57f1b74e0 | ||
|
|
3427593daf | ||
|
|
bddeb634b5 | ||
|
|
3b92a2bced | ||
|
|
8b9940a7b5 | ||
|
|
b6d9acca63 | ||
|
|
2d4a910fcd | ||
|
|
35e5508fd0 | ||
|
|
69d8afefc1 | ||
|
|
c55ae3b9ec | ||
|
|
166dcd21d0 | ||
|
|
c182f72ad3 | ||
|
|
6cc45e8dc9 | ||
|
|
e20da1f205 | ||
|
|
4a6c17ce83 | ||
|
|
c9f49db907 | ||
|
|
b912b117f0 | ||
|
|
f72931d9dd | ||
|
|
5cbf06c7ad | ||
|
|
11f27deddf | ||
|
|
bef4321afb | ||
|
|
3f1a77f68e | ||
|
|
880cca1bca | ||
|
|
f85056d83e | ||
|
|
527b76ef78 | ||
|
|
ce73c3fed2 | ||
|
|
430c297ca4 | ||
|
|
c3f2b8e774 | ||
|
|
bc2cd8b874 | ||
|
|
63012eeb60 | ||
|
|
5fd0db37d9 | ||
|
|
e7cbfe04fd | ||
|
|
522c2d0ecb | ||
|
|
5ead2cfbc4 | ||
|
|
3a4a63f70a | ||
|
|
58ef8ac3aa | ||
|
|
be3cfbffdf | ||
|
|
11fc3b9815 | ||
|
|
78f4848d70 | ||
|
|
c13c9d41f8 | ||
|
|
ad1f6b508f | ||
|
|
6aa6350c80 | ||
|
|
f7954a8fd6 | ||
|
|
de9593ad48 | ||
|
|
1260cacc67 | ||
|
|
ef535b81fe | ||
|
|
64ebdf4501 | ||
|
|
6dd719eaf6 | ||
|
|
3ed59b2812 | ||
|
|
764ff05231 | ||
|
|
75f50d86f6 | ||
|
|
6caa38a0ca | ||
|
|
0374865836 | ||
|
|
e9fac56232 | ||
|
|
5a0e0ba3b8 | ||
|
|
3af3811ab0 | ||
|
|
2572e1eb2f | ||
|
|
24a0bf9a61 | ||
|
|
630bdafcac | ||
|
|
dd3640548f | ||
|
|
e84ad86a75 | ||
|
|
b3b44d8a2d | ||
|
|
02beb74535 | ||
|
|
21a8bd0ccd | ||
|
|
78014e3d14 | ||
|
|
472b72a530 | ||
|
|
894c30ef02 | ||
|
|
f9eaf056ef | ||
|
|
50787b2faf | ||
|
|
911d580634 | ||
|
|
b62931f833 | ||
|
|
d2aed172ab | ||
|
|
00a6ac3de9 | ||
|
|
4bfd8038be | ||
|
|
e1ca412757 | ||
|
|
66c26f280b | ||
|
|
cffc548cd3 | ||
|
|
ae81f64d2a | ||
|
|
c945931961 | ||
|
|
69be514c67 | ||
|
|
554e9b1612 | ||
|
|
9622b4b4cf | ||
|
|
8238232485 | ||
|
|
c8e66c704a | ||
|
|
91daaa68ae | ||
|
|
9a4e6da5a9 | ||
|
|
fb679b63c2 | ||
|
|
6d1f39b250 | ||
|
|
99263a720d | ||
|
|
5af1699103 | ||
|
|
d3f498ceec | ||
|
|
1d15bcb9ec | ||
|
|
739039092f | ||
|
|
3f6d6dc756 | ||
|
|
5a7c769692 | ||
|
|
8d7071da4c | ||
|
|
72548712fe | ||
|
|
904d03e987 | ||
|
|
d0f3480a3c | ||
|
|
00eb03d0f2 | ||
|
|
75a75cbb20 | ||
|
|
09c249deb8 | ||
|
|
a5b970c6bf | ||
|
|
65c879757e | ||
|
|
18c18290d0 | ||
|
|
b1b687fb75 | ||
|
|
863ed47de2 | ||
|
|
1dccd0dee0 | ||
|
|
6582b2433a | ||
|
|
66f3cfa594 | ||
|
|
e2c23f3640 | ||
|
|
8d44b3edd3 | ||
|
|
431e8ebfcf | ||
|
|
9dd6cef90a | ||
|
|
9a2f395028 | ||
|
|
fd008cd8e0 | ||
|
|
fe843ec0b1 | ||
|
|
38ebd54776 | ||
|
|
3201f0538d | ||
|
|
af48fe8791 | ||
|
|
417737748a | ||
|
|
98de3380e3 | ||
|
|
9bab55337a | ||
|
|
c3bc38bf6c | ||
|
|
7a96391001 | ||
|
|
ab5a1c61b1 | ||
|
|
f04445c57c | ||
|
|
44b3627ab0 | ||
|
|
26881da76a | ||
|
|
d311940f24 | ||
|
|
cf061159ab | ||
|
|
5bcb6645b5 | ||
|
|
fd3282a87e | ||
|
|
a4e57366dd | ||
|
|
d565ecdc25 | ||
|
|
8189081511 | ||
|
|
7f1fa64019 | ||
|
|
c8aed4740d | ||
|
|
fa02df9ad3 | ||
|
|
ddea06360a | ||
|
|
94cc8475e0 | ||
|
|
1a24be574d | ||
|
|
217e8cf59f | ||
|
|
a55b5bb8ee | ||
|
|
ba35b5509d | ||
|
|
11d0ef2e09 | ||
|
|
805c338f36 | ||
|
|
a6a8f67649 | ||
|
|
7864d665e1 | ||
|
|
df6e476d67 | ||
|
|
ed3a7f070c | ||
|
|
132a3a46be | ||
|
|
e828acce0e | ||
|
|
53fa55937f | ||
|
|
e60aa11108 | ||
|
|
8b228ae701 | ||
|
|
03bc6d36db | ||
|
|
b6c21b1db5 | ||
|
|
433ebda8b4 | ||
|
|
2c1bf92010 | ||
|
|
7d4ed59816 | ||
|
|
7f1ae42f71 | ||
|
|
6ad62cec6a | ||
|
|
39db9fe424 | ||
|
|
c3d013b2fd | ||
|
|
a5d2986b1e | ||
|
|
6a37346acb | ||
|
|
f94fc7967b | ||
|
|
88a3c3f8a4 | ||
|
|
8dd1d84471 | ||
|
|
5802ef8746 | ||
|
|
3aaf7e2c54 | ||
|
|
fc80ea88df | ||
|
|
b89de8feab | ||
|
|
ae72abd019 | ||
|
|
ea0b733687 | ||
|
|
19f8cddfd6 | ||
|
|
9f42495d11 | ||
|
|
9b392bb2f8 | ||
|
|
5f27e02fae | ||
|
|
735c10059c | ||
|
|
77799e6c90 | ||
|
|
965d0b35f9 | ||
|
|
5295d56b10 | ||
|
|
dee8853fde | ||
|
|
494d4db6d9 | ||
|
|
e7f54b3310 | ||
|
|
3c84080aab | ||
|
|
55e5eb090b | ||
|
|
5e7f35c7c3 | ||
|
|
9c10a64f87 | ||
|
|
ba3869c597 | ||
|
|
38b293329c | ||
|
|
feeb19b8b3 | ||
|
|
2ef02ced16 | ||
|
|
1a936887b2 | ||
|
|
3fff74fb5b | ||
|
|
a574b744b8 | ||
|
|
bede3e3c60 | ||
|
|
de45f1dcb9 | ||
|
|
b3223e3f61 | ||
|
|
22acb0d5d7 | ||
|
|
1bd641d39f | ||
|
|
864205570a | ||
|
|
b81d18eeee | ||
|
|
c73742f300 | ||
|
|
adc07f5eaa | ||
|
|
0ce529af4e | ||
|
|
4a98c3ea03 | ||
|
|
12a2e3793a | ||
|
|
2b7bb3bb6a | ||
|
|
1b6f05470d | ||
|
|
5daaa453ff | ||
|
|
f557207887 | ||
|
|
5f3ba3e688 | ||
|
|
d1e5c0e1fd | ||
|
|
9997152c2d | ||
|
|
50f7e81432 | ||
|
|
a3bf4a0944 | ||
|
|
2919def828 | ||
|
|
d19686a2a5 | ||
|
|
bae87b4ded | ||
|
|
82895959d1 | ||
|
|
ffbde92f9a | ||
|
|
82957c98bf | ||
|
|
9c25dc4ed2 | ||
|
|
f3e6582e9e | ||
|
|
b37e47c9f7 | ||
|
|
6aba6ae231 | ||
|
|
ea9c3dab6b | ||
|
|
205a977777 | ||
|
|
6fa6f1f9b1 | ||
|
|
00fa97422f | ||
|
|
ebb4c45b68 | ||
|
|
28e0f4e9ec | ||
|
|
f73bb0fe11 | ||
|
|
348323dd23 | ||
|
|
8eac290e8d | ||
|
|
6256b68666 | ||
|
|
b225b0f64b | ||
|
|
bb91abd3df | ||
|
|
063cd25a93 | ||
|
|
52bf709bce | ||
|
|
3460eda9ca | ||
|
|
f5013c50ca | ||
|
|
eba709e5c0 | ||
|
|
e07b81649e | ||
|
|
c6d3bdf597 | ||
|
|
b5e98915c6 | ||
|
|
4f099a10f8 | ||
|
|
53fe7d6d3b | ||
|
|
778e464c20 | ||
|
|
ad86d39d2c | ||
|
|
e793e124a2 | ||
|
|
5b383b2ddd | ||
|
|
92617f9657 | ||
|
|
42e6a8a6f6 | ||
|
|
97822f233b | ||
|
|
09830dad05 | ||
|
|
5b59b87726 | ||
|
|
69a45e0bbc | ||
|
|
79bf26f33f | ||
|
|
ea58834940 | ||
|
|
828395d510 | ||
|
|
84e4361c09 | ||
|
|
05bb630a00 | ||
|
|
5b51fa01e2 | ||
|
|
333d118c0f | ||
|
|
4c8dc39f43 | ||
|
|
d40502aa3b | ||
|
|
4007b20fc2 | ||
|
|
3e4d4d93fd | ||
|
|
0f77ada55e | ||
|
|
9e30d93af3 | ||
|
|
c30878f616 | ||
|
|
6b7923cdd2 | ||
|
|
2df6b51ebb | ||
|
|
4200a425d5 | ||
|
|
9975bd23cd | ||
|
|
5b28a8b331 | ||
|
|
056c123759 | ||
|
|
4a5673e1dc | ||
|
|
385445804a | ||
|
|
bcfb4072f2 | ||
|
|
23c4eba5b3 | ||
|
|
80777d96da | ||
|
|
2fd11961cf | ||
|
|
55f0267c40 | ||
|
|
488fd5f60b | ||
|
|
d8c18f604f | ||
|
|
9c28f3c4c0 | ||
|
|
a8195ba2cc | ||
|
|
31ed005183 | ||
|
|
6a431d806a | ||
|
|
9d1c3631b8 | ||
|
|
405732a7f5 | ||
|
|
4fbc501121 | ||
|
|
0356f7677f | ||
|
|
b7bc855492 | ||
|
|
726b730b84 | ||
|
|
b7539467a6 | ||
|
|
7ee3f236a9 | ||
|
|
9dd72c4911 | ||
|
|
4ecc7a72f2 | ||
|
|
ed933d1893 | ||
|
|
fa26affc86 | ||
|
|
8097ec456a | ||
|
|
f570d40f50 | ||
|
|
bb6a58c2a7 | ||
|
|
6ee87fbf2d | ||
|
|
a6d9e49ab4 | ||
|
|
eec75ce918 | ||
|
|
4f4cc59873 | ||
|
|
dcb036292e | ||
|
|
9fca6c0296 | ||
|
|
aa6eee1a71 | ||
|
|
c7e207c290 | ||
|
|
b5c0b5b53e | ||
|
|
4ec2343d91 | ||
|
|
21241d2323 | ||
|
|
4593c29b64 | ||
|
|
a3920e2abd | ||
|
|
1b0ab75a84 | ||
|
|
3afcd64146 | ||
|
|
b783e7febe | ||
|
|
60c110d97b | ||
|
|
9bd7e2cb45 | ||
|
|
d6e545cc48 | ||
|
|
9240bd8d16 | ||
|
|
ea853cebde | ||
|
|
9ddf22e196 | ||
|
|
56e2bf7696 | ||
|
|
41681742cc | ||
|
|
a5797c5be6 | ||
|
|
5f73cb061b | ||
|
|
6c68ff565f | ||
|
|
dfad09e3b5 | ||
|
|
3b64c813cf | ||
|
|
acf33d6df2 | ||
|
|
ecf3ed8bdb | ||
|
|
4ad937c6ca | ||
|
|
2f08c7c932 | ||
|
|
36aeec9586 | ||
|
|
5dd3b4655e | ||
|
|
4cea84d7cb | ||
|
|
9aec6908b5 | ||
|
|
60cb68a276 | ||
|
|
271b1620e6 | ||
|
|
2a6eb41147 | ||
|
|
c34efca4cd | ||
|
|
3f547eafa4 | ||
|
|
7eb1a9500c | ||
|
|
6740ed5215 | ||
|
|
abf7c1142e | ||
|
|
20152a9872 | ||
|
|
0e2738ffb6 | ||
|
|
9d1f88cbe8 | ||
|
|
6221989f89 | ||
|
|
f8bb113c22 | ||
|
|
f2dc749c86 | ||
|
|
c523c38027 | ||
|
|
e80fe414b1 | ||
|
|
e8c56e0c0a | ||
|
|
fa2b6c5a61 | ||
|
|
34d00fd323 | ||
|
|
2f604a3c3a | ||
|
|
a6d5b3ea9d | ||
|
|
e1d755f7cd | ||
|
|
3fd96506f2 | ||
|
|
79ea571512 | ||
|
|
7b3c797237 | ||
|
|
8c24272ee7 | ||
|
|
fe342faa83 | ||
|
|
33b20991c4 | ||
|
|
f2bbd5957e | ||
|
|
bf9cce1aef | ||
|
|
c2a6f4bc43 | ||
|
|
1860d4f0e5 | ||
|
|
bedd55a4b4 | ||
|
|
24e1fa9a76 | ||
|
|
53db3a476b | ||
|
|
b3aca5289b | ||
|
|
a8c58c6605 | ||
|
|
f55c2b9267 | ||
|
|
bd0e341572 | ||
|
|
7ba3580c86 | ||
|
|
dca293a446 | ||
|
|
23e56c517c | ||
|
|
f3645aa51e | ||
|
|
7acac615ca | ||
|
|
0fe96d92df | ||
|
|
a66e43a99d | ||
|
|
5457f4a2bc | ||
|
|
9e7605482a | ||
|
|
b3529aa327 | ||
|
|
f580d9d073 | ||
|
|
593a8e12ce | ||
|
|
0ae3c0837b | ||
|
|
a2a3243ea8 | ||
|
|
07ff8b30ee | ||
|
|
81996df7a5 | ||
|
|
0d96275b68 | ||
|
|
2160d4fae5 | ||
|
|
c490d85045 | ||
|
|
07888ec096 | ||
|
|
d4dc940bd8 | ||
|
|
d65730c4c2 | ||
|
|
e91c8c39c2 | ||
|
|
344b9b6dff | ||
|
|
bf04d1aea3 | ||
|
|
8b62b1a715 | ||
|
|
1783bf6dfd | ||
|
|
ed4a62871f | ||
|
|
b8a9da0c0f | ||
|
|
6901edc05d | ||
|
|
8f229ede70 | ||
|
|
51559245d2 | ||
|
|
84cc819284 | ||
|
|
6838b6bff9 | ||
|
|
960b4df056 | ||
|
|
a83dbb2fb3 | ||
|
|
71f03a7224 | ||
|
|
7b6f7becd2 | ||
|
|
1f8ad7bc68 | ||
|
|
7f46bb1824 | ||
|
|
dae2b9cc4e | ||
|
|
48053d1032 | ||
|
|
f0d8cdafa8 | ||
|
|
97f6a0f3fd | ||
|
|
92a1c33625 | ||
|
|
8ccdb33dd2 | ||
|
|
788f4057ae | ||
|
|
f07acde513 | ||
|
|
f47ee1abb4 | ||
|
|
935721df10 | ||
|
|
3c42403b78 | ||
|
|
a8161f0e55 | ||
|
|
513d9b1555 | ||
|
|
7f2348619a | ||
|
|
ede91ff1f0 | ||
|
|
f3b3fb07de | ||
|
|
ab4193c1f4 | ||
|
|
c824db1ab7 | ||
|
|
17d7dbc461 | ||
|
|
2a45d8de7d | ||
|
|
ba098f24e5 | ||
|
|
1712e6d821 | ||
|
|
3e1e8cd3f8 | ||
|
|
59d465971e | ||
|
|
281e41f766 | ||
|
|
0f96bf365c | ||
|
|
969519c00f |
@@ -1,39 +0,0 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug in REA
|
||||
title: "[Bug] "
|
||||
labels: ["bug", "triage"]
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Bug Description
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
What you expected to happen.
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
What actually happened, including any error messages or unexpected output.
|
||||
|
||||
## Environment
|
||||
|
||||
- REA version (`rea --version` or `npm list @morluto/rea`):
|
||||
- Node.js version (`node --version`):
|
||||
- macOS version:
|
||||
- Hopper Disassembler version:
|
||||
- Target binary format (Mach-O, ELF, PE, .hop):
|
||||
|
||||
## Additional Context
|
||||
|
||||
- MCP client and version (if applicable):
|
||||
- Any relevant logs or stack traces:
|
||||
- Does the issue reproduce with a different target binary?
|
||||
@@ -0,0 +1,155 @@
|
||||
name: Bug report
|
||||
description: Report a reproducible problem in REA
|
||||
title: "[Bug] "
|
||||
labels:
|
||||
- bug
|
||||
- triage
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please report one primary behavior per issue. Keep observed behavior
|
||||
separate from suspected causes, and remove credentials and private paths.
|
||||
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the open and closed issues for a related report.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Area
|
||||
options:
|
||||
- Setup, packaging, or upgrade
|
||||
- Provider or capability discovery
|
||||
- Process capture or runtime observation
|
||||
- Native analysis or decompilation
|
||||
- Evidence, artifacts, or reconstruction
|
||||
- CLI
|
||||
- MCP or agent integration
|
||||
- Documentation
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: observed-behavior
|
||||
attributes:
|
||||
label: Observed behavior
|
||||
description: What happened? Include the command, MCP tool, provider, target type, and conditions that matter.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Give the smallest realistic command, configuration, input, or event sequence that demonstrates the problem.
|
||||
placeholder: |
|
||||
1. Configure or start ...
|
||||
2. Run ...
|
||||
3. Observe ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: What result, contract, invariant, recovery action, or state transition should have occurred?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: impact-scope
|
||||
attributes:
|
||||
label: Impact and scope
|
||||
description: What is affected, what is not affected, and does this affect evidence integrity, process isolation, compatibility, or recovery?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: rea-version
|
||||
attributes:
|
||||
label: REA version
|
||||
description: Use `rea --version` or provide the source revision.
|
||||
placeholder: 3.1.0 or git revision
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: runtime
|
||||
attributes:
|
||||
label: Runtime and platform
|
||||
description: Include Node.js, OS, architecture, and Hopper/Ghidra version when relevant.
|
||||
placeholder: Node 24.18; macOS 15 arm64; Hopper 5.15
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: target
|
||||
attributes:
|
||||
label: Target and artifact context
|
||||
description: Include binary format, app/container type, artifact kind, or workload details when relevant.
|
||||
placeholder: Mach-O arm64 app; Evidence v2 bundle; not applicable
|
||||
|
||||
- type: dropdown
|
||||
id: interface
|
||||
attributes:
|
||||
label: Interface
|
||||
options:
|
||||
- CLI
|
||||
- MCP
|
||||
- Python bridge
|
||||
- Package or setup
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: failure-stage
|
||||
attributes:
|
||||
label: Earliest failure stage
|
||||
description: Select where the incorrect behavior first becomes observable.
|
||||
options:
|
||||
- Request parsing or validation
|
||||
- Setup or provider admission
|
||||
- Capture or observation
|
||||
- Artifact or evidence registration
|
||||
- Native analysis or reconstruction
|
||||
- Transport or result projection
|
||||
- Cleanup, cancellation, or lifecycle
|
||||
- Unknown
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: relevant-evidence
|
||||
attributes:
|
||||
label: Relevant output or evidence
|
||||
description: Paste the smallest useful CLI output, MCP result, error, trace, or evidence excerpt. Redact sensitive values.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
id: typed-outcome
|
||||
attributes:
|
||||
label: Typed outcome or recovery expected
|
||||
description: Optional. What typed result, error, next tool, cleanup state, or limitation should the caller have received?
|
||||
|
||||
- type: textarea
|
||||
id: analysis
|
||||
attributes:
|
||||
label: Analysis or suspected cause
|
||||
description: Optional. Clearly mark source-derived reasoning or a hypothesis; do not present an unverified cause as fact.
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add links, reduced fixtures, screenshots, or a proposed regression test.
|
||||
@@ -0,0 +1,69 @@
|
||||
name: Design discussion
|
||||
description: Resolve an REA architecture, provider, or contract question before implementation
|
||||
title: "[Design] "
|
||||
labels:
|
||||
- enhancement
|
||||
- triage
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Use this for a substantial provider, public/persisted contract, or
|
||||
authority-boundary decision. Keep one decision per discussion.
|
||||
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the open and closed issues and pull requests for a related discussion.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: decision
|
||||
attributes:
|
||||
label: Decision needed
|
||||
description: State the exact question maintainers need to resolve.
|
||||
placeholder: Should REA ... or ...?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: context-evidence
|
||||
attributes:
|
||||
label: Context and evidence
|
||||
description: Explain why the decision is needed now. Separate observed behavior, derived evidence, and inference.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: boundary-invariants
|
||||
attributes:
|
||||
label: Boundary and invariants
|
||||
description: Identify the owning layer, provider/bridge boundary, artifact or evidence invariants, and any process or authorization constraints.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: options
|
||||
attributes:
|
||||
label: Options and tradeoffs
|
||||
description: Compare a small number of viable approaches, including platform, compatibility, safety, evidence, and maintenance costs.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: success-criteria
|
||||
attributes:
|
||||
label: Success criteria and open questions
|
||||
description: State what outcome would settle the decision and list only unresolved questions that could change the choice.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: references
|
||||
attributes:
|
||||
label: References
|
||||
description: Link relevant contracts, upstream provider documentation, tests, measurements, or prior discussions.
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Documentation
|
||||
about: Suggest improvements to REA documentation
|
||||
title: "[Docs] "
|
||||
labels: ["documentation", "triage"]
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Documentation Area
|
||||
|
||||
Which documentation needs improvement? (README, AGENTS.md, API docs, inline JSDoc, skill files)
|
||||
|
||||
## Problem
|
||||
|
||||
What is unclear, missing, or incorrect in the current documentation?
|
||||
|
||||
## Suggested Improvement
|
||||
|
||||
Describe the change you would like to see.
|
||||
|
||||
## Additional Context
|
||||
|
||||
- Link to the relevant documentation file or section:
|
||||
- Any examples or references that would help:
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Documentation
|
||||
description: Report unclear, missing, or incorrect REA documentation
|
||||
title: "[Docs] "
|
||||
labels:
|
||||
- documentation
|
||||
- triage
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the existing issues for a related documentation report.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: location
|
||||
attributes:
|
||||
label: Documentation location
|
||||
description: Link the page, section, command, API, skill, or inline comment.
|
||||
placeholder: README.md#quick-start or docs/mcp-contracts.md
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Problem
|
||||
description: What is unclear, missing, outdated, or incorrect, and who is affected?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-content
|
||||
attributes:
|
||||
label: Expected content
|
||||
description: Describe the accurate behavior or example that should be documented.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: evidence
|
||||
attributes:
|
||||
label: Supporting evidence
|
||||
description: Add source, command output, provider documentation, or a link that supports the correction.
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Include screenshots, affected translations, or related issues.
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Propose a new feature or enhancement for REA
|
||||
title: "[Feature] "
|
||||
labels: ["enhancement", "triage"]
|
||||
assignees: []
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
What problem does this feature solve? Describe the use case and why existing tools are insufficient.
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
Describe the feature or enhancement you would like to see. Include:
|
||||
|
||||
- Which REA tool(s) this affects (direct, enhanced, or session tools)
|
||||
- Expected inputs and outputs
|
||||
- How it integrates with the existing MCP workflow
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
Any alternative approaches or workarounds you have tried.
|
||||
|
||||
## Additional Context
|
||||
|
||||
- Relevant Hopper API methods or concepts:
|
||||
- Links to related issues or discussions:
|
||||
- Would this require changes to the bridge (`bridge/hopper_bridge.py`)?
|
||||
@@ -0,0 +1,88 @@
|
||||
name: Feature request
|
||||
description: Propose a focused improvement to REA
|
||||
title: "[Feature] "
|
||||
labels:
|
||||
- enhancement
|
||||
- triage
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Keep one primary capability or workflow outcome per request. For a
|
||||
substantial provider, public contract, or architecture change, explain
|
||||
the decision maintainers need to make before implementation.
|
||||
|
||||
- type: checkboxes
|
||||
id: before-submitting
|
||||
attributes:
|
||||
label: Before submitting
|
||||
options:
|
||||
- label: I searched the open and closed issues and pull requests for a related proposal.
|
||||
required: true
|
||||
- label: I removed credentials, tokens, private paths, and sensitive target data.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Area
|
||||
options:
|
||||
- Setup, packaging, or upgrade
|
||||
- Provider or capability discovery
|
||||
- Process capture or runtime observation
|
||||
- Native analysis or decompilation
|
||||
- Evidence, artifacts, or reconstruction
|
||||
- CLI
|
||||
- MCP or agent integration
|
||||
- Documentation
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: current-limitation
|
||||
attributes:
|
||||
label: Current limitation
|
||||
description: What user or agent workflow is blocked, ambiguous, or unnecessarily difficult today?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: use-cases
|
||||
attributes:
|
||||
label: Use cases
|
||||
description: Give concrete applications, targets, callers, or investigation workflows. Explain why current tools are insufficient.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: desired-outcome
|
||||
attributes:
|
||||
label: Desired outcome and success criteria
|
||||
description: Describe observable behavior, inputs, outputs, evidence, and compatibility criteria that would make this useful.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: boundaries
|
||||
attributes:
|
||||
label: Authority boundaries and non-goals
|
||||
description: Call out process execution, target mutation, provider licensing, evidence provenance, supported platforms, and work that should remain out of scope.
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives considered
|
||||
description: Describe existing REA operations, upstream tools, or workarounds considered and why they do not meet the need.
|
||||
|
||||
- type: textarea
|
||||
id: possible-direction
|
||||
attributes:
|
||||
label: Possible direction
|
||||
description: Optional implementation or integration idea. Treat it as a proposal, not a requirement.
|
||||
|
||||
- type: textarea
|
||||
id: references
|
||||
attributes:
|
||||
label: References
|
||||
description: Link relevant provider documentation, source, measurements, artifacts, issues, or discussions.
|
||||
@@ -1,34 +1,73 @@
|
||||
## Description
|
||||
<!--
|
||||
PR title: type(optional-scope): imperative outcome
|
||||
The title becomes the squash-merge commit subject and generated changelog entry.
|
||||
-->
|
||||
|
||||
<!-- What does this change do? Why is it needed? -->
|
||||
## Summary
|
||||
|
||||
## Type of Change
|
||||
<!-- What user, agent, or maintainer problem does this solve? Link the issue
|
||||
with "Fixes #123" when applicable. Keep the prior behavior, expected contract,
|
||||
and new behavior understandable without private context. -->
|
||||
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature
|
||||
- [ ] Documentation update
|
||||
- [ ] Performance improvement
|
||||
- [ ] Refactoring (no functional change)
|
||||
- [ ] Tool contract change (MCP schema modification)
|
||||
## Problem and expected behavior
|
||||
|
||||
## Testing Performed
|
||||
<!-- For fixes, give the smallest trigger and violated invariant. For features,
|
||||
describe the use case and observable outcome. -->
|
||||
|
||||
<!-- Describe the testing you have done to verify this change. -->
|
||||
## Change and scope
|
||||
|
||||
- [ ] Unit tests added or updated
|
||||
- [ ] TypeScript typecheck passes (`npm run typecheck`)
|
||||
- [ ] Linting passes (`npm run lint`)
|
||||
- [ ] Full test suite passes (`npm test`)
|
||||
- [ ] Real Hopper verification performed (`npm run verify:hopper`)
|
||||
- [ ] Linux real Hopper verification performed (`npm run verify:hopper:linux`) or unavailability explained
|
||||
- [ ] Real browser verification performed (`npm run verify:browser`) or unavailability explained
|
||||
- [ ] Package verification performed (`npm run verify:package`)
|
||||
<!-- Explain the chosen approach, why it fits REA's architecture, and what is
|
||||
intentionally not included. -->
|
||||
|
||||
## Tool Contract Impact
|
||||
## Contract and boundary impact
|
||||
|
||||
<!-- If this PR adds, removes, or modifies MCP tools, list them here. -->
|
||||
<!-- Canonical inventory and family counts are generated in docs/product-catalog.json. -->
|
||||
<!-- Complete the applicable lines; use "none" or "not applicable" explicitly. -->
|
||||
|
||||
## Related Issues
|
||||
- Semantic owner and earliest changed stage:
|
||||
- CLI and MCP/tool-catalog contract:
|
||||
- Provider, bridge, target-format, or platform compatibility:
|
||||
- Evidence, artifact, provenance, or reconstruction contract:
|
||||
- Process execution, authorization, cleanup, or containment impact:
|
||||
- Generated metadata (`docs/product-catalog.json`), package, or installation impact:
|
||||
|
||||
<!-- Link issues this PR closes or relates to. Use "Closes #123" syntax. -->
|
||||
## Evidence and regression coverage
|
||||
|
||||
<!-- State whether evidence is executed, source-derived, or proposed. For fixes,
|
||||
explain how the regression would fail on the affected base revision when practical. -->
|
||||
|
||||
- Tests added or updated:
|
||||
- Base reproduction or other evidence:
|
||||
- User-visible CLI/MCP output (if applicable):
|
||||
- Remaining proof gaps:
|
||||
|
||||
For evidence-bearing changes:
|
||||
|
||||
- [ ] Observed, derived, and inferred claims remain distinguishable.
|
||||
- [ ] Artifact identity, source provenance, and failed attempts remain preserved.
|
||||
- [ ] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.
|
||||
|
||||
## Validation performed
|
||||
|
||||
<!-- List only commands that actually ran, with observed results. Include the
|
||||
relevant real-provider or platform workflow, or explain why it was unavailable. -->
|
||||
|
||||
- `command` — result
|
||||
|
||||
## Compatibility, safety, and release
|
||||
|
||||
<!-- Call out breaking changes, migrations, package effects, platform/provider
|
||||
limits, security/privacy/containment implications, and meaningful proof gaps. -->
|
||||
|
||||
- Breaking changes or migration steps:
|
||||
- Real Hopper/Ghidra, browser, or OS coverage:
|
||||
- Package or release metadata impact:
|
||||
- Security, privacy, process, or containment review:
|
||||
|
||||
## Review checklist
|
||||
|
||||
- [ ] The PR has one focused outcome and the title follows `type(scope): outcome`.
|
||||
- [ ] Related issue is linked, or the reason for not linking one is stated above.
|
||||
- [ ] Tests cover changed observable behavior and meaningful failure paths.
|
||||
- [ ] Owning docs, contracts, and generated metadata are updated where needed.
|
||||
- [ ] User-visible CLI/MCP changes include representative output.
|
||||
- [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims.
|
||||
|
||||
+209
-34
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
branches: [main, "release/*"]
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -77,6 +77,8 @@ jobs:
|
||||
- run: npm ci
|
||||
- if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
run: npm run check:ci
|
||||
- if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
run: npm run verify:test-discovery
|
||||
- if: github.event_name == 'pull_request' && needs.changes.outputs.code != 'true'
|
||||
run: npm run format:check
|
||||
|
||||
@@ -88,7 +90,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1/2, 2/2]
|
||||
shard: [1/4, 2/4, 3/4, 4/4]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
@@ -105,11 +107,11 @@ jobs:
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: vitest-report-${{ strategy.job-index }}
|
||||
path: .vitest-reports/*
|
||||
path: .vitest/blob/*
|
||||
include-hidden-files: true
|
||||
if-no-files-found: error
|
||||
overwrite: true
|
||||
retention-days: 1
|
||||
retention-days: 7
|
||||
|
||||
build:
|
||||
name: Build package
|
||||
@@ -129,7 +131,7 @@ jobs:
|
||||
name: rea-dist
|
||||
path: dist
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
retention-days: 7
|
||||
|
||||
test:
|
||||
name: Test (coverage + timing)
|
||||
@@ -157,28 +159,143 @@ jobs:
|
||||
- uses: actions/download-artifact@v4
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
with:
|
||||
path: .vitest-reports
|
||||
path: .vitest/blob
|
||||
pattern: vitest-report-*
|
||||
merge-multiple: true
|
||||
- if: needs.changes.outputs.code == 'true' && success()
|
||||
run: npm run test:ci:merge
|
||||
- name: Merge coverage, JUnit, and timing reports
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
run: >-
|
||||
npm run test:ci:merge --
|
||||
--coverage.reportsDirectory=coverage
|
||||
--coverage.reporter=text
|
||||
--coverage.reporter=json-summary
|
||||
--coverage.reporter=lcov
|
||||
--reporter=default
|
||||
--reporter=junit
|
||||
--reporter=json
|
||||
--outputFile.junit=test-results.xml
|
||||
--outputFile.json=test-results.json
|
||||
- name: Summarize slowest test files
|
||||
if: needs.changes.outputs.code == 'true' && success()
|
||||
shell: bash
|
||||
run: |
|
||||
node <<'NODE' >> "${GITHUB_STEP_SUMMARY}"
|
||||
const report = JSON.parse(require("node:fs").readFileSync("test-results.json", "utf8"));
|
||||
const files = (report.testResults ?? [])
|
||||
.map((result) => ({
|
||||
name: result.name,
|
||||
duration: Math.max(0, (result.endTime ?? result.startTime) - result.startTime),
|
||||
}))
|
||||
.sort((left, right) => right.duration - left.duration)
|
||||
.slice(0, 20);
|
||||
console.log("## Slowest deterministic test files\n");
|
||||
console.log("| File | Duration (ms) |");
|
||||
console.log("| --- | ---: |");
|
||||
for (const file of files) {
|
||||
console.log(`| \`${file.name}\` | ${file.duration} |`);
|
||||
}
|
||||
NODE
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: needs.changes.outputs.code == 'true' && always()
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results.xml
|
||||
path: |
|
||||
test-results.xml
|
||||
test-results.json
|
||||
coverage/
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
|
||||
package-e2e:
|
||||
name: Package E2E (${{ matrix.os }})
|
||||
conformance-fixtures:
|
||||
name: Source-built conformance fixtures
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Verify fixtures from a clean checkout
|
||||
run: npm run verify:fixtures
|
||||
|
||||
windows-native-build:
|
||||
name: Build bundled Windows x64 native controls
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Prepare cross compiler
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
- run: npm run build:windows-native
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
if-no-files-found: error
|
||||
|
||||
package-e2e:
|
||||
name: Native package baseline (${{ matrix.platform }} ${{ matrix.architecture }})
|
||||
needs: [changes, windows-native-build]
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-14]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
architecture: x64
|
||||
- os: ubuntu-24.04-arm
|
||||
platform: linux
|
||||
architecture: arm64
|
||||
- os: macos-15
|
||||
platform: darwin
|
||||
architecture: arm64
|
||||
- os: macos-15-intel
|
||||
platform: darwin
|
||||
architecture: x64
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=1024"
|
||||
UV_THREADPOOL_SIZE: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Verify native runtime identity
|
||||
env:
|
||||
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
|
||||
EXPECTED_CI_ARCHITECTURE: ${{ matrix.architecture }}
|
||||
run: >-
|
||||
node -e "const assert = require('node:assert/strict');
|
||||
assert.equal(process.platform, process.env.EXPECTED_CI_PLATFORM);
|
||||
assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE);
|
||||
console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));"
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- run: npm run verify:package
|
||||
- name: Verify real Node Inspector discovery and observation
|
||||
if: matrix.platform == 'darwin'
|
||||
run: npm run verify:inspector
|
||||
|
||||
apple-artifacts:
|
||||
name: Real Apple artifact CLI/MCP E2E
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
@@ -186,7 +303,17 @@ jobs:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run verify:package
|
||||
- run: npm run verify:keyed-archive
|
||||
- run: npm run verify:asset-catalog
|
||||
- run: npm run verify:macos-bundle
|
||||
- name: Native compiled NIB hierarchy regressions
|
||||
run: npm run test:focused -- tests/boundary/filesystem/nibNativeHierarchy.test.ts tests/boundary/filesystem/interfaceBuilderBundle.test.ts
|
||||
- name: Real binary metadata and Interface Builder integration
|
||||
run: npm run verify:apple-dispatch && npm run verify:interface-builder
|
||||
- name: Native plist CLI selection regressions
|
||||
run: npm run test:focused -- tests/boundary/cli/nativePlistCli.test.ts tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts tests/boundary/cli/nativePlistSelectionFailureCli.test.ts
|
||||
- name: Native Swift demangling CLI and MCP regressions
|
||||
run: npm run test:focused -- tests/boundary/cli/nativeDemangleCli.test.ts tests/boundary/mcp/nativeDemangleMcp.test.ts
|
||||
|
||||
reconstruction-readiness:
|
||||
name: Reconstruction readiness
|
||||
@@ -218,24 +345,50 @@ jobs:
|
||||
retention-days: 30
|
||||
|
||||
javascript-runtime-observation:
|
||||
name: JavaScript runtime observation
|
||||
name: JavaScript runtime observation (${{ matrix.os }})
|
||||
needs: changes
|
||||
if: github.event_name == 'push' || needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
architecture: x64
|
||||
- os: ubuntu-24.04-arm
|
||||
platform: linux
|
||||
architecture: arm64
|
||||
- os: windows-latest
|
||||
platform: win32
|
||||
architecture: x64
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=1024"
|
||||
UV_THREADPOOL_SIZE: "2"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Verify native runtime identity
|
||||
env:
|
||||
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
|
||||
EXPECTED_CI_ARCHITECTURE: ${{ matrix.architecture }}
|
||||
run: >-
|
||||
node -e "const assert = require('node:assert/strict');
|
||||
assert.equal(process.platform, process.env.EXPECTED_CI_PLATFORM);
|
||||
assert.equal(process.arch, process.env.EXPECTED_CI_ARCHITECTURE);
|
||||
console.log(JSON.stringify({ platform: process.platform, architecture: process.arch, node: process.version }));"
|
||||
- run: npm ci
|
||||
- name: Verify real Node Inspector discovery and observation
|
||||
run: npm run verify:inspector
|
||||
|
||||
windows-curated:
|
||||
name: Windows Ghidra P0 and package lane
|
||||
needs: changes
|
||||
name: Windows capability and package lane
|
||||
needs: [changes, windows-native-build]
|
||||
if: github.event_name == 'push' || needs.changes.outputs.package == 'true'
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 25
|
||||
@@ -248,6 +401,11 @@ jobs:
|
||||
- run: npm ci
|
||||
- run: npm run build:cached
|
||||
- run: npm run typecheck
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-native-controls
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- name: Record named Windows host capabilities
|
||||
run: node scripts/windows-capabilities.mjs | Out-File -Encoding utf8NoBOM windows-capabilities.json
|
||||
- name: Validate Windows host capability report
|
||||
@@ -255,20 +413,42 @@ jobs:
|
||||
node -e "const report = JSON.parse(require('node:fs').readFileSync('windows-capabilities.json', 'utf8'));
|
||||
if (report.platform !== 'win32' || report.architecture !== 'x64')
|
||||
throw new Error('expected a Windows x64 capability report');"
|
||||
- name: Run curated Windows P0 tests
|
||||
# Keep this list aligned with tests/process-global/curatedWindowsLane.test.ts,
|
||||
# which fails when any path below is renamed or removed. Vitest silently
|
||||
# ignores unmatched paths, so a stale entry would otherwise report green
|
||||
# while skipping coverage.
|
||||
#
|
||||
# The matching native artifact establishes real Job Object and private
|
||||
# runtime boundaries for these fixtures. Ghidra and Java are not needed;
|
||||
# real engine claims remain in the separate controlled-provider workflow.
|
||||
- name: Run curated Windows capability tests
|
||||
run: >-
|
||||
npx vitest run --coverage.enabled=false
|
||||
tests/binaryTarget.test.ts
|
||||
tests/windowsCapabilities.test.ts
|
||||
tests/ghidraWindowsFixture.test.ts
|
||||
tests/ghidraInstallation.test.ts
|
||||
tests/ghidraProvider.test.ts
|
||||
tests/ghidraTransport.test.ts
|
||||
tests/ghidraTargetSnapshot.test.ts
|
||||
tests/ghidraClient.test.ts
|
||||
tests/ghidraLauncher.test.ts
|
||||
tests/processOwnership.test.ts
|
||||
tests/realGhidraWindowsWorkflow.test.ts
|
||||
tests/boundary/filesystem/binaryTarget.test.ts
|
||||
tests/boundary/filesystem/safeOutputTree.test.ts
|
||||
tests/boundary/providers/native/artifactProvider.archiveSafety.test.ts
|
||||
tests/boundary/filesystem/setupUnavailableProvider.test.ts
|
||||
tests/boundary/cli/referenceSourceImportStatus.test.ts
|
||||
src/cliReferenceSourceImportStatus.test.ts
|
||||
tests/boundary/filesystem/asarInventoryFreshness.test.ts
|
||||
tests/composition/javascript-applications/javascriptArtifactReconstruction.test.ts
|
||||
tests/conformance/providers/ghidra/windowsCapabilities.test.ts
|
||||
tests/conformance/providers/ghidra/ghidraWindowsFixture.test.ts
|
||||
src/ghidra/GhidraInstallation.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraTransport.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraTargetSnapshot.test.ts
|
||||
tests/boundary/providers/ghidra/ghidraLauncher.test.ts
|
||||
src/windows/WindowsNativeLoader.test.ts
|
||||
src/windows/WindowsOwnedProcess.test.ts
|
||||
src/ghidra/GhidraLauncherEnvironment.test.ts
|
||||
src/process/ProcessOwnership.test.ts
|
||||
src/process/ProcessOwnership.part2.test.ts
|
||||
src/process/ProcessOwnership.part3.test.ts
|
||||
src/process/ProcessOwnership.validation.test.ts
|
||||
src/application/Update.test.ts
|
||||
tests/process-global/updateMaintenance.test.ts
|
||||
tests/conformance/providers/ghidra/realGhidraWindowsWorkflow.test.ts
|
||||
- run: npm run verify:windows-native
|
||||
- run: npm run verify:package:windows
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
@@ -289,8 +469,3 @@ jobs:
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:check
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: api-docs
|
||||
path: build/api-docs
|
||||
retention-days: 14
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Source-built NativeAOT fixtures
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "bridge/ghidra/extensions/nativeaot/**"
|
||||
- "scripts/generate-nativeaot-fixtures.mjs"
|
||||
- "tests/fixtures/nativeaot/**"
|
||||
- ".github/workflows/nativeaot-fixtures.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
windows-fixture:
|
||||
name: Build Windows x64 NativeAOT fixture
|
||||
# .NET 8.0.22's bundled linker discovery supports VS 2019/2022.
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
|
||||
DOTNET_PROCESSOR_COUNT: "1"
|
||||
COMPlus_GCHeapHardLimit: "30000000"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "8.0.416"
|
||||
- name: Build benign fixture and independent byte oracle
|
||||
run: node scripts/generate-nativeaot-fixtures.mjs
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nativeaot-win-x64-${{ github.event.pull_request.head.sha || github.sha }}
|
||||
path: |
|
||||
_reference/nativeaot-integration/generated/win-x64/symbols/NativeAotFixture.exe
|
||||
_reference/nativeaot-integration/generated/win-x64/NativeAotFixture.map
|
||||
_reference/nativeaot-integration/generated/win-x64/symbols.txt
|
||||
_reference/nativeaot-integration/generated/win-x64/oracle.json
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Documentation build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: documentation-build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build documentation
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:build
|
||||
@@ -5,10 +5,34 @@ on:
|
||||
paths:
|
||||
- ".github/workflows/real-browser.yml"
|
||||
- "scripts/verify-real-browser.mjs"
|
||||
- "scripts/verify-browser-network.mjs"
|
||||
- "scripts/verify-browser-scripts.mjs"
|
||||
- "scripts/verify-browser-modules.mjs"
|
||||
- "scripts/verify-browser-dom-destinations.mjs"
|
||||
- "scripts/lib/browser-*.mjs"
|
||||
- "scripts/fixtures/browser-network-site.mjs"
|
||||
- "scripts/fixtures/browser-script-site.mjs"
|
||||
- "scripts/fixtures/browser-module-site.mjs"
|
||||
- "scripts/fixtures/browser-verifier-site.mjs"
|
||||
- "scripts/fixtures/page-cdp-proxy.mjs"
|
||||
- "src/browser/**"
|
||||
- "src/domain/browser*.ts"
|
||||
- "src/domain/webScript*.ts"
|
||||
- "src/domain/webModule*.ts"
|
||||
- "src/application/WebModule*.ts"
|
||||
- "src/composition/webModules.ts"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "src/artifacts/ArtifactPaths.ts"
|
||||
- "src/application/WebScriptExportService.ts"
|
||||
- "src/contracts/webScriptToolContracts.ts"
|
||||
- "src/server/registerWebScriptTool.ts"
|
||||
- "src/server/registerWebModuleTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/webModuleCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/cli/webScriptCommands.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -11,9 +11,29 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
name: Real Ghidra 12.1.2 Windows x64 PE proof
|
||||
native-artifact:
|
||||
name: Build matching Windows native artifact
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Prepare cross compiler
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
- run: npm run build:windows-native
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: real-ghidra-windows-native
|
||||
path: native/windows/build/
|
||||
if-no-files-found: error
|
||||
|
||||
verify:
|
||||
name: Real Ghidra 12.1.4 Windows x64 PE proof
|
||||
if: github.ref == 'refs/heads/main'
|
||||
needs: native-artifact
|
||||
environment: real-ghidra-windows
|
||||
runs-on: [self-hosted, Windows, x64, ghidra-12-1-2]
|
||||
timeout-minutes: 20
|
||||
@@ -23,13 +43,25 @@ jobs:
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: real-ghidra-windows-native
|
||||
path: native/windows/build/
|
||||
- run: npm run verify:windows-native:artifact
|
||||
- run: npm run verify:windows-native
|
||||
- name: Verify all admitted Windows Ghidra operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows | Tee-Object -FilePath windows-ghidra-proof.log
|
||||
- name: Verify ordinary-user CLI and MCP operations
|
||||
env:
|
||||
REA_ANALYSIS_PROVIDER: ghidra
|
||||
run: npm run verify:ghidra:windows:package | Tee-Object -FilePath windows-ghidra-package-proof.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: windows-ghidra-proof
|
||||
path: windows-ghidra-proof.log
|
||||
path: |
|
||||
windows-ghidra-proof.log
|
||||
windows-ghidra-package-proof.log
|
||||
retention-days: 30
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Real JavaScript recovery
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-javascript-recovery.yml"
|
||||
- "src/javascript/recovery/**"
|
||||
- "src/domain/javascript/**"
|
||||
- "src/application/javascript/JavaScriptRecovery*.ts"
|
||||
- "src/composition/javascriptRecovery.ts"
|
||||
- "src/contracts/javascript/javascriptRecoveryToolContracts.ts"
|
||||
- "src/cli/javascriptRecoveryCommands.ts"
|
||||
- "src/cli/javascriptApplicationAnalysis.ts"
|
||||
- "src/server/registerJavaScriptRecoveryTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/sessionAvailabilityPolicy.ts"
|
||||
- "src/application/CapabilityInventory.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "scripts/rea.mjs"
|
||||
- "src/process/**"
|
||||
- "src/artifacts/SafeOutputTree.ts"
|
||||
- "scripts/verify/javascript/recovery.mjs"
|
||||
- "scripts/fixtures/javascript-recovery/**"
|
||||
- "third_party/wakaru"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: javascript-recovery-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
NODE_OPTIONS: "--max-old-space-size=768"
|
||||
RAYON_NUM_THREADS: "1"
|
||||
|
||||
jobs:
|
||||
real-javascript-recovery:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Fetch pinned Wakaru release into the job workspace
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/rea-wakaru"
|
||||
curl --fail --location --retry 3 https://github.com/pionxzh/wakaru/releases/download/v1.13.0/wakaru-linux-x64.tar.gz -o "$RUNNER_TEMP/rea-wakaru/release.tar.gz"
|
||||
tar -xzf "$RUNNER_TEMP/rea-wakaru/release.tar.gz" -C "$RUNNER_TEMP/rea-wakaru" wakaru
|
||||
printf '%s %s\n' e8c7ca052974604197389f6ace7464e4dd24dce64e2730971521bd6606ec9c9e "$RUNNER_TEMP/rea-wakaru/wakaru" | sha256sum --check
|
||||
- name: Install isolated fixture compilers
|
||||
run: npm install --prefix "$RUNNER_TEMP/rea-fixture-compilers" --ignore-scripts --no-audit --no-fund esbuild@0.25.10 webpack@5.101.3
|
||||
- run: npm run build:cached
|
||||
- run: npm run verify:javascript:recovery
|
||||
env:
|
||||
REA_WAKARU_COMMAND: ${{ runner.temp }}/rea-wakaru/wakaru
|
||||
REA_JAVASCRIPT_FIXTURE_TOOLS: ${{ runner.temp }}/rea-fixture-compilers
|
||||
@@ -1,40 +0,0 @@
|
||||
name: Real JavaScript replay verification
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-javascript-replay.yml"
|
||||
- "scripts/verify-real-javascript-replay.mjs"
|
||||
- "src/replay/**"
|
||||
- "src/application/JavaScriptReplay*"
|
||||
- "src/domain/javascriptReplay.ts"
|
||||
- "tests/fixtures/replay/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: real-replay-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
real-replay:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: sudo apt-get update && sudo apt-get install -y bubblewrap
|
||||
- run: npm run verify:replay
|
||||
env:
|
||||
REA_REPLAY_ALLOW_UNAVAILABLE: "true"
|
||||
@@ -0,0 +1,54 @@
|
||||
name: Real website runtime attribution
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-runtime.yml"
|
||||
- "scripts/verify-browser-runtime.mjs"
|
||||
- "scripts/lib/browser-runtime-*.mjs"
|
||||
- "src/process/ProviderProcess.ts"
|
||||
- "src/process/ProcessOwnership*.ts"
|
||||
- "scripts/fixtures/browser-runtime-site.mjs"
|
||||
- "src/browser/execution/**"
|
||||
- "src/browser/CdpConnection.ts"
|
||||
- "src/browser/CdpEndpoint.ts"
|
||||
- "src/browser/CdpTarget*.ts"
|
||||
- "src/browser/CdpCaptureValues.ts"
|
||||
- "src/browser/CdpAuthorized*.ts"
|
||||
- "src/domain/webRuntime.ts"
|
||||
- "src/domain/webExecution.ts"
|
||||
- "src/domain/webEventListeners.ts"
|
||||
- "src/domain/browserObservationError*.ts"
|
||||
- "src/application/WebRuntime*.ts"
|
||||
- "src/composition/webRuntime.ts"
|
||||
- "src/contracts/webRuntimeToolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/server/registerWebRuntimeTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli/webRuntimeCommands.ts"
|
||||
- "src/cli/commandCancellation.ts"
|
||||
- "src/cli.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: real-web-runtime-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
HUSKY: "0"
|
||||
jobs:
|
||||
real-web-runtime:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run verify:browser:runtime
|
||||
env:
|
||||
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
|
||||
REA_BROWSER_NO_SANDBOX: "true"
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Real website source-map verification
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/real-web-source-map.yml"
|
||||
- "scripts/verify-browser-source-maps.mjs"
|
||||
- "scripts/lib/browser-source-map-e2e.mjs"
|
||||
- "scripts/fixtures/browser-source-map-site.mjs"
|
||||
- "src/javascript/sourceMaps/**"
|
||||
- "src/browser/assets/**"
|
||||
- "src/domain/webSource*.ts"
|
||||
- "src/domain/webScript*.ts"
|
||||
- "src/application/WebSourceLocation*.ts"
|
||||
- "src/application/ValidateSelectedWebScript.ts"
|
||||
- "src/composition/webSourceLocations.ts"
|
||||
- "src/server/registerWebSourceLocationTool.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/contracts/webScriptToolContracts.ts"
|
||||
- "src/process/**"
|
||||
- "src/cli/webSourceLocationCommands.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: real-web-source-map-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
HUSKY: "0"
|
||||
jobs:
|
||||
real-web-source-map:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Install the pinned compiler into an isolated fixture directory
|
||||
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:
|
||||
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
|
||||
+123
-15
@@ -2,7 +2,8 @@ name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: ["release/*"]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
@@ -21,11 +22,47 @@ jobs:
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
tag_name: ${{ steps.release.outputs.tag_name }}
|
||||
sha: ${{ steps.release.outputs.sha }}
|
||||
steps:
|
||||
- name: Validate release selection
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RELEASE_BRANCH: ${{ github.ref_name }}
|
||||
run: |
|
||||
if [[ "${GITHUB_REF}" != "refs/heads/${RELEASE_BRANCH}" || "${RELEASE_BRANCH}" != release/* ]]; then
|
||||
echo "Select a release/ branch; main and tags are not release checkpoints." >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! git check-ref-format "refs/heads/${RELEASE_BRANCH}"; then
|
||||
echo "Invalid release branch: ${RELEASE_BRANCH}" >&2
|
||||
exit 1
|
||||
fi
|
||||
selected_sha="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/heads/${RELEASE_BRANCH}" --jq '.object.sha')"
|
||||
if [[ "${selected_sha}" != "${GITHUB_SHA}" ]]; then
|
||||
echo "The release branch moved after this run was triggered; review its new checkpoint before publication." >&2
|
||||
exit 1
|
||||
fi
|
||||
printf 'Selected release checkpoint: %s\n' "${selected_sha}"
|
||||
|
||||
- uses: googleapis/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# A dedicated credential lets release-PR updates trigger CI. GitHub
|
||||
# requires manual approval for pull_request events from GITHUB_TOKEN.
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
target-branch: ${{ github.ref_name }}
|
||||
|
||||
- name: Bind publication to the triggering checkpoint
|
||||
if: steps.release.outputs.release_created == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_SHA: ${{ steps.release.outputs.sha }}
|
||||
run: |
|
||||
if [[ "${RELEASE_SHA}" != "${GITHUB_SHA}" ]]; then
|
||||
echo "The tagged release SHA differs from the triggering checkpoint; refusing to publish mismatched source provenance." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check out release pull request
|
||||
if: steps.release.outputs.prs_created == 'true'
|
||||
@@ -53,9 +90,22 @@ jobs:
|
||||
if: steps.release.outputs.prs_created == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_PR_BRANCH: ${{ fromJSON(steps.release.outputs.pr).headBranchName }}
|
||||
# Use the same identity for the follow-up commit so it also triggers CI.
|
||||
GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
RELEASE_PR_JSON: ${{ steps.release.outputs.pr }}
|
||||
run: |
|
||||
release_pr_branch="$(node --input-type=module -e '
|
||||
const value = JSON.parse(process.env.RELEASE_PR_JSON ?? "");
|
||||
if (
|
||||
typeof value !== "object" ||
|
||||
value === null ||
|
||||
typeof value.headBranchName !== "string" ||
|
||||
value.headBranchName.length === 0
|
||||
) {
|
||||
process.exit(1);
|
||||
}
|
||||
process.stdout.write(value.headBranchName);
|
||||
')"
|
||||
changed_paths="$(git diff --name-only)"
|
||||
if [[ -z "${changed_paths}" ]]; then
|
||||
echo "Release documentation is already canonical."
|
||||
@@ -72,7 +122,7 @@ jobs:
|
||||
git add -- docs/product-catalog.json
|
||||
git commit -m "chore: normalize release product catalog"
|
||||
gh auth setup-git
|
||||
git push origin "HEAD:${RELEASE_PR_BRANCH}"
|
||||
git push origin "HEAD:${release_pr_branch}"
|
||||
|
||||
publish:
|
||||
name: Publish to npm
|
||||
@@ -84,6 +134,9 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@@ -93,11 +146,25 @@ jobs:
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- name: Prepare Windows native build toolchain
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
|
||||
|
||||
- name: Build and verify bundled Windows native controls
|
||||
run: npm run build:windows-native && npm run verify:windows-native:artifact
|
||||
|
||||
- name: Verify packaged release before publishing
|
||||
run: npm run verify:package
|
||||
|
||||
- name: Publish
|
||||
run: npm publish --access public
|
||||
shell: bash
|
||||
run: |
|
||||
version="$(node --input-type=module -e 'import packageJson from "./package.json" with { type: "json" }; process.stdout.write(packageJson.version)')"
|
||||
published_version="$(npm view "rea-agents@${version}" version --json 2>/dev/null || true)"
|
||||
if [ "${published_version}" = "\"${version}\"" ]; then
|
||||
echo "rea-agents@${version} is already published; verifying the existing package."
|
||||
else
|
||||
npm publish --access public
|
||||
fi
|
||||
|
||||
- name: Verify published CLI from npm
|
||||
shell: bash
|
||||
@@ -106,17 +173,58 @@ jobs:
|
||||
verification_root="$(mktemp -d)"
|
||||
trap 'rm -rf -- "${verification_root}"' EXIT
|
||||
cd "${verification_root}"
|
||||
for attempt in 1 2 3 4 5 6; do
|
||||
if npm view "rea-agents@${version}" version --json >/dev/null 2>&1 \
|
||||
&& npm exec --yes --package="rea-agents@${version}" -- rea --help \
|
||||
&& npm exec --yes --package="rea-agents@${version}" -- rea --llms \
|
||||
&& node "${GITHUB_WORKSPACE}/scripts/verify-published-package.mjs" "${version}"; then
|
||||
for attempt in $(seq 1 80); do
|
||||
published_version="$(npm view --prefer-online "rea-agents@${version}" version --json 2>/dev/null || true)"
|
||||
if [ "${published_version}" = "\"${version}\"" ]; then
|
||||
echo "rea-agents@${version} is visible on npm after attempt ${attempt}; verifying the published CLI."
|
||||
npm exec --yes --package="rea-agents@${version}" -- rea --help
|
||||
npm exec --yes --package="rea-agents@${version}" -- rea --llms
|
||||
node "${GITHUB_WORKSPACE}/scripts/verify-published-package.mjs" "${version}"
|
||||
exit 0
|
||||
else
|
||||
echo "rea-agents@${version} is not visible on npm yet (attempt ${attempt}/80; up to 20 minutes)" >&2
|
||||
fi
|
||||
if [ "${attempt}" -lt 6 ]; then
|
||||
echo "Published package verification attempt ${attempt} failed; retrying in 10 seconds" >&2
|
||||
sleep 10
|
||||
if [ "${attempt}" -lt 80 ]; then
|
||||
sleep 15
|
||||
fi
|
||||
done
|
||||
echo "Published rea-agents@${version} failed registry and CLI verification" >&2
|
||||
echo "rea-agents@${version} did not become visible on npm within 20 minutes" >&2
|
||||
exit 1
|
||||
|
||||
publish-mcp:
|
||||
name: Publish to MCP Registry
|
||||
needs: [release-please, publish]
|
||||
if: needs.release-please.outputs.release_created == 'true' && needs.publish.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.release-please.outputs.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Verify package and registry metadata versions
|
||||
run: npm run mcp-registry:check
|
||||
|
||||
- name: Install MCP Registry publisher
|
||||
env:
|
||||
MCP_PUBLISHER_VERSION: v1.8.0
|
||||
MCP_PUBLISHER_SHA256_LINUX_AMD64: 1370446bbe74d562608e8005a6ccce02d146a661fbd78674e11cc70b9618d6cf
|
||||
run: |
|
||||
archive="mcp-publisher_linux_amd64.tar.gz"
|
||||
curl -fsSLo "${archive}" \
|
||||
"https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}/${archive}"
|
||||
echo "${MCP_PUBLISHER_SHA256_LINUX_AMD64} ${archive}" | sha256sum --check --strict
|
||||
tar xzf "${archive}" mcp-publisher
|
||||
chmod +x mcp-publisher
|
||||
|
||||
- name: Validate MCP Registry metadata
|
||||
run: ./mcp-publisher validate
|
||||
|
||||
- name: Authenticate to MCP Registry
|
||||
run: ./mcp-publisher login github-oidc
|
||||
|
||||
- name: Publish MCP Registry metadata
|
||||
run: ./mcp-publisher publish
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Website checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "website/**"
|
||||
- "scripts/verify-website.py"
|
||||
- "scripts/prepare-website.py"
|
||||
- ".github/workflows/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: website-check-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Website checks
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare the example download
|
||||
run: python3 scripts/prepare-website.py
|
||||
- name: Check website links and publishing configuration
|
||||
run: python3 scripts/verify-website.py
|
||||
@@ -0,0 +1,48 @@
|
||||
name: Publish REA website
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: website-pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
pages: read
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare the example download
|
||||
run: python3 scripts/prepare-website.py
|
||||
- name: Check website links and publishing configuration
|
||||
run: python3 scripts/verify-website.py
|
||||
- name: Configure Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload the website
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: website/public
|
||||
|
||||
deploy:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Publish to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
+8
-2
@@ -6,8 +6,7 @@ __pycache__/
|
||||
# Distribution
|
||||
dist/
|
||||
build/
|
||||
docs/api/
|
||||
node_modules/
|
||||
node_modules
|
||||
*.egg-info/
|
||||
*.egg
|
||||
|
||||
@@ -38,7 +37,14 @@ coverage/
|
||||
test-results.xml
|
||||
.turbo/
|
||||
.cache/
|
||||
docs/.vitepress/cache/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Local reference sources and analysis artifacts
|
||||
/_reference/
|
||||
|
||||
# Generated website download
|
||||
/website/public/examples/notes-example.zip
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
[submodule "third_party/jadx-headless-mcp"]
|
||||
path = third_party/jadx-headless-mcp
|
||||
url = https://github.com/1013503897/jadx-headless-mcp.git
|
||||
[submodule "third_party/binwalk"]
|
||||
path = third_party/binwalk
|
||||
url = https://github.com/ReFirmLabs/binwalk.git
|
||||
[submodule "third_party/unblob"]
|
||||
path = third_party/unblob
|
||||
url = https://github.com/onekey-sec/unblob.git
|
||||
[submodule "third_party/ghidra-nativeaot"]
|
||||
path = third_party/ghidra-nativeaot
|
||||
url = https://github.com/Washi1337/ghidra-nativeaot.git
|
||||
[submodule "third_party/wakaru"]
|
||||
path = third_party/wakaru
|
||||
url = https://github.com/pionxzh/wakaru.git
|
||||
+8
-3
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"threshold": 3,
|
||||
"_comment": "Test files are intentionally NOT excluded: the gate used to report zero clones because it skipped **/*.test.ts, hiding all test-tree duplication. Current actual is ~2.4% lines across src+ and tests/, down from 3.4% when tests were included. Lower this as duplication is removed.",
|
||||
"threshold": 2.5,
|
||||
"reporters": ["consoleFull"],
|
||||
"ignore": [
|
||||
"node_modules/**",
|
||||
@@ -7,8 +8,12 @@
|
||||
"docs/**",
|
||||
".codex/**",
|
||||
".devin/**",
|
||||
"**/*.test.ts",
|
||||
"tests/fixtures/**"
|
||||
"tests/fixtures/**",
|
||||
"third_party/**",
|
||||
"tests/conformance/**/*.c",
|
||||
"tests/conformance/**/*.m",
|
||||
"tests/conformance/**/*.swift",
|
||||
"tests/conformance/**/*.xib"
|
||||
],
|
||||
"gitignore": true,
|
||||
"minLines": 5,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"printWidth": 80,
|
||||
"sortImports": false,
|
||||
"sortPackageJson": false,
|
||||
"ignorePatterns": [
|
||||
"dist/**",
|
||||
"build/**",
|
||||
"docs/api/**",
|
||||
"node_modules/**",
|
||||
"CHANGELOG.md",
|
||||
"package-lock.json",
|
||||
"_reference/**",
|
||||
"third_party/**"
|
||||
]
|
||||
}
|
||||
+85
-6
@@ -6,11 +6,19 @@
|
||||
"max-depth": ["warn", 4],
|
||||
"max-lines": [
|
||||
"warn",
|
||||
{ "max": 500, "skipBlankLines": true, "skipComments": true }
|
||||
{
|
||||
"max": 500,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"max-lines-per-function": [
|
||||
"warn",
|
||||
{ "max": 100, "skipBlankLines": true, "skipComments": true }
|
||||
{
|
||||
"max": 100,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"max-params": ["warn", 4],
|
||||
"no-var": "warn",
|
||||
@@ -18,10 +26,69 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["tests/**/*.ts"],
|
||||
"files": [
|
||||
"scripts/electron-active-hook*.cjs",
|
||||
"tests/conformance/readiness/electron/*.js",
|
||||
"website/public/examples/notes-electron/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"max-lines-per-function": "off",
|
||||
"max-lines": "off"
|
||||
"no-var-requires": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["tests/**/*.ts", "src/**/*.test.ts", "src/**/*.fixture.ts"],
|
||||
"rules": {
|
||||
"max-lines": "off",
|
||||
"max-lines-per-function": [
|
||||
"error",
|
||||
{
|
||||
"max": 150,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/application/**/*.test.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"group": ["**/server/**", "**/cli.*", "**/main.*"],
|
||||
"message": "Service tests must use application ports rather than CLI or MCP entrypoints."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["tests/composition/**/*.test.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"group": [
|
||||
"**/server/**",
|
||||
"**/cli.*",
|
||||
"**/main.*",
|
||||
"node:child_process",
|
||||
"node:net",
|
||||
"node:http",
|
||||
"node:https",
|
||||
"ws",
|
||||
"playwright"
|
||||
],
|
||||
"message": "Composition tests may wire provider-neutral sessions but must not invoke CLI, MCP, process, network, or browser boundaries."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -29,5 +96,17 @@
|
||||
"node": true,
|
||||
"es2023": true
|
||||
},
|
||||
"ignorePatterns": ["dist", "node_modules", "docs/api", ".codex", ".devin"]
|
||||
"ignorePatterns": [
|
||||
"dist",
|
||||
"build",
|
||||
"node_modules",
|
||||
"docs/api",
|
||||
"coverage",
|
||||
".turbo",
|
||||
".cache",
|
||||
".codex",
|
||||
".devin",
|
||||
".factory",
|
||||
"third_party"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
dist
|
||||
docs/api
|
||||
node_modules
|
||||
CHANGELOG.md
|
||||
package-lock.json
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "2.7.0"
|
||||
".": "5.0.0"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Product Direction
|
||||
|
||||
REA exposes reverse-engineering tools through a CLI and MCP server. Hopper and the bring-your-own Ghidra adapter are operation-capable deep binary-analysis providers. Ghidra is supported on Linux x64 and has an experimental Windows x64 P0 boundary for approved native x86-64 PE applications; it supplies admitted read-only inventory and function-analysis operations but no GUI or mutation authority. Keep provider-specific code out of the domain and application layers.
|
||||
REA exposes reverse-engineering tools through a CLI and MCP server. Hopper, the bring-your-own Ghidra adapter, and the bring-your-own IDA MCP adapter are operation-capable deep binary-analysis providers. Ghidra is supported on Linux x64 and macOS x64/arm64 with matching native decompiler tools, and has an experimental Windows x64 P0 boundary for approved native x86-64 PE applications; on Linux and macOS it supplies inventory, function analysis, and atomic function annotation edits in an ephemeral database, without modifying executable bytes or controlling a GUI. Windows P0 has no mutation authority. IDA adapts upstream legacy attached GUI and modern headless database-supervisor profiles for live read-only analysis; initial real verification covers Windows, and an attached GUI database is never saved or closed. Keep provider-specific code out of the domain and application layers.
|
||||
|
||||
Prioritize:
|
||||
|
||||
@@ -13,126 +13,101 @@ Prioritize:
|
||||
|
||||
Installers must not install or upgrade Homebrew, Node.js, npm, Java, Ghidra, or other unrelated software. Ghidra is bring-your-own. `rea setup` must print its planned changes and require approval before writing files or installing Hopper.
|
||||
|
||||
REA is a local-only tool; do not sanitize actionable local diagnostics such as artifact paths, digests, mismatch locations, or analysis metadata, while continuing to redact genuine secrets such as credentials and authorization headers.
|
||||
REA is a local-only tool. Preserve caller-selected inputs, captured output, URLs, paths, digests, mismatch locations, and analysis metadata. Do not guess that local evidence is secret from environment-variable names, argument names, or text patterns. Redact transport authentication credentials and values the caller explicitly marks sensitive; do not persist the entire ambient environment merely because a child inherits it.
|
||||
|
||||
## Project Structure & Module Organization
|
||||
|
||||
REA is a layered ESM TypeScript application. Dependencies flow inward: `domain` underlies `contracts` and the shared `process` primitives; providers depend on those layers, followed by `application`, `server`, and the entry adapters.
|
||||
REA is a layered ESM TypeScript application. Dependencies flow inward from pure domain logic through contracts, providers, application workflows, and CLI/MCP adapters. See [docs/architecture.mermaid](docs/architecture.mermaid) for the component map.
|
||||
|
||||
See [docs/architecture.mermaid](docs/architecture.mermaid) for a visual architecture diagram.
|
||||
|
||||
- `scripts/rea.mjs`: executable dispatcher. Routes only bare `mcp` and `--mcp` to the production stdio server; Incur handles registration utilities and one-shot commands.
|
||||
- `src/main.ts`: MCP adapter. Parses config, wires the shared session runtime, starts stdio transport, and owns process-lifetime shutdown.
|
||||
- `src/cli.ts`: one-shot CLI adapter for setup, diagnostics, analysis, and decompilation.
|
||||
- `src/config.ts`: Zod-validated parsing of environment configuration into `AppConfig`.
|
||||
- `src/domain/`: pure, side-effect-free modules. `errors.ts` owns the tagged error algebra; `result.ts` owns `Result`/`ok`/`err`; `hopperValues.ts` owns shared function-dossier values plus Hopper boundary parsers; `symbolAnalysis.ts` parses Swift/ObjC names; `javascriptApplicationGraph.ts` validates and canonically commits the provider-neutral JavaScript Application Graph; `javascriptStaticAnalysis.ts` performs bounded AST-only JavaScript structure recovery.
|
||||
- `src/contracts/`: caller-visible tool schemas and catalog metadata; `toolContracts.ts` owns the canonical inventory and `enhancedInputs.ts` owns enhanced input parsing.
|
||||
- `src/process/`: provider-neutral process ownership and lifecycle primitives. It owns private runtime roots, session-assigned run identity and token-verified lineage, absolute startup deadlines, correlated request waits, bounded output capture, and TERM-to-KILL cleanup without defining any provider wire protocol.
|
||||
- `src/replay/`: Linux x64 controlled-JavaScript-replay adapter. It owns exact runtime closure inspection, Bubblewrap/seccomp/cgroup admission, the disposable worker, strict parent/worker protocol validation, and complete cleanup observation.
|
||||
- `src/browser/`: loopback CDP/Inspector discovery, bounded WebSocket transport, exact-origin or canonical-root target authorization, passive browser/Electron and attach-only Node/Electron V8 observation, plus controlled Playwright scenarios with explicit launch/attach ownership and cleanup.
|
||||
- `src/hopper/`: Hopper launch and Unix-socket protocol mechanics. `BridgeLauncher.ts` spawns the Hopper app with the in-process bridge, `HopperClient.ts` correlates request/response over the socket with timeouts and cancellation, `protocol.ts` frames bridge messages.
|
||||
- `bridge/hopper_bridge.py`: runs inside Hopper and adapts declared operations to Hopper's public Python API. Hopper's bundled MCP server is not used.
|
||||
- `src/ghidra/`: exact Ghidra 12.1.2/JDK 21 inspection, analysis-profile commitment, digest-bound target snapshots, isolated `analyzeHeadless` launch, authenticated Unix-socket or Windows loopback transport, bounded serial request queue, and strict inventory/function boundaries.
|
||||
- `src/dotnet/`: execution-free managed PE/CLI inspection. It owns bounded PE, CLI metadata, heap, table, and resource parsing for `rea-dotnet-static`; it must never load, reflect, execute, decompile, or resolve target assemblies.
|
||||
- `bridge/ghidra/ReaGhidraBridge.java`: packaged read-only `HeadlessScript` loaded through Ghidra's `scriptPath`; it owns the persistent decompiler and adapts admitted inventory, function, reference, and CFG operations to public Ghidra APIs.
|
||||
- `src/application/`: shared CLI/MCP session composition, setup and diagnostics, and enhanced workflows. `AnalysisProviderRegistry` discovers overlapping deep candidates without starting them; `SessionProviderRouter` binds one candidate per target and composes it with disjoint auxiliary providers; `JavaScriptArtifactReconstruction.ts` safely projects local directories/ASARs and inert bundle syntax into the application graph without adding a caller-visible tool yet.
|
||||
- `src/server/`: MCP request translation. `createServer.ts` assembles the MCP server, `registerOfficialTools.ts`/`registerEnhancedTools.ts` register each tool set, `toolResult.ts` maps `Result` values to MCP content.
|
||||
- `docs/product-catalog.json`: generated package, tool-family, provider, setup-client, schema-version, and CLI facts. Regenerate it from source; do not edit it by hand.
|
||||
- `tests/`: Vitest suite. `tests/fixtures/` holds reusable provider-process fixtures, source-owned synthetic JavaScript graph and Electron/Webpack/Rspack artifact trees, and the fake launcher, Hopper bridge, and CDP seams.
|
||||
- `scripts/verify-real-hopper.mjs`: real-Hopper end-to-end verifier.
|
||||
- `scripts/verify-real-ghidra.mjs`: real Linux Ghidra verifier for x86-64 debug/stripped ELF, AArch64 ELF, PE, and Mach-O function semantics plus complete cleanup.
|
||||
- `scripts/verify-real-ghidra-windows.mjs`: real Windows x64 Ghidra P0 verifier for the source-owned native PE fixture, all 19 admitted operations, digest linkage, loopback transport, and cleanup.
|
||||
- `scripts/verify-real-browser.mjs`: real Chrome end-to-end verifier for the passive CDP provider.
|
||||
- `scripts/print-mcp-config.mjs`: prints an MCP server config with absolute paths filled in (`npm run config:print -- /path/to/binary`).
|
||||
- `src/domain/` owns pure provider-neutral semantics; `src/contracts/` owns caller-visible schemas and the canonical tool inventory.
|
||||
- `src/hopper/`, `src/ghidra/`, `src/ida/`, `src/browser/`, `src/inspector/`, `src/native/`, `src/artifacts/`, and `src/dotnet/` own provider-specific boundaries. Keep provider protocols out of domain and application code.
|
||||
- `src/application/` composes shared CLI/MCP workflows; `src/server/` translates MCP requests; `src/cli.ts` and `src/main.ts` are the CLI and MCP entry points.
|
||||
- `src/process/` owns shared process lifecycle primitives, not provider wire protocols. `bridge/` contains provider-side adapters.
|
||||
- `tests/` contains unit, composition, boundary, acceptance, and conformance tests. `scripts/verify-*` and capability directories under `scripts/verify/` contain real-toolchain checks.
|
||||
- `docs/product-catalog.json` is generated. Update its source contracts and regenerate it; do not edit it directly.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
- `npm ci`: install exact lockfile dependencies.
|
||||
- `npm run deps:check`: verify that installed direct dependency versions match the root lockfile; on failure, run `npm ci`, then retry.
|
||||
- `npm run metadata:generate`: refresh checked-in package metadata after package, lockfile, or skill-version changes.
|
||||
- `npm run metadata:check`: verify checked-in package metadata without rewriting it.
|
||||
- `npm run build`: compile `src/` into `dist/`.
|
||||
- `npm run build:cached`: compile through Turbo and reuse a valid local build cache entry.
|
||||
- `npm test`: restore or build `dist/`, then run the Vitest suite once without coverage or retries locally.
|
||||
- `npm run test:fast`: run pure and subprocess test projects without the serial integration group.
|
||||
- `npm run test:changed`: run changed and related non-serial tests once.
|
||||
- `npm run test:integration`: run the serial filesystem, process, and CLI integration group.
|
||||
- `npm run test:watch`: watch changed and related non-serial tests, without coverage.
|
||||
- `npm run test:coverage`: run the complete suite with coverage; CI shards the suite and owns JUnit reporting.
|
||||
- `npm run typecheck`: run strict TypeScript checks without emitting files.
|
||||
- `npm run lint`: apply oxlint rules (complexity, max-lines, unused vars, and TypeScript-specific checks).
|
||||
- `npm run lint:fix`: auto-fix oxlint violations where possible.
|
||||
- `npm run format:check`: verify Prettier formatting.
|
||||
- `npm run check`: run cached typecheck, lint, format:check, knip, and package-metadata freshness tasks.
|
||||
- `npm run check:fast`: run only cached typecheck and lint for rapid local and pre-push feedback.
|
||||
- `npm run check:ci`: run the static gate plus duplicate-code and debt-marker scans.
|
||||
- `npm run check:test`: run the cached static tasks plus the uncached complete test suite.
|
||||
- `npm run check:pr`: run the test gate plus uncached generated-document validation for PR preparation.
|
||||
- `npm run knip`: detect unused files, dependencies, and exports.
|
||||
- `npm run jscpd`: detect duplicate code blocks.
|
||||
- `npm run scan:todos`: scan for TODO, FIXME, and HACK markers.
|
||||
- `npm run verify:hopper`: build and run the real-Hopper verifier with two distinct binaries.
|
||||
- `npm run verify:ghidra`: build and run the real-Ghidra verifier against `GHIDRA_INSTALL_DIR` and optional `GHIDRA_TARGET_PATH`.
|
||||
- `npm run verify:ghidra:windows`: build and run the real Windows Ghidra verifier against `GHIDRA_INSTALL_DIR` and the source-owned x64 PE fixture.
|
||||
- `npm run verify:browser`: build and run the real Chrome verifier against `REA_BROWSER_EXECUTABLE` or a platform-default Chrome-family executable, including browser-scenario CLI parity, attach disconnect-only ownership, launched-process termination, and temporary-profile cleanup.
|
||||
- `npm run verify:inspector`: build and run the real attach-only Node Inspector verifier through the public CLI.
|
||||
- `npm run verify:managed`: build and run the source-owned managed PE/CLI conformance verifier for artifact triage, member inspection, managed/native boundary declarations, token drift comparison, malformed metadata, non-managed degradation, managed application-graph projection, manifest-verifier self-test, and optional BYO ILSpy oracle checks; set `REA_MANAGED_APP_MANIFEST_PATH` to verify an operator-local managed app manifest, including optional graph/trace assertions, and set `REA_ILSPY_CMD_PATH` to an absolute `ilspycmd` path to run the real ILSpy reconstruction oracle.
|
||||
- `npm run evidence:generate`: regenerate the managed conformance manifest and Evidence v2 completion ledger from live verifier output.
|
||||
- `npm run evidence:check`: rerun managed conformance and fail on generated manifest, completion ledger, or bundled skill drift.
|
||||
- `npm run verify:replay`: build and run the real Linux Bubblewrap/seccomp/cgroup verifier against source-owned replay fixtures; set `REA_REPLAY_INPUT_PATH` to verify an operator-local manifest.
|
||||
- `npm run verify:package`: pack and test the CLI, setup transaction, skill, and canonical target-free MCP catalog in an isolated environment.
|
||||
- `npm run docs:api`: render uncommitted API documentation from JSDoc comments into `build/api-docs/` using TypeDoc.
|
||||
- `npm run docs:api:cached`: render API documentation through Turbo or restore a valid local result.
|
||||
- `npm run docs:generate`: refresh committed metadata and render the uncommitted API documentation.
|
||||
- `npm run docs:check`: verify generated package metadata, the canonical product catalog, caller-visible documentation facts, TypeDoc rendering, and the error JSON schema.
|
||||
- `npm run config:print -- /path/to/binary`: print an MCP server config with absolute paths.
|
||||
- `HOPPER_TARGET_PATH=/path/to/binary npm start`: launch Hopper and run the built stdio MCP server.
|
||||
- `npm ci`: install the locked dependencies.
|
||||
- `npm run build:cached`: build the CLI and MCP server.
|
||||
- `npm run test:local`: run changed source tests without building; pass exact source test paths to run them regardless of Git status.
|
||||
- `npm run test:focused -- PATH...`: run exact test files; build first for boundary, acceptance, or process-global tests.
|
||||
- `npm run check:changed`: run cached static checks and source tests affected since the branch merge base (default `origin/main`).
|
||||
- `npm run check:fast`: run cached typecheck and lint checks.
|
||||
- `npm run check:pr`: opt into the complete local deterministic gate and generated-document checks for broad changes; CI owns full coverage. Routine iterations need focused tests and relevant checks, not the whole gate each time.
|
||||
- `npm run docs:check`: check committed generated documents; `npm run docs:generate` regenerates them.
|
||||
- For provider-dependent changes, see [docs/testing.md](docs/testing.md) and run the matching real-provider verification.
|
||||
- Keep each verification lane's prerequisites limited to the claim it checks. Use host-native fixtures for host/provider acceptance; put optional cross-target formats and their external toolchains in a separate lane. Preflight required commands and report the missing dependency and lane clearly.
|
||||
|
||||
Pre-commit hooks via Husky format then lint staged source files. Pre-push runs `npm run check:fast`; CI owns the broader static and generated-document gates. Use `npm run lint:fix` to auto-correct violations locally.
|
||||
See [docs/testing.md](docs/testing.md) for test scopes and verification lanes and [CONTRIBUTING.md](CONTRIBUTING.md) for contribution checks. Pre-commit formats and lints staged source; pre-push runs `npm run check:fast`.
|
||||
|
||||
## Configuration & Environment Variables
|
||||
|
||||
- `REA_ANALYSIS_PROVIDER` (optional, default `auto`): require one deep-analysis provider ID for startup and one-shot commands, or use deterministic automatic selection. A request-level `provider_id`/`--provider` takes precedence.
|
||||
- `GHIDRA_INSTALL_DIR` (optional): absolute root of an extracted official Ghidra 12.1.2 distribution. The adapter supports Linux x64 and the experimental Windows x64 P0 boundary documented in `docs/windows-ghidra-p0.md`.
|
||||
- `JAVA_HOME` (optional): absolute 64-bit full JDK 21 root used by Ghidra. When absent, doctor probes `java`/`javac` from `PATH`.
|
||||
- `REA_ILSPY_CMD_PATH` (optional): absolute path to a bring-your-own `ilspycmd`; doctor reports its configured version, and `verify:managed` can use it as a real reconstruction oracle without making ILSpy a setup-installed dependency or canonical parser.
|
||||
- `HOPPER_TARGET_PATH` (optional): absolute initial binary or `.hop` target. Target-free MCP sessions use `open_binary` instead.
|
||||
- `HOPPER_LAUNCHER_PATH` (optional): override the Hopper executable path (defaults to `/Applications/Hopper Disassembler.app/Contents/MacOS/hopper`).
|
||||
- `HOPPER_TARGET_KIND` (optional, default `executable`): startup kind for `HOPPER_TARGET_PATH`; dynamic targets are classified from their paths and headers.
|
||||
- `HOPPER_LOADER_ARGS_JSON` (optional): JSON array overriding derived Hopper loader arguments for supported executable targets, e.g. `["-l","Mach-O","--aarch64"]`.
|
||||
- `REA_BROWSER_OBSERVE_ENABLED` (optional, default `false`): add browser observation authority to the administrator ceiling.
|
||||
- `REA_BROWSER_CDP_ENDPOINTS_JSON` (optional): approved literal loopback CDP HTTP endpoints.
|
||||
- `REA_BROWSER_ALLOWED_ORIGINS_JSON` (optional): exact HTTP(S) page origins approved for passive observation.
|
||||
- `REA_BROWSER_SCENARIO_ENABLED` (optional, default `false`): add controlled browser automation to the administrator ceiling.
|
||||
- `REA_BROWSER_SCENARIO_AUTO_GRANT` (optional, default `false`): issue the exact configured browser-automation ceiling as an administrator grant for trusted unattended use.
|
||||
- `REA_BROWSER_SCENARIO_EXECUTABLE_ROOTS_JSON` (optional): canonical executable roots approved for provider-owned browser launch.
|
||||
- `REA_BROWSER_SCENARIO_CDP_ENDPOINTS_JSON` (optional): literal-loopback CDP endpoints approved for external-browser attachment.
|
||||
- `REA_BROWSER_SCENARIO_ALLOWED_ORIGINS_JSON` (optional): exact HTTP(S) page origins approved for browser scenarios.
|
||||
- `REA_BROWSER_SCENARIO_ALLOWED_ENV_JSON` (optional): environment-variable names that scenario secret references may resolve.
|
||||
- `REA_ELECTRON_OBSERVE_ENABLED` (optional, default `false`): add passive Electron file-page observation authority to the administrator ceiling.
|
||||
- `REA_ELECTRON_CDP_ENDPOINTS_JSON` (optional): approved literal loopback Electron CDP HTTP endpoints.
|
||||
- `REA_ELECTRON_FILE_ROOTS_JSON` (optional): canonical filesystem roots approved for passive Electron page observation.
|
||||
- `REA_V8_INSPECTOR_OBSERVE_ENABLED` (optional, default `false`): add passive attach-only Node/Electron Inspector observation authority to the administrator ceiling.
|
||||
- `REA_V8_INSPECTOR_ENDPOINTS_JSON` (optional): approved literal-loopback Node/Electron Inspector HTTP endpoints.
|
||||
- `REA_V8_INSPECTOR_FILE_ROOTS_JSON` (optional): canonical filesystem roots approved for Inspector targets and script locations.
|
||||
- `REA_V8_INSPECTOR_ALLOWED_ORIGINS_JSON` (optional): exact HTTP(S) origins approved for Electron renderer Inspector targets and scripts.
|
||||
- `REA_INVESTIGATION_INPUT_ROOTS_JSON` (optional): canonical filesystem roots approved for static JavaScript/Electron application analysis and other investigation inputs.
|
||||
- `REA_JAVASCRIPT_REPLAY_ENABLED` (optional, default `false`): add controlled extracted-module execution authority to the administrator ceiling.
|
||||
- `REA_JAVASCRIPT_REPLAY_ROOTS_JSON` (optional): exact canonical source roots approved for controlled replay.
|
||||
- `REA_JAVASCRIPT_REPLAY_NODE_PATH`, `REA_JAVASCRIPT_REPLAY_BWRAP_PATH`, `REA_JAVASCRIPT_REPLAY_SYSTEMD_RUN_PATH`, `REA_JAVASCRIPT_REPLAY_SYSTEMCTL_PATH`, and `REA_JAVASCRIPT_REPLAY_SHELL_PATH` (optional): absolute paths committed by each replay plan; defaults target the current Node runtime and standard Linux system executables.
|
||||
Configuration is parsed and validated by `src/config.ts` and `src/config/`. Keep user-facing setup and provider configuration in [README.md](README.md) and the relevant guide under `docs/`; do not maintain a second environment-variable catalogue here.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
Use ESM TypeScript, two-space indentation, and Prettier defaults. Keep compiler strictness intact (`strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). Use `camelCase` for values/functions, `PascalCase` for classes/types, and `UPPER_SNAKE_CASE` for constants. Parse unknown values at every MCP, environment, and subprocess boundary. Avoid `any`, unchecked casts, non-null assertions, import-time I/O, and floating promises. Exported APIs require concise JSDoc. Model expected failures with the tagged error algebra and `Result`, not broad exception wrappers.
|
||||
Use ESM TypeScript, two-space indentation, and the committed Oxfmt configuration. Keep compiler strictness intact (`strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). Use `camelCase` for values/functions, `PascalCase` for classes/types, and `UPPER_SNAKE_CASE` for constants. Parse unknown values at every MCP, environment, and subprocess boundary. Avoid `any`, unchecked casts, non-null assertions, import-time I/O, and floating promises. Exported APIs require concise JSDoc. Model expected failures with the tagged error algebra and `Result`, not broad exception wrappers.
|
||||
|
||||
## Boundary Contracts
|
||||
|
||||
Treat a boundary as a contract between the producer's actual representation and the consumer's required meaning. When implementing or auditing a boundary, trace the value through parsing, normalization, authorization, serialization, and the CLI/MCP result. Establish affected callers from their code paths; similar tool names or workflows do not prove that they share a schema or failure mode.
|
||||
|
||||
Keep portable evidence and scenario validation distinct from host-native execution checks. Absolute filesystem paths, file URLs, and HTTP paths have different semantics; do not substitute one platform's syntax for the domain concept. Interpret provider metadata according to its documented or observed producer behavior. When a transformation loses information, preserve the reported value and an explicit unknown rather than guessing a canonical identity.
|
||||
|
||||
Preserve meaningful failure reasons through application and adapter layers. Malformed input is distinct from an unsupported target, unavailable provider, or host operating-system permission denial. Diagnostics should identify the failed constraint and the target or lifecycle request it applies to. Recovery advice must address that reason and point to an available workflow; generic catches must not erase actionable validation details.
|
||||
|
||||
Leave meaningful target, action, capture, and output choices to the agent. A selected operation already expresses intent; do not require approval booleans or repeated permission declarations. Trace each setting to its consumer: remove ignored options and single-value confirmations, derive built-in lifecycle behavior, and supply defaults for omitted optional metadata. Report actual effects and limitations where they help interpret results rather than asking callers to restate them.
|
||||
|
||||
Verify the representation consumed at the next boundary. Internal validator success does not establish JSON Schema validity or client compatibility: validate advertised input and output schemas against their declared dialect after SDK conversion, and keep generated contracts aligned. Use representative producer data and regressions that exercise the failed behavior. Platform and provider support claims require the corresponding real workflow; package startup, capability probes, and mock transport tests establish only their narrower claims. Report unverified coverage explicitly.
|
||||
|
||||
Bind tool handlers to named contracts, preserving their exact input and output types. Catalog ordering is presentation metadata; changing that order must not select a different schema or operation for an existing handler.
|
||||
|
||||
## Designing MCP Tools
|
||||
|
||||
Start from the analyst question and desired result, not a provider API. Before adding a tool, inspect the existing contract and its nearest alternative.
|
||||
|
||||
Let agents compose experiments with ordinary commands, scripts, and local fixture servers. REA tools should perform the requested inspection or capture and return evidence. A custom orchestration language, replay engine, or separate prepare/execute plan needs an observed requirement that those primitives cannot satisfy. A plan-only tool without an executor does not establish runtime behavior; expose the useful inspection directly.
|
||||
|
||||
- Prefer reusable, composable primitives: inspect one explicit object or
|
||||
relationship and return evidence that can support different analyst
|
||||
questions. Compose primitives into a workflow only for a recurring outcome
|
||||
that benefits from joining evidence sources; keep one-application or
|
||||
business-domain interpretations out of general tool contracts.
|
||||
- Use **inspect/search** tools for facts about a target or candidate set; use **trace** tools for relationships; use **compare** tools for explicitly paired inputs.
|
||||
- Add a **workflow** when observed agent use shows a repeated sequence that REA can compose without losing analyst control. Return useful results inline so callers can choose their next action.
|
||||
- Use **observe/capture** only when runtime activity is required, and declare authority and lifecycle effects in the contract.
|
||||
- Extend an existing tool when intent and result contract are unchanged. Add a tool for a distinct analyst outcome or materially different authority.
|
||||
- Keep caller-facing names and results provider-neutral. Put engine-specific behavior in provider adapters and report each provider's exact coverage.
|
||||
- Keep results complete by default. Add a limit only when it follows from a real format, protocol, authority, or resource-safety constraint; explain truncation and unsupported facets. Keep observed, derived, inferred, and unknown results distinct.
|
||||
- Include artifact identity, source locations, Evidence references, actionable errors, and relevant limitations when they affect conclusions. Return the evidence needed for the next analysis inline rather than requiring a resource or identifier lookup.
|
||||
- Implement shared application workflows behind CLI and MCP adapters. Update canonical contracts and generated catalog together.
|
||||
|
||||
See [docs/tool-design.md](docs/tool-design.md) for the design checklist. When usability or tool selection changes, evaluate representative CLI/MCP tasks as well as schema and transport behavior.
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
Name tests `*.test.ts`. Use Vitest and production seams (`tests/fixtures/`) rather than module mocks. Domain tests assert pure behavior; adapter tests use fake launcher/socket seams; MCP tests connect with the client SDK version pinned in `package.json`. Preserve the canonical tool inventory defined by `TOOL_CONTRACTS` and verified through `CATALOG_IDENTITY` and generated product metadata. Cover malformed input, cancellation, timeouts, process exit, concurrency, limits, and clean shutdown. Real Hopper, Ghidra, browser, JavaScript replay, managed conformance, and any real managed-tool claims cannot be replaced by mocks; use the corresponding `verify:*` command.
|
||||
Name tests `*.test.ts`. Use Vitest and production seams (`tests/fixtures/`) rather than module mocks. Domain tests assert pure behavior; adapter tests use fake launcher/socket seams; MCP tests connect with the client SDK version pinned in `package.json`. Preserve the canonical tool inventory defined by `TOOL_CONTRACTS` and verified through `CATALOG_IDENTITY` and generated product metadata. Cover malformed input, cancellation, lifecycle cleanup, and actual format, protocol, host-permission, and target-identity boundaries. Do not add tests that merely freeze arbitrary caps or prescribed call sequences. Real Hopper, Ghidra, browser, managed conformance, and any real managed-tool claims cannot be replaced by mocks; use the corresponding `verify:*` command.
|
||||
|
||||
MCP tools are agent-computer interfaces. Optimize them first for successful agent use: distinct purposes, clear names and parameters, relevant result context, actionable errors, and realistic multi-step workflows. Validate tool-design changes with representative agent tasks and transcripts when possible; schema validity and transport success alone do not prove that a tool is easy for an agent to use.
|
||||
Keep tool catalogs complete and self-describing; prefer capability- and session-scoped availability over schema truncation. Serialized bytes alone do not measure agent usability or model context cost.
|
||||
|
||||
MCP tool catalogs must remain complete and self-describing. Prefer capability- and session-scoped tool advertisement over schema truncation. Never use serialized byte counts as a proxy for model-context cost, token use, tool usability, or agent performance. Measure bytes only when evaluating a named transport, storage, or protocol constraint. Any model-context or token-efficiency claim must identify the client and its actual model-facing projection and tokenizer, since clients may omit, transform, dereference, or otherwise reproject MCP schemas. Do not weaken names, descriptions, schemas, examples, errors, or result context to improve a byte-based metric.
|
||||
## Cursor Cloud specific instructions
|
||||
|
||||
Development requires Node.js 24.18.0 and npm 11.16.0 (`.nvmrc` and `packageManager`). The Cloud Agent image places an older `node` on `PATH` ahead of a normal install. Environment setup installs the pinned toolchain under `/usr/local` and prepends `/usr/local/bin` for login shells. Confirm `node -v` is `v24.18.0` before installing dependencies.
|
||||
|
||||
`npm ci` installs locked dependencies. `npm run build:cached` produces the CLI and MCP server. `npm run check:fast` is the pre-push typecheck and lint. Hopper, Ghidra, and IDA are optional bring-your-own providers. JavaScript analysis and the deterministic Vitest suites do not need them. `rea doctor` reports those engines as missing until they are configured.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
Use Conventional Commit subjects because Release Please derives versions and changelogs from them. Examples: `feat: add historical source import`, `fix(process): stop timers after exit`, and `docs: update architecture`. Use `!` or a `BREAKING CHANGE:` footer for breaking changes. Pull request titles must follow the same format because squash merges use the title as the release commit. Pull requests should describe contract or behavior changes, list verification commands, link issues, and include sanitized MCP examples when schemas change. State whether real Hopper/Ghidra verification was performed. Never commit binaries, Hopper or Ghidra project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts (e.g. `.codex/`).
|
||||
|
||||
<!-- BEGIN:turborepo-agent-rules -->
|
||||
|
||||
# This is NOT the Turborepo you know
|
||||
|
||||
Turborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p "require.resolve('turbo/package.json')"` from a workspace that depends on `turbo`.
|
||||
|
||||
Read `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.
|
||||
|
||||
This block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `"agentGuidance": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.
|
||||
<!-- END:turborepo-agent-rules -->
|
||||
|
||||
+746
@@ -1,5 +1,751 @@
|
||||
# Changelog
|
||||
|
||||
## [5.0.0](https://github.com/morluto/rea/compare/rea-agents-4.1.0...rea-agents-5.0.0) (2026-10-07)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **native:** preserve incomplete native UI captures ([#991](https://github.com/morluto/rea/issues/991))
|
||||
* **android:** Android analysis now requires a full JDK 17 or newer with its compiler module for the packaged Java source bridge.
|
||||
|
||||
### Features
|
||||
|
||||
* **apple:** project macOS bundle anatomy in application graphs ([88c940b](https://github.com/morluto/rea/commit/88c940b165d788a491e212ea93e00462c4dde6a2))
|
||||
* **apple:** project macOS bundle anatomy in application graphs ([dd3e7ae](https://github.com/morluto/rea/commit/dd3e7aef0d52c478f6cdf475501c42ebe7f5c97e))
|
||||
* **application:** wire Android and Apple inventory graph tools ([#852](https://github.com/morluto/rea/issues/852)) ([e0fd820](https://github.com/morluto/rea/commit/e0fd8207a1fb738bcc34f204b55c4fa154f5f02d))
|
||||
* **browser:** attribute native execution and listener sources ([#917](https://github.com/morluto/rea/issues/917)) ([b5afa5f](https://github.com/morluto/rea/commit/b5afa5f91eed9ae31d670aeab9bbf5112d790ec2))
|
||||
* **browser:** capture correlated network content and lifecycle evidence ([#797](https://github.com/morluto/rea/issues/797)) ([f9eaf05](https://github.com/morluto/rea/commit/f9eaf056ef58215094513176a70fabae0740081f))
|
||||
* **browser:** trace captured website module imports ([#879](https://github.com/morluto/rea/issues/879)) ([01be082](https://github.com/morluto/rea/commit/01be082e9e11c78b91828c06a9c543c754783c07))
|
||||
* **browser:** trace captured website source-map locations ([#897](https://github.com/morluto/rea/issues/897)) ([f430c41](https://github.com/morluto/rea/commit/f430c416c72d444fbe521e0263f8935da4cdb040))
|
||||
* **javascript:** recover readable modules with Wakaru ([#848](https://github.com/morluto/rea/issues/848)) ([166dcd2](https://github.com/morluto/rea/commit/166dcd21d06f687a9c480a6a6d36beb3b4954dd0))
|
||||
* **native:** decode chained fixups, binds, ObjC categories and properties ([a657557](https://github.com/morluto/rea/commit/a65755728f7a10d984514c75fa863b84ddcdaee9))
|
||||
* **native:** decode chained fixups, binds, ObjC categories and properties ([3e24a72](https://github.com/morluto/rea/commit/3e24a72465fe6c628db1f836e93d74d7f4a92b80))
|
||||
* **native:** open iOS-style and iOS-on-Mac wrapper app bundles ([2ed89e6](https://github.com/morluto/rea/commit/2ed89e6529924248d2e21084f8730bedbb16bc3f))
|
||||
* **native:** open iOS-style and iOS-on-Mac wrapper app bundles ([aeebba3](https://github.com/morluto/rea/commit/aeebba391ee5c288b75cd828e7d18d32b40aa698))
|
||||
* **web:** export captured scripts for JavaScript application analysis ([#801](https://github.com/morluto/rea/issues/801)) ([472b72a](https://github.com/morluto/rea/commit/472b72a53068a8e2ec78fe239d687023482daaa7))
|
||||
* **website:** add a shared back-to-top link ([5047373](https://github.com/morluto/rea/commit/504737300632d8644fbd6c658d2c97e38bb54c3b))
|
||||
* **website:** introduce REA and the DX-Ball reconstruction ([#906](https://github.com/morluto/rea/issues/906)) ([4e3953c](https://github.com/morluto/rea/commit/4e3953cc45ce9c21786a7a6fc495d2350aa85d24))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* address review nits from batch integration follow-ups ([#818](https://github.com/morluto/rea/issues/818)) ([5a0e0ba](https://github.com/morluto/rea/commit/5a0e0ba3b81ea0ec336addb07da98ec7bc0a35d3))
|
||||
* **android:** align runtime suffix inference ([#977](https://github.com/morluto/rea/issues/977)) ([fb3749a](https://github.com/morluto/rea/commit/fb3749aff692a7a8225dff00a4516b48a76866b6))
|
||||
* **android:** bound fragmented JADX MCP frame memory ([#937](https://github.com/morluto/rea/issues/937)) ([9e39795](https://github.com/morluto/rea/commit/9e397958951edb543134e3ad57e7ab253d6df21a))
|
||||
* **android:** preserve JVM settings and reuse metadata sessions ([#908](https://github.com/morluto/rea/issues/908)) ([eab34a9](https://github.com/morluto/rea/commit/eab34a9d8c5eebad6b04b85750be0276d679c3f9))
|
||||
* **apple:** attribute only application-root components in macOS inventories ([5524970](https://github.com/morluto/rea/commit/55249704c5c83cbf8d20ac7869863cc196da00a6))
|
||||
* **apple:** count only real framework version directories ([2e81e86](https://github.com/morluto/rea/commit/2e81e867b24da8314f666f62d154de5803040cdf))
|
||||
* **apple:** pair bridge hypotheses within one application root ([7acf4e7](https://github.com/morluto/rea/commit/7acf4e741a6a314ea7aabb18fc757e196158ff76))
|
||||
* **apple:** pair bridge hypotheses within one application root ([9226ade](https://github.com/morluto/rea/commit/9226ade5114f287eb8e1b00b08dd91da1cc66003))
|
||||
* **apple:** traverse keyed archive hierarchy collections ([#976](https://github.com/morluto/rea/issues/976)) ([71ac83f](https://github.com/morluto/rea/commit/71ac83f3dd376135098f460c86e7897183456ec5))
|
||||
* **artifacts:** accept APFS image disks ejected with their container ([d93ebec](https://github.com/morluto/rea/commit/d93ebec9df951d40e66ced667f07fadd2dd5d25a))
|
||||
* **artifacts:** accept APFS image disks ejected with their container ([b72c079](https://github.com/morluto/rea/commit/b72c079a9a90c374bd6dd30ff8c4387306b8f59b))
|
||||
* **artifacts:** accept the bundle subject when extracting an app bundle ([#891](https://github.com/morluto/rea/issues/891)) ([37d2eba](https://github.com/morluto/rea/commit/37d2ebac4c2a524eec6e32c4c57cbbb632491ad8))
|
||||
* **artifacts:** decode flat keyed-archive NIBs with data and shared references ([b3f013f](https://github.com/morluto/rea/commit/b3f013fa183e8351319a053575aeee5df337c276))
|
||||
* **artifacts:** decode flat keyed-archive NIBs with data and shared references ([2a4667b](https://github.com/morluto/rea/commit/2a4667bb79117b202abb009c0bb9ebfdfde57158))
|
||||
* **artifacts:** harden reader lifecycle boundaries ([#951](https://github.com/morluto/rea/issues/951)) ([2366bda](https://github.com/morluto/rea/commit/2366bda37b1084016d42a0d06920dc57e7c7723f))
|
||||
* **artifacts:** keep the post-snapshot detach check out of provenance ([62634e4](https://github.com/morluto/rea/commit/62634e4c0177bde8a08decc356da98fbe25d2000))
|
||||
* **artifacts:** preserve reader failure diagnostics ([#987](https://github.com/morluto/rea/issues/987)) ([fc4a9a0](https://github.com/morluto/rea/commit/fc4a9a05922ce8474fd0717bc800343b0a95bd41))
|
||||
* **artifacts:** project compiled AppKit actions from the control to its target ([b08228d](https://github.com/morluto/rea/commit/b08228d19f8a0f6dbc940e8d322ef2bb42d2ce1a))
|
||||
* **artifacts:** project compiled AppKit actions from the control to its target ([#905](https://github.com/morluto/rea/issues/905)) ([900d7c8](https://github.com/morluto/rea/commit/900d7c8606cbc1ccbca818827a55bf2eab28bca4))
|
||||
* **artifacts:** project UIKit runtime outlet and event connections ([aa8082e](https://github.com/morluto/rea/commit/aa8082ea6c0898feb4d842135aea6b9539f769f8))
|
||||
* **artifacts:** project UIKit runtime outlet and event connections ([8b24387](https://github.com/morluto/rea/commit/8b243877c22a5550d005e7b8abf93791ce8566b9))
|
||||
* **artifacts:** read AppKit connectors from flat keyed-archive NIBs ([#900](https://github.com/morluto/rea/issues/900)) ([27c272b](https://github.com/morluto/rea/commit/27c272bd9a42e7235c7991881196c60dbc126714))
|
||||
* **artifacts:** read keyed archive dictionaries keyed UID as dictionaries ([cfd2b0c](https://github.com/morluto/rea/commit/cfd2b0c75dfb9d51b936788a46d0338145803ece))
|
||||
* **artifacts:** read keyed archive dictionaries keyed UID as dictionaries ([35c82b9](https://github.com/morluto/rea/commit/35c82b93eb2579687bb446c3a8a2a9f1a7563d28))
|
||||
* **artifacts:** report which path constraint failed ([#847](https://github.com/morluto/rea/issues/847)) ([3f1a77f](https://github.com/morluto/rea/commit/3f1a77f68ec4355d8b792ccb2970bc03485889c9))
|
||||
* **browser:** distinguish computed call keys from identifiers ([#808](https://github.com/morluto/rea/issues/808)) ([02beb74](https://github.com/morluto/rea/commit/02beb745358f02bdaabed4bb523c5e3527a9841e))
|
||||
* **browser:** distinguish WebMCP registration frame owners ([#813](https://github.com/morluto/rea/issues/813)) ([b3b44d8](https://github.com/morluto/rea/commit/b3b44d8a2d8b2aa70a8ed8b9cddf365865fcc53f))
|
||||
* **browser:** fingerprint cookies at the selected page URL ([#763](https://github.com/morluto/rea/issues/763)) ([78014e3](https://github.com/morluto/rea/commit/78014e3d149f762c848581a0aac7e44f75d51225))
|
||||
* **browser:** identify NodeNext TypeScript original source artifacts ([#811](https://github.com/morluto/rea/issues/811)) ([e84ad86](https://github.com/morluto/rea/commit/e84ad86a7578fb46568663aca6fe6a6f3986cc8d))
|
||||
* **browser:** preserve evidence through deep AST analysis ([#924](https://github.com/morluto/rea/issues/924)) ([f826aa8](https://github.com/morluto/rea/commit/f826aa893fa7d55e87d907f0c94c39421a166dd8))
|
||||
* **browser:** preserve form destinations and satisfies mutation uncertainty ([#825](https://github.com/morluto/rea/issues/825)) ([6dd719e](https://github.com/morluto/rea/commit/6dd719eaf605b37cd5dad6d01d33527ca9d59f06)), closes [#820](https://github.com/morluto/rea/issues/820)
|
||||
* **browser:** preserve Link header value delimiters ([#805](https://github.com/morluto/rea/issues/805)) ([dd36405](https://github.com/morluto/rea/commit/dd3640548fe3d1e8b92784a63fac55d757dcc1f0))
|
||||
* **build:** validate installed dependencies before cache hits ([#982](https://github.com/morluto/rea/issues/982)) ([a45fe86](https://github.com/morluto/rea/commit/a45fe86a39b9e7a7c21e3665b6de4763cb72d782))
|
||||
* capture IndexedDB object-store record fingerprints ([#809](https://github.com/morluto/rea/issues/809)) ([21a8bd0](https://github.com/morluto/rea/commit/21a8bd0ccde2326c8bc022714a88a438d8b89dde))
|
||||
* **cli:** align structured output guard with native flags ([#777](https://github.com/morluto/rea/issues/777)) ([e9fac56](https://github.com/morluto/rea/commit/e9fac56232e74c8287863ca5c4cc7d8e39fb4a40))
|
||||
* **cli:** emit empty structured filter projections ([#914](https://github.com/morluto/rea/issues/914)) ([2db668f](https://github.com/morluto/rea/commit/2db668f6ca91d2514a354fd459239b84031812e7))
|
||||
* **cli:** honor the selected native plist path ([#773](https://github.com/morluto/rea/issues/773)) ([0374865](https://github.com/morluto/rea/commit/0374865836aa4960657fb92f588c71e35dd3bed8))
|
||||
* **cli:** preserve output and comparison input validation ([#981](https://github.com/morluto/rea/issues/981)) ([d5c43d6](https://github.com/morluto/rea/commit/d5c43d65f0f6a1de8fb2eb5f2c1940a6ea2af20c))
|
||||
* **cli:** preserve unsupported historical-source imports ([#795](https://github.com/morluto/rea/issues/795)) ([911d580](https://github.com/morluto/rea/commit/911d58063477912042da5e9e573a9e8008956622))
|
||||
* **contracts:** align CLI/MCP boundary contracts ([#946](https://github.com/morluto/rea/issues/946)) ([4a60738](https://github.com/morluto/rea/commit/4a6073810a955d058c10f209157cbd68832a92c5))
|
||||
* **deps:** remediate smol-toml advisories ([93091ab](https://github.com/morluto/rea/commit/93091abb4dbd3cceee217d179db816895df24d70))
|
||||
* **deps:** remediate smol-toml advisories ([c55ae3b](https://github.com/morluto/rea/commit/c55ae3b9eccc17e6e8f4369e13044995d0650704))
|
||||
* **dotnet:** compare members with undecoded signatures by exact identity ([28fd49f](https://github.com/morluto/rea/commit/28fd49f0f6cf54e373411f5999e6b5722dd826f5))
|
||||
* **dotnet:** compare members with undecoded signatures by exact identity ([d52af3b](https://github.com/morluto/rea/commit/d52af3b321a321964598fb302edeb26552c9dffd))
|
||||
* **dotnet:** inspect native boundaries of non-managed and malformed PEs ([d5f28fb](https://github.com/morluto/rea/commit/d5f28fb29c5c3e8acef940fcae042e660e3a4d72))
|
||||
* **dotnet:** inspect native boundaries of non-managed and malformed PEs ([d98dcfa](https://github.com/morluto/rea/commit/d98dcfa8c2e46a7cc918aeb01058c26c4a84060c))
|
||||
* **dotnet:** keep a decoded member unknown beside an undecoded counterpart ([76b7020](https://github.com/morluto/rea/commit/76b70203d941ab85c1772f330407fd832d438c9c))
|
||||
* **dotnet:** keep admitted CLI header facts when metadata is unreadable ([e5ba693](https://github.com/morluto/rea/commit/e5ba6934e4ff819c09077ca70bd2417d37f85bc0))
|
||||
* **dotnet:** name the shared key in exact-signature ambiguities ([7714e76](https://github.com/morluto/rea/commit/7714e763d9ac2e87994068b3dee37ca6c5ffc6f4))
|
||||
* **dotnet:** treat ambiguous undecoded members as possible counterparts ([73370e1](https://github.com/morluto/rea/commit/73370e1c064899753988581c9a821e0e7f579561))
|
||||
* **errors:** report union input issues at the union's path ([bddeb63](https://github.com/morluto/rea/commit/bddeb634b583cf4f7d5fa6cf3a1ee821e05d2923))
|
||||
* **evaluation:** canonicalize Unicode argument keys ([#980](https://github.com/morluto/rea/issues/980)) ([0a4caf4](https://github.com/morluto/rea/commit/0a4caf4a0a1355b6a121fc88886b22e55843ac76))
|
||||
* **firmware:** accept the verified Binwalk and Unblob release lines ([#844](https://github.com/morluto/rea/issues/844)) ([bef4321](https://github.com/morluto/rea/commit/bef4321afb3d0d480e8a04ad7e256663542caa3d))
|
||||
* **firmware:** retain multi-file extraction failures ([#978](https://github.com/morluto/rea/issues/978)) ([8aaf9a1](https://github.com/morluto/rea/commit/8aaf9a19788beb6612332505850ad3407cb00027))
|
||||
* **ghidra:** admit the 12.1 line and the install's JDK range ([#840](https://github.com/morluto/rea/issues/840)) ([5cbf06c](https://github.com/morluto/rea/commit/5cbf06c7ad0ace599721bffd7da566b8a790b622))
|
||||
* **ghidra:** select exact packaged bridge scripts ([#800](https://github.com/morluto/rea/issues/800)) ([894c30e](https://github.com/morluto/rea/commit/894c30ef02d946f0c8de3ae6c9168d9525d95033))
|
||||
* **ida:** share concurrent session cleanup ([#950](https://github.com/morluto/rea/issues/950)) ([f14b6a8](https://github.com/morluto/rea/commit/f14b6a84306116716e78c5ffc84353ac07939593))
|
||||
* **installer:** validate exact semantic versions ([#954](https://github.com/morluto/rea/issues/954)) ([248972f](https://github.com/morluto/rea/commit/248972f327776687037dc07099117584c0674e77))
|
||||
* integrate 24 reviewed browser/native/semantics corrections ([#815](https://github.com/morluto/rea/issues/815)) ([3af3811](https://github.com/morluto/rea/commit/3af3811ab0a212b6aab6185628d47ecad37958fc))
|
||||
* **javascript:** advertise the exact empty seed rule and match empty API keys ([35e5508](https://github.com/morluto/rea/commit/35e5508fd095a062d7477bd6bb7d2d8e28201cd8))
|
||||
* **javascript:** analyze source maps with empty source names ([58ef8ac](https://github.com/morluto/rea/commit/58ef8ac3aa803d7b64dd12a5b2a2e8ec24261060))
|
||||
* **javascript:** analyze sources containing empty string literals ([e7cbfe0](https://github.com/morluto/rea/commit/e7cbfe04fdfd7affe50dba9e2c7a9a0a72291041))
|
||||
* **javascript:** analyze sources containing empty string literals ([11fc3b9](https://github.com/morluto/rea/commit/11fc3b981509e8107b10b2e75363f5bb489879db))
|
||||
* **javascript:** filter keyed-collection receivers only for get/delete ([00f707b](https://github.com/morluto/rea/commit/00f707b250451246d69a1205c39c6843fab6bb5b))
|
||||
* **javascript:** handle deep AST traversal and addition without stack overflow ([#915](https://github.com/morluto/rea/issues/915)) ([62b3711](https://github.com/morluto/rea/commit/62b37111e7f09887eb712c6e8e721e0334cf10b9))
|
||||
* **javascript:** handle deep static member paths ([#939](https://github.com/morluto/rea/issues/939)) ([348c65c](https://github.com/morluto/rea/commit/348c65c7ece1aee280f9431d995996b444e7549d))
|
||||
* **javascript:** keep bare relative URLs for computed-method XHR ([e32028d](https://github.com/morluto/rea/commit/e32028d1b566872ba8cd08fc6800c198d6aef30b))
|
||||
* **javascript:** keep computed-method XHR request URLs ([602d002](https://github.com/morluto/rea/commit/602d0021d45a6cdab9f806554f3ee12ea670ec97))
|
||||
* **javascript:** keep exact empty env keys and method names ([522c2d0](https://github.com/morluto/rea/commit/522c2d0ecb1eb7be262eaef2926cceac9a68badc))
|
||||
* **javascript:** keep exact empty env keys and method names ([f7954a8](https://github.com/morluto/rea/commit/f7954a8fd692f262ef3ce1507b957681150ce9c4)), closes [#783](https://github.com/morluto/rea/issues/783)
|
||||
* **javascript:** keep exact empty require paths and request fields ([ad1f6b5](https://github.com/morluto/rea/commit/ad1f6b508fbdd44d8bf1884ed96ae69082808e7d)), closes [#783](https://github.com/morluto/rea/issues/783)
|
||||
* **javascript:** keep exact empty values in graph identities ([f608127](https://github.com/morluto/rea/commit/f608127635ed8e0d105ad95252e5ea0dff899f5b))
|
||||
* **javascript:** keep exact empty values in graph identities ([63012ee](https://github.com/morluto/rea/commit/63012eeb601fc74ca82f3a027d852a5da1b099d2))
|
||||
* **javascript:** keep keyed-collection reads out of endpoints ([9075ce7](https://github.com/morluto/rea/commit/9075ce71ada4334407aa2d194dc82e3031e30260))
|
||||
* **javascript:** keep keyed-collection reads out of endpoints ([eb68761](https://github.com/morluto/rea/commit/eb68761eb0a35b876e33dd6115d0d07f97e99dd5))
|
||||
* **javascript:** keep literal HTTP methods on storage-named receivers ([9f48a76](https://github.com/morluto/rea/commit/9f48a76e1186cc43c6928eff1c82e93280caee1a))
|
||||
* **javascript:** keep literal-method and URL-literal requests on aliased receivers ([e3e7664](https://github.com/morluto/rea/commit/e3e76646657e950e24241ba715eb97ea385933e9))
|
||||
* **javascript:** keep mutated object initializer values unknown ([#752](https://github.com/morluto/rea/issues/752)) ([6caa38a](https://github.com/morluto/rea/commit/6caa38a0cad369ddff52b08f8d7d44c1b92ecac0))
|
||||
* **javascript:** keep storage open versions out of XHR endpoints ([4f51103](https://github.com/morluto/rea/commit/4f5110306d9edab94bf63bf81cf259324d96af57))
|
||||
* **javascript:** keep storage open versions out of XHR endpoints ([898b0f6](https://github.com/morluto/rea/commit/898b0f62d76ea1cf793140cb255cad16ab85b44d))
|
||||
* **javascript:** keep XHR and client endpoints that receiver spellings hid ([05f43ed](https://github.com/morluto/rea/commit/05f43ed388ae4e9316d320b592b6eba5b119ce89))
|
||||
* **javascript:** leave empty application values unlabeled ([527b76e](https://github.com/morluto/rea/commit/527b76ef78e84d29bb41bd45f1f83fae05ca66ca))
|
||||
* **javascript:** match exact empty literal channels and seeds ([e20da1f](https://github.com/morluto/rea/commit/e20da1f205b105b1b6a5decd3c2d0fe36fd12410))
|
||||
* **javascript:** never read window or document open as XHR ([ae849a7](https://github.com/morluto/rea/commit/ae849a72811857ba9755962ec48a1d76c9d1181a))
|
||||
* **javascript:** preserve computed Electron member uncertainty ([3a4a63f](https://github.com/morluto/rea/commit/3a4a63f70a2e42fb1fa48881e08b08d612289f2e))
|
||||
* **javascript:** preserve computed Electron member uncertainty ([1260cac](https://github.com/morluto/rea/commit/1260cacc67b8e0c93fb9434452053feca956e7e7))
|
||||
* **javascript:** project decoded file URL paths ([#810](https://github.com/morluto/rea/issues/810)) ([2572e1e](https://github.com/morluto/rea/commit/2572e1eb2f853a4cebfc8303d9e293e1d106d608))
|
||||
* **javascript:** read open() endpoints only from XHR method/URL pairs ([63e0822](https://github.com/morluto/rea/commit/63e0822cc794bd6ec1688340b3230ffb6b5c4a8c))
|
||||
* **javascript:** read open() endpoints only from XHR method/URL pairs ([1be3705](https://github.com/morluto/rea/commit/1be370543f7ea39016f86ebdec80a8ceaf7f8a28))
|
||||
* **javascript:** read substitution-free templates as exact strings ([c371d6e](https://github.com/morluto/rea/commit/c371d6e1e9cceb28e6cfc874e14c4e740bb87751))
|
||||
* **javascript:** read substitution-free templates as exact strings ([77cf757](https://github.com/morluto/rea/commit/77cf75710369acc6c304ec352ab25aa7274e04b1))
|
||||
* **javascript:** resolve bare source-map and worker URLs next to the script ([#890](https://github.com/morluto/rea/issues/890)) ([45fcd28](https://github.com/morluto/rea/commit/45fcd28b4018723bdb508a9229b74ab9eb749f75))
|
||||
* **javascript:** retain recovered call argument positions ([#929](https://github.com/morluto/rea/issues/929)) ([e6ba9c3](https://github.com/morluto/rea/commit/e6ba9c3af3053485f154825eea6a156127328be9))
|
||||
* **linux:** improve Hopper reliability and CachyOS support ([#963](https://github.com/morluto/rea/issues/963)) ([98b2b02](https://github.com/morluto/rea/commit/98b2b027afd8c80b68e9daaf97bdaa4d942105cc))
|
||||
* **mcp:** advertise reserved process environment key ([#949](https://github.com/morluto/rea/issues/949)) ([e0d76ae](https://github.com/morluto/rea/commit/e0d76ae80ddc70ae4d5afcbd1427afb81eee3669))
|
||||
* **mcp:** isolate optional observation adapter failures ([#838](https://github.com/morluto/rea/issues/838)) ([be3cfbf](https://github.com/morluto/rea/commit/be3cfbffdfbbb65da75dcec0518e2ebf387e66bc))
|
||||
* **mcp:** preserve exact guided prompt context ([#761](https://github.com/morluto/rea/issues/761)) ([66c26f2](https://github.com/morluto/rea/commit/66c26f280b1d494806ee182b52564166760f0ae2))
|
||||
* **native-ui:** handle incomplete accessibility child collections ([#944](https://github.com/morluto/rea/issues/944)) ([ac3bae3](https://github.com/morluto/rea/commit/ac3bae3cb97c4fb622eb1b6ac1f3b84e6a70ba03))
|
||||
* **native:** inspect plists containing data, dates, and non-finite reals ([bc2cd8b](https://github.com/morluto/rea/commit/bc2cd8b874e115eee446860043758a80bd583ad0))
|
||||
* **native:** inspect plists containing data, dates, and non-finite reals ([5ead2cf](https://github.com/morluto/rea/commit/5ead2cfbc47896b43502363d3568330f5d94762e))
|
||||
* **native:** keep echoed codesign paths out of signature fields ([#865](https://github.com/morluto/rea/issues/865)) ([4aa2b60](https://github.com/morluto/rea/commit/4aa2b60c1d84607a138395ae93260b2e50c2a721))
|
||||
* **native:** keep echoed Mach-O paths out of load commands and symbols ([ab985c6](https://github.com/morluto/rea/commit/ab985c6a10e9da856ed151100cee8459793c3573))
|
||||
* **native:** keep echoed Mach-O paths out of load commands and symbols ([37fe105](https://github.com/morluto/rea/commit/37fe105ba168488d05226f2912bed802e2d48d75))
|
||||
* **native:** keep integral plist reals beyond the JSON range as reals ([#860](https://github.com/morluto/rea/issues/860)) ([3b92a2b](https://github.com/morluto/rea/commit/3b92a2bceddaa1659654da08160813a3affbc6b7))
|
||||
* **native:** keep plist XML element types for large numbers ([f85056d](https://github.com/morluto/rea/commit/f85056d83e42a461c8c947792774580294d0ceb9))
|
||||
* **native:** keep the resolved bundle plist and permission failures ([418a556](https://github.com/morluto/rea/commit/418a5562f6f640503c751924a4787586c2c5c98a))
|
||||
* **native:** pass Swift symbols to swift-demangle as operands ([dd90965](https://github.com/morluto/rea/commit/dd90965058a9e6f7dd7f30eb7e462572e777db7f))
|
||||
* **native:** pass Swift symbols to swift-demangle as operands ([6c88094](https://github.com/morluto/rea/commit/6c88094da45258b346fe594764e561f999622a58))
|
||||
* **native:** preserve incomplete native UI captures ([#991](https://github.com/morluto/rea/issues/991)) ([9f496ef](https://github.com/morluto/rea/commit/9f496ef2d4a3aa68816287bf5d2ba1620d72c97a))
|
||||
* **native:** report exact plist integers beyond the JSON number range ([c9f49db](https://github.com/morluto/rea/commit/c9f49db907db0513ad8b22c9b2bbdcee400aff11))
|
||||
* **native:** report exact plist integers beyond the JSON number range ([430c297](https://github.com/morluto/rea/commit/430c297ca4ddb3491c2b835b889486e6a2ffdc17))
|
||||
* **native:** report plist permission denials as access failures ([5738a0c](https://github.com/morluto/rea/commit/5738a0cf8c111833847fecb2f7d0c2ee35eabbd6))
|
||||
* **native:** report why a selected plist cannot be inspected ([14f9b3d](https://github.com/morluto/rea/commit/14f9b3d93119a60a3cf5f3d9c138ca829a5462e8))
|
||||
* **native:** report why a selected plist cannot be inspected ([0674fac](https://github.com/morluto/rea/commit/0674facd468c3321584ece908031feee7220b9c6))
|
||||
* **plist:** keep character references beyond Unicode literal ([5afd8a8](https://github.com/morluto/rea/commit/5afd8a850b2ebd62e3400a5e2ece1a1cfa98bab6))
|
||||
* **plist:** read XML plists containing a __proto__ key ([faaee69](https://github.com/morluto/rea/commit/faaee693d5bb4bb957387ebd0f4338513c4c914a))
|
||||
* **plist:** read XML plists containing a __proto__ key ([a08be51](https://github.com/morluto/rea/commit/a08be51e9f0d23bd46b66bbdb88dbda44bc54c88))
|
||||
* **plist:** report omitted __proto__ entries in signatures and Interface Builder ([15ab5db](https://github.com/morluto/rea/commit/15ab5dbcaf3fac09756e98926373965247dad622))
|
||||
* **plist:** report omitted __proto__ entries in signatures and Interface Builder ([6729ee7](https://github.com/morluto/rea/commit/6729ee7636889a0a2ebc9fca8e35860476f8ca34))
|
||||
* preserve native UI failure reasons and dedupe version literals ([#854](https://github.com/morluto/rea/issues/854)) ([2d4a910](https://github.com/morluto/rea/commit/2d4a910fcd30e8d5b96fcb957b931e9aa3e4ab0c))
|
||||
* **process:** clarify Windows capture support and recovery ([#798](https://github.com/morluto/rea/issues/798)) ([50787b2](https://github.com/morluto/rea/commit/50787b2faf1a4bea799d041960ca2b91199ef403))
|
||||
* **process:** harden filesystem snapshot identity and cancellation ([#956](https://github.com/morluto/rea/issues/956)) ([93aa9eb](https://github.com/morluto/rea/commit/93aa9eb5c2551984182a4025d836e4223e8ecc8a))
|
||||
* **process:** retain provider output until streams close ([#928](https://github.com/morluto/rea/issues/928)) ([b929251](https://github.com/morluto/rea/commit/b9292516f4eedc2dedba713abf57f665ef1fb596))
|
||||
* **reference:** retain partial imports for unreadable directories ([#766](https://github.com/morluto/rea/issues/766)) ([cffc548](https://github.com/morluto/rea/commit/cffc548cd3127c5db811f20db4816039846b3170))
|
||||
* **release:** drive checkpoint releases from frozen branches ([fc58fa1](https://github.com/morluto/rea/commit/fc58fa1eb01fe5a8200a78e9551799c9c1a3d256))
|
||||
* **release:** publish from explicit source checkpoints ([49887f0](https://github.com/morluto/rea/commit/49887f0239da4483153a5e1610058619855525db))
|
||||
* **semantics:** isolate named function expression bindings ([#776](https://github.com/morluto/rea/issues/776)) ([ae81f64](https://github.com/morluto/rea/commit/ae81f64d2ad10e5cdbf8356819351c02480aee7a))
|
||||
* **semantics:** preserve direct promise ownership boundaries ([#758](https://github.com/morluto/rea/issues/758)) ([e1ca412](https://github.com/morluto/rea/commit/e1ca412757bb01be855a66bf72006e3becd497b6))
|
||||
* **semantics:** preserve empty string property keys ([#764](https://github.com/morluto/rea/issues/764)) ([630bdaf](https://github.com/morluto/rea/commit/630bdafcacc621105f6a43ed68a6e51651aca629))
|
||||
* **semantics:** recognize Node builtin default namespaces ([#769](https://github.com/morluto/rea/issues/769)) ([24a0bf9](https://github.com/morluto/rea/commit/24a0bf9a618604e2fa30817d673ce911c7842819))
|
||||
* **semantics:** retain trailing rest argument flows ([#756](https://github.com/morluto/rea/issues/756)) ([4bfd803](https://github.com/morluto/rea/commit/4bfd8038be0a60c43a452bec15e23799a0b040c0))
|
||||
* **setup:** accept the pinned Hopper RPM launcher ([#933](https://github.com/morluto/rea/issues/933)) ([dda8e6a](https://github.com/morluto/rea/commit/dda8e6a72890f87bdf8bbd3250b68b6648aae553))
|
||||
* **setup:** read an OpenCode V2 server named type as a table entry ([#866](https://github.com/morluto/rea/issues/866)) ([4e17be5](https://github.com/morluto/rea/commit/4e17be57053fb0ce2561d92e05b6e38ef789b2d4))
|
||||
* **setup:** register OpenCode V2 configurations in mcp.servers ([957069d](https://github.com/morluto/rea/commit/957069d14d832ddeac3f90dd141d379b81e2832a))
|
||||
* **setup:** register OpenCode V2 configurations in mcp.servers ([69d8afe](https://github.com/morluto/rea/commit/69d8afefc19e014a4bba7a182983a9e77e100a75))
|
||||
* **target:** decode app Info.plist executables with an XML parser ([c57f1b7](https://github.com/morluto/rea/commit/c57f1b74e0af5cf5fbb459cb247e1448cbf8df1b))
|
||||
* **target:** decode app Info.plist executables with an XML parser ([c182f72](https://github.com/morluto/rea/commit/c182f72ad3d9eea10b084a43527acb7c8ca2d6bf))
|
||||
* **test:** disconnect Hopper fixture IPC on SIGTERM ([b912b11](https://github.com/morluto/rea/commit/b912b117f019d5d3e4b4e3ef683e9b25f3a709bc))
|
||||
* **windows:** accept ordinary drive path separators ([#791](https://github.com/morluto/rea/issues/791)) ([b62931f](https://github.com/morluto/rea/commit/b62931f8335f58bfe344883a4f149e9eef73d835))
|
||||
* **windows:** remove cancelled snapshot outputs ([#957](https://github.com/morluto/rea/issues/957)) ([d76f7d1](https://github.com/morluto/rea/commit/d76f7d150895289aa1fb85a057b81ca7ff102df4))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **artifacts:** stream ASAR members without full buffering ([#962](https://github.com/morluto/rea/issues/962)) ([abd7a5e](https://github.com/morluto/rea/commit/abd7a5ed091061e4b1e83e5933e9009fb5b8d577))
|
||||
* **call-path:** prune dead ends and enumerate without recursion ([#909](https://github.com/morluto/rea/issues/909)) ([e52fe97](https://github.com/morluto/rea/commit/e52fe9719fec03f2aa79b1a1b9e11b38c229903c))
|
||||
* **cli:** defer catalog digests and use canonical cache scope ([9a15525](https://github.com/morluto/rea/commit/9a1552529889e9f5b52a88739eb767b92e57e24e))
|
||||
* **cli:** defer unused catalog work during command startup ([#920](https://github.com/morluto/rea/issues/920)) ([9a15525](https://github.com/morluto/rea/commit/9a1552529889e9f5b52a88739eb767b92e57e24e))
|
||||
* **comparison:** reuse canonical sort keys ([#990](https://github.com/morluto/rea/issues/990)) ([ebb065e](https://github.com/morluto/rea/commit/ebb065e2dc15f5763276f401521896f66bae6339))
|
||||
* **dotnet:** index declaring-type ranges once per metadata table ([#918](https://github.com/morluto/rea/issues/918)) ([7a9f587](https://github.com/morluto/rea/commit/7a9f587c3a9998d828b83388438976527ca7074f))
|
||||
* **dotnet:** index ordered declaring-type ranges once per table ([7a9f587](https://github.com/morluto/rea/commit/7a9f587c3a9998d828b83388438976527ca7074f))
|
||||
* **evidence:** scale imports and dependency graphs ([#941](https://github.com/morluto/rea/issues/941)) ([7dc40d3](https://github.com/morluto/rea/commit/7dc40d3d9e00caf013109942a5d9a1d7fdf9ec62))
|
||||
* **inspector:** bound runtime script location authorization ([#945](https://github.com/morluto/rea/issues/945)) ([58381ee](https://github.com/morluto/rea/commit/58381eefa24f73580e31eaa09dc14b9c04d78d6c))
|
||||
* **javascript:** analyze member chains without recursive copying ([#932](https://github.com/morluto/rea/issues/932)) ([dcde958](https://github.com/morluto/rea/commit/dcde9584508830a5d99ac5e2032f539563c69002))
|
||||
* **javascript:** index Promise return-site ownership ([#971](https://github.com/morluto/rea/issues/971)) ([2cedde9](https://github.com/morluto/rea/commit/2cedde9f1cf72b04bef37f9f004bb68d5ff6502a))
|
||||
* **javascript:** index semantic fingerprint relations ([#942](https://github.com/morluto/rea/issues/942)) ([c92a344](https://github.com/morluto/rea/commit/c92a344d4770125d85a11f3fc7a23d800bd30003))
|
||||
* **javascript:** reduce analysis and source-map work ([#955](https://github.com/morluto/rea/issues/955)) ([c7530f9](https://github.com/morluto/rea/commit/c7530f9a816d84354516d3ea137f4a0d91d3b8b3))
|
||||
* **providers:** assemble fragmented responses once per line ([#911](https://github.com/morluto/rea/issues/911)) ([df7151b](https://github.com/morluto/rea/commit/df7151ba305eab3a33a9261a023705b29862b308))
|
||||
* **reconstruction:** index coverage evaluation ([#983](https://github.com/morluto/rea/issues/983)) ([f26c8b5](https://github.com/morluto/rea/commit/f26c8b5b8cff10b93173359b5453874d659f2440))
|
||||
* **setup:** reuse initial diagnostics during planning ([#940](https://github.com/morluto/rea/issues/940)) ([118d43e](https://github.com/morluto/rea/commit/118d43e787e38857714852cc846b1b604a61950d))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* add incremental capability migration safeguards ([#819](https://github.com/morluto/rea/issues/819)) ([75f50d8](https://github.com/morluto/rea/commit/75f50d86f6d504f4dd97a707871d39ec022b3047))
|
||||
* **android:** group layered source and verification ([#873](https://github.com/morluto/rea/issues/873)) ([8e8921d](https://github.com/morluto/rea/commit/8e8921d40c63b40ab7e1598f672063dd304d93f0))
|
||||
* **apple:** group artifact producers and verification ([#952](https://github.com/morluto/rea/issues/952)) ([ef1a0cc](https://github.com/morluto/rea/commit/ef1a0cc65af7918ef7204a39f42a4299b486c453))
|
||||
* **artifacts:** isolate JavaScript file acquisition ([#916](https://github.com/morluto/rea/issues/916)) ([f0a68dc](https://github.com/morluto/rea/commit/f0a68dce60e81b4d2f420bf774b61957a37326ac))
|
||||
* **binary:** group session selection and snapshot ownership ([#886](https://github.com/morluto/rea/issues/886)) ([dd114d3](https://github.com/morluto/rea/commit/dd114d39801973e958a582c8fcba7ccce0e50299))
|
||||
* **binary:** move production composition outside application workflows ([#965](https://github.com/morluto/rea/issues/965)) ([33d4761](https://github.com/morluto/rea/commit/33d4761cc7a96630b7c2fed890df83d433c45914))
|
||||
* **browser:** align inspector exclusions with canonical vocabulary ([#834](https://github.com/morluto/rea/issues/834)) ([de9593a](https://github.com/morluto/rea/commit/de9593ad48c0b97507f18949b7ad86ad9c743ebc))
|
||||
* **cli:** split core analysis command registrar ([#858](https://github.com/morluto/rea/issues/858)) ([8b9940a](https://github.com/morluto/rea/commit/8b9940a7b5d3fcd5a8eda6d397f0ab7c3aaf2ac9))
|
||||
* colocate investigation record implementation ([#867](https://github.com/morluto/rea/issues/867)) ([bb19c94](https://github.com/morluto/rea/commit/bb19c9477ef3eefd9c5b2e6cabe78cc94e6779ae))
|
||||
* compose investigation record ownership ([#845](https://github.com/morluto/rea/issues/845)) ([ce73c3f](https://github.com/morluto/rea/commit/ce73c3fed2e5fd20ae0113579345a69763eb7655))
|
||||
* declare auxiliary capabilities from source ([#853](https://github.com/morluto/rea/issues/853)) ([4a6c17c](https://github.com/morluto/rea/commit/4a6c17ce83dfd20412f0e168525314a20c61169e))
|
||||
* **domain:** consolidate property-key extraction behind semanticStaticPropertyKey ([#823](https://github.com/morluto/rea/issues/823)) ([764ff05](https://github.com/morluto/rea/commit/764ff05231f1a666dfa655492da42b4b6ba742c3))
|
||||
* **domain:** converge comparison mechanics on shared vocabulary ([#824](https://github.com/morluto/rea/issues/824)) ([c13c9d4](https://github.com/morluto/rea/commit/c13c9d41f8441a711266adee21429fb7433a6aa1))
|
||||
* **domain:** extract artifact path syntax helpers ([#830](https://github.com/morluto/rea/issues/830)) ([ef535b8](https://github.com/morluto/rea/commit/ef535b81fefc358b0bdb655e5aa057fc3949ce5f))
|
||||
* **domain:** extract source-map envelope walk and disambiguate stringValue ([#828](https://github.com/morluto/rea/issues/828)) ([64ebdf4](https://github.com/morluto/rea/commit/64ebdf45016bda679674cbd2287576eadf66a1fc))
|
||||
* **domain:** map validation taxonomies and add inventory Result seam ([#833](https://github.com/morluto/rea/issues/833)) ([78f4848](https://github.com/morluto/rea/commit/78f4848d70ce462426e9f221d6e7b3e34219bf46))
|
||||
* **domain:** split Interface Builder keyed-archive parse from graph builder ([#859](https://github.com/morluto/rea/issues/859)) ([da09706](https://github.com/morluto/rea/commit/da0970672da725dbb095ffcaefad07303ae85aab))
|
||||
* **firmware:** group layered source and verification ([#878](https://github.com/morluto/rea/issues/878)) ([507e8b3](https://github.com/morluto/rea/commit/507e8b3c7be75f8de69be214618394aca3585404))
|
||||
* **ghidra:** extract createClient and load-image attestation ([#856](https://github.com/morluto/rea/issues/856)) ([b7df5b7](https://github.com/morluto/rea/commit/b7df5b78052539e57967db8e6562befd70c6ebdc))
|
||||
* give Inspector its own capability directory ([#869](https://github.com/morluto/rea/issues/869)) ([021ba69](https://github.com/morluto/rea/commit/021ba690b07eab4b990be47f90946db9f70e0fc0))
|
||||
* **javascript:** group application workflows and verification ([#896](https://github.com/morluto/rea/issues/896)) ([0804722](https://github.com/morluto/rea/commit/0804722cdf721a8d767d02f9342ebdf8d186f7e1))
|
||||
* **javascript:** group domain semantics and contracts ([#907](https://github.com/morluto/rea/issues/907)) ([f6a58f1](https://github.com/morluto/rea/commit/f6a58f10629614fb7a6970421218b73d41bce6a5))
|
||||
* **managed:** group capability workflows and verification ([#926](https://github.com/morluto/rea/issues/926)) ([28713f9](https://github.com/morluto/rea/commit/28713f9b4dcb882f0e811653890fde70cb5fdadb))
|
||||
* **native:** group analyst semantics workflows and contracts ([#973](https://github.com/morluto/rea/issues/973)) ([ea4db4c](https://github.com/morluto/rea/commit/ea4db4c9f7cbc10dc701c4d5770109c08952a5b8))
|
||||
* **native:** split Apple dispatch metadata decode facets ([#855](https://github.com/morluto/rea/issues/855)) ([676959e](https://github.com/morluto/rea/commit/676959ecb55b606a567af3d2eb3d3edc96f0aa2f))
|
||||
* **provider:** declare deep capabilities from source ([#892](https://github.com/morluto/rea/issues/892)) ([b808e2d](https://github.com/morluto/rea/commit/b808e2d6bbbae38c1ff2e293e1cf77464d889bdd))
|
||||
* share Android and firmware provider factories ([#826](https://github.com/morluto/rea/issues/826)) ([3ed59b2](https://github.com/morluto/rea/commit/3ed59b281247f2180b3c79e658078507e4cc1be2))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add concise lawful-use disclaimer ([#888](https://github.com/morluto/rea/issues/888)) ([4037c65](https://github.com/morluto/rea/commit/4037c6545dc8cc47f4602d8f06e5329ed641cdbb))
|
||||
* add DX-Ball reconstruction showcase ([#876](https://github.com/morluto/rea/issues/876)) ([895079d](https://github.com/morluto/rea/commit/895079dd093f02487789d9aa967f0cccbfe379d8))
|
||||
* add prominent website and showcase links to READMEs ([2ccc86c](https://github.com/morluto/rea/commit/2ccc86cd13d71f08b553d3b9962175144b882539))
|
||||
* add README star history chart ([#850](https://github.com/morluto/rea/issues/850)) ([11f27de](https://github.com/morluto/rea/commit/11f27deddf88703368942d3ddfc239a7ebe72e3e))
|
||||
* add website and showcase links to READMEs ([#930](https://github.com/morluto/rea/issues/930)) ([2ccc86c](https://github.com/morluto/rea/commit/2ccc86cd13d71f08b553d3b9962175144b882539))
|
||||
* **apple:** describe directory-based framework plist ambiguity ([6d74992](https://github.com/morluto/rea/commit/6d74992efed3271e8370c505afdde347609f8353))
|
||||
* audit and improve localized readmes translations and wording ([00a6ac3](https://github.com/morluto/rea/commit/00a6ac3de99f432bd6dbb88148a2aab5f8d3d338))
|
||||
* clarify NativeAOT scope and regenerate conformance ledger ([c945931](https://github.com/morluto/rea/commit/c945931961cbd488163c6d92ef91336ed506942e))
|
||||
* fix stale IDA roadmap item, mermaid paths, toolchain wording ([d2aed17](https://github.com/morluto/rea/commit/d2aed172ab79d98ff4e62c14d8f34cc49c1d17b4))
|
||||
* move quick start up and mark skill install optional ([554e9b1](https://github.com/morluto/rea/commit/554e9b1612f7799c2bc7177eb1a9b04a0f27d2e9))
|
||||
* note Cloud Agent Node.js toolchain ([#837](https://github.com/morluto/rea/issues/837)) ([6aa6350](https://github.com/morluto/rea/commit/6aa6350c800796d0953937e894ff176ae248e930))
|
||||
* publish VitePress documentation on GitHub Pages ([c78fe69](https://github.com/morluto/rea/commit/c78fe6990bcface6853ac5c11c90df6bc0df562d))
|
||||
* separate ambiguous and unavailable provider recovery ([c3f2b8e](https://github.com/morluto/rea/commit/c3f2b8e7742915dc27ab7c801a8a1a9127169896))
|
||||
* show task-scoped readiness and provider preference in the quick start ([880cca1](https://github.com/morluto/rea/commit/880cca1bca497bb53c97a821b470df0f2235e043))
|
||||
* show task-scoped readiness and provider preference in the quick start ([5fd0db3](https://github.com/morluto/rea/commit/5fd0db37d94f1c1a0495920c79e121d799da0d30)), closes [#726](https://github.com/morluto/rea/issues/726)
|
||||
* update release boundary to npm 4.1.0 with Windows native bundle ([69be514](https://github.com/morluto/rea/commit/69be514c67c8b2eb8b450355a9782d9237748fda))
|
||||
* **website:** add Notion case study and improve onboarding ([a6ba772](https://github.com/morluto/rea/commit/a6ba772c08e2648e6602787a0ea537d75d72802d))
|
||||
* **website:** add TH04 bullet-ring case study ([f8d84da](https://github.com/morluto/rea/commit/f8d84da127fad871050c098b004a503b36795ad0))
|
||||
* **website:** add worked guides and keep Pages publishing manual ([#925](https://github.com/morluto/rea/issues/925)) ([4f25ee3](https://github.com/morluto/rea/commit/4f25ee33dcd4195f8d877f8835f81c4e08c95235))
|
||||
* **website:** clarify guides and case studies ([5065fcd](https://github.com/morluto/rea/commit/5065fcd5205ec7c0d259131431c2cf54d9f69cf6))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **artifacts:** report compiled connector records when a storyboard route is missing ([d421d3f](https://github.com/morluto/rea/commit/d421d3f286ce515b5126859a0f9cac60667a168e))
|
||||
* **artifacts:** report observed action routes when a compiled route is missing ([c60b737](https://github.com/morluto/rea/commit/c60b737f7db8e2c9a1982cdb4357fcda1f47ff08))
|
||||
* **artifacts:** write the storyboard fixture's action in Cocoa syntax ([53bfa7e](https://github.com/morluto/rea/commit/53bfa7e3f792cdb9f2e95fc954f3616426414bcc))
|
||||
* eliminate fixture races and prune redundant scaffolding ([#849](https://github.com/morluto/rea/issues/849)) ([f72931d](https://github.com/morluto/rea/commit/f72931d9dd54609bfca422750fc2667f80af691c))
|
||||
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
* consolidate macOS native baseline checks ([#935](https://github.com/morluto/rea/issues/935)) ([810fe53](https://github.com/morluto/rea/commit/810fe53e799b89dc215c52c9245d36fb2a44d9c4))
|
||||
* verify native arm64 and Intel macOS workflows ([#898](https://github.com/morluto/rea/issues/898)) ([4801a24](https://github.com/morluto/rea/commit/4801a24089964860db7f4855be21d2d4313c7eab))
|
||||
|
||||
## [4.1.0](https://github.com/morluto/rea/compare/rea-agents-4.0.1...rea-agents-4.1.0) (2026-10-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **android:** integrate static APK analysis with headless JADX ([#738](https://github.com/morluto/rea/issues/738)) ([904d03e](https://github.com/morluto/rea/commit/904d03e987a6c498561b314a266202dee84b13ab))
|
||||
* **evidence:** accept exact retained application references ([#734](https://github.com/morluto/rea/issues/734)) ([d0f3480](https://github.com/morluto/rea/commit/d0f3480a3cb9887499c2b188c783dd1c4db09787))
|
||||
* **firmware:** integrate Binwalk and Unblob analysis ([5a7c769](https://github.com/morluto/rea/commit/5a7c769692c21efc4cc0e5ebf06a9f2534dc75ed))
|
||||
* **ghidra:** add atomic function annotation edits ([#709](https://github.com/morluto/rea/issues/709)) ([9bab553](https://github.com/morluto/rea/commit/9bab55337ab10616be6929afddb08fb19fbb8ab7))
|
||||
* **ghidra:** admit explicit DOS COM analysis ([#702](https://github.com/morluto/rea/issues/702)) ([5bcb664](https://github.com/morluto/rea/commit/5bcb6645b561e666b7e07dd8f38d2296fa7cbba8))
|
||||
* **ghidra:** enable Windows x64 read-only analysis ([#699](https://github.com/morluto/rea/issues/699)) ([26881da](https://github.com/morluto/rea/commit/26881da76aa320c8529fcc741df0fc9898d97bdc))
|
||||
* **ghidra:** integrate optional NativeAOT metadata recovery ([#748](https://github.com/morluto/rea/issues/748)) ([fb679b6](https://github.com/morluto/rea/commit/fb679b63c27e6c5f137532953cfa24a2ffb1aaf2))
|
||||
* **ghidra:** verify DOS load images and expose memory evidence ([#697](https://github.com/morluto/rea/issues/697)) ([a6a8f67](https://github.com/morluto/rea/commit/a6a8f67649cab558b0de11601e65425f280c7170))
|
||||
* **ida:** add read-only GUI and headless MCP providers ([#754](https://github.com/morluto/rea/issues/754)) ([8238232](https://github.com/morluto/rea/commit/82382324855a3a4ed51d2cdc3b2b455c9e8419e2))
|
||||
* **setup:** support Command Code MCP registration ([#645](https://github.com/morluto/rea/issues/645)) ([7f1ae42](https://github.com/morluto/rea/commit/7f1ae42f71ce85547d317eed5e62fa330d9876bb))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **android:** validate explicit Java runtime ([#742](https://github.com/morluto/rea/issues/742)) ([7390390](https://github.com/morluto/rea/commit/739039092f98c67e8e6ccfb1f8445e8f13ac8f15))
|
||||
* **artifacts:** make extraction output tree work on Windows ([#753](https://github.com/morluto/rea/issues/753)) ([c8e66c7](https://github.com/morluto/rea/commit/c8e66c704a70f5133922aa1bd1a3ba42959093ba))
|
||||
* **browser:** classify CSP host sources by their own origin ([#707](https://github.com/morluto/rea/issues/707)) ([1d15bcb](https://github.com/morluto/rea/commit/1d15bcb9ecd7efa487a8fd3f0d9755bd60051722))
|
||||
* **browser:** keep bare module locations unresolved ([#676](https://github.com/morluto/rea/issues/676)) ([8189081](https://github.com/morluto/rea/commit/8189081511441f0e3909b21b80c21c658ec9a453))
|
||||
* **ci:** upload Vitest blob reports from current path ([77799e6](https://github.com/morluto/rea/commit/77799e6c90ce36cd52e45b62c9cba2d46641c4cf))
|
||||
* **cli:** disable unmanaged skill synchronization ([433ebda](https://github.com/morluto/rea/commit/433ebda8b492f388faf6e6cac63ccc6f9c2d8d7e))
|
||||
* **cli:** disable unmanaged skill synchronization ([2c1bf92](https://github.com/morluto/rea/commit/2c1bf9201040ed3d60f475af9129b7bf85a508fa))
|
||||
* **cli:** fail when JSON input files cannot be parsed ([18c1829](https://github.com/morluto/rea/commit/18c18290d048b78a435f6864dfc778a9fea0af2e))
|
||||
* **cli:** fail when JSON input files cannot be parsed ([3201f05](https://github.com/morluto/rea/commit/3201f0538dc88e54bce00decf689bf9b5603bfd3))
|
||||
* **cli:** include build step in missing-runtime recovery ([6a37346](https://github.com/morluto/rea/commit/6a37346acb9829acaab0e2042624e6ea0d03e2a9))
|
||||
* **cli:** include build step in missing-runtime recovery ([ae72abd](https://github.com/morluto/rea/commit/ae72abd019b25347bfc7593b5dcadc5a2cbc3786))
|
||||
* **cli:** report reference-source import failures ([00eb03d](https://github.com/morluto/rea/commit/00eb03d0f2247da95dfa7bda0f41a0f366a25dd9))
|
||||
* **cli:** report reference-source import failures ([431e8eb](https://github.com/morluto/rea/commit/431e8ebfcfba2925904c0deb4888bbd3755b17bf))
|
||||
* **comparison:** break function collection collation ties ([c3d013b](https://github.com/morluto/rea/commit/c3d013b2fd5c3a32dfa73ef40dafdaeaf935f37e))
|
||||
* **comparison:** break function collection collation ties ([3aaf7e2](https://github.com/morluto/rea/commit/3aaf7e2c54029a456145d6e3287fd9122f3abbe3))
|
||||
* **comparison:** break JSON shape collation ties ([217e8cf](https://github.com/morluto/rea/commit/217e8cf59f49e2f68b394fbcbc87f914dbee97ee))
|
||||
* **comparison:** break JSON shape collation ties ([805c338](https://github.com/morluto/rea/commit/805c338f360184d73ae335a723fca656830d6328))
|
||||
* **comparison:** retain container-only export shape paths ([a55b5bb](https://github.com/morluto/rea/commit/a55b5bb8ee400ef5565954d57916e8aae7dac760))
|
||||
* **comparison:** retain container-only export shape paths ([7864d66](https://github.com/morluto/rea/commit/7864d665e15edec29ade0f3fb7ac374b8579c2c0))
|
||||
* **dotnet:** preserve leading Unicode content in metadata strings ([#667](https://github.com/morluto/rea/issues/667)) ([ddea063](https://github.com/morluto/rea/commit/ddea06360a09508a0ff695ab6e141eff19534619))
|
||||
* **electron:** respect computed option keys and overrides ([af48fe8](https://github.com/morluto/rea/commit/af48fe879161ee749692751b3e29ba06b63b7409))
|
||||
* **electron:** respect computed option keys and overrides ([98de338](https://github.com/morluto/rea/commit/98de3380e3062cb25778263bfea07b215a09b39c))
|
||||
* **evidence:** admit current process provider identity ([ab5a1c6](https://github.com/morluto/rea/commit/ab5a1c61b12475c554a5e4d60b48b642b2464bbb))
|
||||
* **evidence:** admit current process provider identity ([d311940](https://github.com/morluto/rea/commit/d311940f2414fe5cceb750de4464b681bb44ae1e))
|
||||
* **firmware:** validate configured executables ([#751](https://github.com/morluto/rea/issues/751)) ([9a4e6da](https://github.com/morluto/rea/commit/9a4e6da5a96ed2708a0f628b436dcd7415999327))
|
||||
* **ghidra:** preserve request and shutdown failure diagnostics ([#647](https://github.com/morluto/rea/issues/647)) ([19f8cdd](https://github.com/morluto/rea/commit/19f8cddfd6f283188567b8ff1e49b810d79952ee))
|
||||
* **ghidra:** refuse ambiguous universal Mach-O imports ([b1b687f](https://github.com/morluto/rea/commit/b1b687fb75325c0275a0cf161843fc5969ba1d47))
|
||||
* **ghidra:** refuse ambiguous universal Mach-O imports ([6582b24](https://github.com/morluto/rea/commit/6582b2433a955167e33246ad07de1a40bcb20144))
|
||||
* **hopper:** retain shutdown request diagnostics ([8d7071d](https://github.com/morluto/rea/commit/8d7071da4c61c5cfb4894be6536405affd90f276))
|
||||
* **hopper:** retain shutdown request diagnostics ([7254871](https://github.com/morluto/rea/commit/72548712fea972b920834663a5e2ceea4738ce1e))
|
||||
* integrate 14 approved fix PRs (668,671,673,675,678-681,685-690) ([#705](https://github.com/morluto/rea/issues/705)) ([cf06115](https://github.com/morluto/rea/commit/cf061159ab69a746675e48896fa953d6990c05f9))
|
||||
* **javascript:** count all source line terminators ([e60aa11](https://github.com/morluto/rea/commit/e60aa1110800e1309b482e7a44ecd1762bade992))
|
||||
* **javascript:** count all source line terminators ([03bc6d3](https://github.com/morluto/rea/commit/03bc6d36db94e70c709557cb7302d40af9e99e6b))
|
||||
* **javascript:** expose result schema failures ([6d1f39b](https://github.com/morluto/rea/commit/6d1f39b250f8a1c6f924b1a6b9d26d56dff4beee))
|
||||
* **javascript:** expose result schema failures ([99263a7](https://github.com/morluto/rea/commit/99263a720d7106aff059ad7fa43849151a94c963))
|
||||
* **javascript:** hash graph and evidence JSON incrementally ([#646](https://github.com/morluto/rea/issues/646)) ([9f42495](https://github.com/morluto/rea/commit/9f42495d11100adc8646de5c8b62762ab9715ae6))
|
||||
* **javascript:** preserve path.resolve reset semantics ([863ed47](https://github.com/morluto/rea/commit/863ed47de2e35ef9b734e45e3bb44ea9e9dc8ff6))
|
||||
* **javascript:** preserve path.resolve reset semantics ([4177377](https://github.com/morluto/rea/commit/417737748aa96ba38ced7b0b22dbb972187f2ee0))
|
||||
* **javascript:** preserve primitive addition coercion ([#666](https://github.com/morluto/rea/issues/666)) ([94cc847](https://github.com/morluto/rea/commit/94cc8475e0e9e24cd2e8345bd36a743a979e3620))
|
||||
* **javascript:** preserve unknown branches in primitive unions ([75a75cb](https://github.com/morluto/rea/commit/75a75cbb20aa9b2148f9fd0547f613eadda68edd))
|
||||
* **javascript:** preserve unknown branches in primitive unions ([9a2f395](https://github.com/morluto/rea/commit/9a2f3950286a01922be0b07b29c4aefee466ec1e))
|
||||
* **javascript:** read source-map directives only from comments ([#672](https://github.com/morluto/rea/issues/672)) ([7f1fa64](https://github.com/morluto/rea/commit/7f1fa64019e01a654ad0eb80b47d268a58f04e57))
|
||||
* **javascript:** scope candidate ambiguity to query traversal ([6ad62ce](https://github.com/morluto/rea/commit/6ad62cec6abf182a52cf9844145e147fb6399c8e))
|
||||
* **javascript:** scope candidate ambiguity to query traversal ([f94fc79](https://github.com/morluto/rea/commit/f94fc7967b2e5604c144d3638515caad6273a0c6))
|
||||
* **mcp:** retain shutdown failure diagnostics ([09c249d](https://github.com/morluto/rea/commit/09c249deb86068503611fc474e76018d5c2a4d0b))
|
||||
* **mcp:** retain shutdown failure diagnostics ([fd008cd](https://github.com/morluto/rea/commit/fd008cd8e0b8277e691efd8e69dc96ac8e188d9e))
|
||||
* **native:** inspect mixed-signature Mach-O slices ([1dccd0d](https://github.com/morluto/rea/commit/1dccd0dee04abfdc80e8b48e3f05fa39cccf0feb))
|
||||
* **native:** inspect mixed-signature Mach-O slices ([66f3cfa](https://github.com/morluto/rea/commit/66f3cfa594a86cbf1f21fc6d2eca1a82e236c896))
|
||||
* **native:** retain spaced symbols in dyld inventory ([#669](https://github.com/morluto/rea/issues/669)) ([fa02df9](https://github.com/morluto/rea/commit/fa02df9ad3bc3139ef10422dedffac88c4d51d36))
|
||||
* **native:** select concrete arm64e Mach-O slices ([f04445c](https://github.com/morluto/rea/commit/f04445c57c597a8312211139b75da706cb0c2a96))
|
||||
* **native:** select concrete arm64e Mach-O slices ([fd3282a](https://github.com/morluto/rea/commit/fd3282a87e40417cf3d7d6211743809f9bad2dc8))
|
||||
* **process:** honor disabled PID normalization in samples ([44b3627](https://github.com/morluto/rea/commit/44b3627ab0b32c42e545dd2fd095006d249a77f6))
|
||||
* **process:** honor disabled PID normalization in samples ([a4e5736](https://github.com/morluto/rea/commit/a4e57366dd75077bde07f25c7966204cedad929e))
|
||||
* **process:** scope comparison unknowns to their evidence ([#732](https://github.com/morluto/rea/issues/732)) ([9dd6cef](https://github.com/morluto/rea/commit/9dd6cef90abf89f9191bce942e31900870231968))
|
||||
* **reconstruction:** accept function comparison source parameters ([c3bc38b](https://github.com/morluto/rea/commit/c3bc38bf6c74d2175a4680530dcadd6cab0e7791))
|
||||
* **reconstruction:** accept function comparison source parameters ([7a96391](https://github.com/morluto/rea/commit/7a96391001780012f704f4ef5cb6597baa590b7d))
|
||||
* **reference:** bound Dockerfile language detection ([132a3a4](https://github.com/morluto/rea/commit/132a3a46be648b036ed36372a50d6e32a9e7f2e5))
|
||||
* **reference:** bound Dockerfile language detection ([53fa559](https://github.com/morluto/rea/commit/53fa55937f241fc9ef3091eb22314cc370c749b9))
|
||||
* **reference:** classify test suffixes before extensions ([e828acc](https://github.com/morluto/rea/commit/e828acce0e0bf3808773590cf2dbb0bc8227df7b))
|
||||
* **reference:** classify test suffixes before extensions ([8b228ae](https://github.com/morluto/rea/commit/8b228ae7010144b69a243c9682aeb0a1dc4249a3))
|
||||
* **reference:** distinguish computed require member names ([ba35b55](https://github.com/morluto/rea/commit/ba35b5509db60c18dd43e350e0dd4a0b5f229c69))
|
||||
* **reference:** distinguish computed require member names ([df6e476](https://github.com/morluto/rea/commit/df6e476d67b2719c4b998daa3e9ad8a4b157ace2))
|
||||
* **reference:** preserve unresolved absolute module specifiers ([#670](https://github.com/morluto/rea/issues/670)) ([c8aed47](https://github.com/morluto/rea/commit/c8aed4740d52b1eecaa93b3a527e8b3814dc88a4))
|
||||
* **reference:** read linked worktree commit provenance ([#677](https://github.com/morluto/rea/issues/677)) ([d565ecd](https://github.com/morluto/rea/commit/d565ecdc257514f5d0c5a412081e322fc66d4215))
|
||||
* **reference:** recognize CMake build manifests ([#665](https://github.com/morluto/rea/issues/665)) ([1a24be5](https://github.com/morluto/rea/commit/1a24be574d1860931ff8d4c8ec07f3968fff9fd5))
|
||||
* **reference:** retain dynamic import relationships ([a5d2986](https://github.com/morluto/rea/commit/a5d2986b1e517b71a1ed51be433c95e975eafe32))
|
||||
* **reference:** retain dynamic import relationships ([b89de8f](https://github.com/morluto/rea/commit/b89de8feab389477a468f0b89b8dfa35d7ddc102))
|
||||
* **reference:** retain recovered parser diagnostics ([11d0ef2](https://github.com/morluto/rea/commit/11d0ef2e09308ac06889865b3fcf356b8ffa647f))
|
||||
* **reference:** retain recovered parser diagnostics ([ed3a7f0](https://github.com/morluto/rea/commit/ed3a7f070cf0793e22b9a0bbfd1f2ebe9dabcfd0))
|
||||
* **setup:** accept leading BOM in JSON client configs ([a5b970c](https://github.com/morluto/rea/commit/a5b970c6bf6848145a82de51c001410aa4d50f6f))
|
||||
* **setup:** accept leading BOM in JSON client configs ([fe843ec](https://github.com/morluto/rea/commit/fe843ec0b101544e22d7599d93b36377d3e3bc34))
|
||||
* **setup:** align Node runtime checks with package engines ([5802ef8](https://github.com/morluto/rea/commit/5802ef8746412649bb641332f2fa7e69bb1a86a3))
|
||||
* **setup:** align Node runtime checks with package engines ([ea0b733](https://github.com/morluto/rea/commit/ea0b733687582d6cc28b42beaf2fc7a48be07b6a))
|
||||
* **target:** explain the JavaScript directory analysis route ([#733](https://github.com/morluto/rea/issues/733)) ([8d44b3e](https://github.com/morluto/rea/commit/8d44b3edd36b528c308e32328552ca386dc43e91))
|
||||
* **trace:** match typed seeds against member arrays ([65c8797](https://github.com/morluto/rea/commit/65c879757ea09fabc5fc036d6e3395fd2939995c))
|
||||
* **trace:** match typed seeds against member arrays ([38ebd54](https://github.com/morluto/rea/commit/38ebd54776d661ced2e6cc2c5e4b260653a086d9))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **app:** split Setup and ProcessOwnership modules ([#628](https://github.com/morluto/rea/issues/628)) ([3c84080](https://github.com/morluto/rea/commit/3c84080aab5e5aa6c9fc14bafa07c635b537b64d))
|
||||
* **cli:** collapse duplicate CLI layer into src/cli ([#630](https://github.com/morluto/rea/issues/630)) ([494d4db](https://github.com/morluto/rea/commit/494d4db6d9d519f9258a25504cf29b07d61fbb9f))
|
||||
* **contracts:** split tool contracts by family ([#634](https://github.com/morluto/rea/issues/634)) ([5295d56](https://github.com/morluto/rea/commit/5295d56b105074b290e0d881df43cd13e34a2f8b))
|
||||
* **domain:** consolidate canonical ordering and digest helpers ([#624](https://github.com/morluto/rea/issues/624)) ([ba3869c](https://github.com/morluto/rea/commit/ba3869c597133b63fdcc0077c558923ae2e17a3f))
|
||||
* **domain:** split error taxonomy into thematic modules ([#636](https://github.com/morluto/rea/issues/636)) ([735c100](https://github.com/morluto/rea/commit/735c10059ca161967d71ccf8fa3f62011d2a22ca))
|
||||
* **native:** extract FAT header format switch in Mach-O slice selection ([#632](https://github.com/morluto/rea/issues/632)) ([38b2933](https://github.com/morluto/rea/commit/38b293329ca06c424a58bb59a28728f19015ce22))
|
||||
* **runtime:** centralize safe JSON parsing and preserve error causes ([#629](https://github.com/morluto/rea/issues/629)) ([e7f54b3](https://github.com/morluto/rea/commit/e7f54b3310b7f45af11c1754e468d4d3804191db))
|
||||
* **runtime:** name all silent catch causes without changing diagnostics ([#637](https://github.com/morluto/rea/issues/637)) ([5f27e02](https://github.com/morluto/rea/commit/5f27e02fae670774667de3618c88ba2ab0e7b661))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add the official Trendshift badge to README headers ([b6c21b1](https://github.com/morluto/rea/commit/b6c21b1db5d4ff4be0968aa26693916537622a06))
|
||||
* add the official Trendshift badge to README headers ([7d4ed59](https://github.com/morluto/rea/commit/7d4ed59816388409885310f40c12dfdcdd2d9246))
|
||||
* align MCP discovery and skill guidance with the stable catalog ([#664](https://github.com/morluto/rea/issues/664)) ([39db9fe](https://github.com/morluto/rea/commit/39db9fe4245cc8142f57f4feeadaff64cb894e31))
|
||||
* **browser:** add an interaction example that captures the visible result ([#750](https://github.com/morluto/rea/issues/750)) ([91daaa6](https://github.com/morluto/rea/commit/91daaa68aea3bbee02b837ac64d3ad13127d601e))
|
||||
* clarify onboarding and align provider guidance ([#745](https://github.com/morluto/rea/issues/745)) ([5af1699](https://github.com/morluto/rea/commit/5af169910391f6b95492865247fabf3c4735a9d6))
|
||||
* correct CLI examples and documentation checks ([#655](https://github.com/morluto/rea/issues/655)) ([fc80ea8](https://github.com/morluto/rea/commit/fc80ea88dfb7bde43e89db0f341c99d7b53c1a7c))
|
||||
* fix architecture diagram and pretty-print tool catalog ([#626](https://github.com/morluto/rea/issues/626)) ([5e7f35c](https://github.com/morluto/rea/commit/5e7f35c7c3ea8d504421a2623eafd1d73fea08a0))
|
||||
* **ghidra:** explain first-query deadlines and recovery ([#736](https://github.com/morluto/rea/issues/736)) ([e2c23f3](https://github.com/morluto/rea/commit/e2c23f3640f83c13587603c763dbd7b5ef1bbbb0))
|
||||
* **hopper:** document HopperTargetLease realpath fallback ([#711](https://github.com/morluto/rea/issues/711)) ([d3f498c](https://github.com/morluto/rea/commit/d3f498ceec65e4f61f39036ab943ace7ef1b9de5))
|
||||
* **installation:** pin manual MCP registration to the release version ([#658](https://github.com/morluto/rea/issues/658)) ([8dd1d84](https://github.com/morluto/rea/commit/8dd1d8447146b9962ec7ab2f245b0fe47d3ddf44))
|
||||
* update Trendshift repository badge ([3f6d6dc](https://github.com/morluto/rea/commit/3f6d6dc756d99ca6e7cdc1737522842fcbaee839))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* extract shared fixtures, drop knip carve-outs ([#627](https://github.com/morluto/rea/issues/627)) ([55e5eb0](https://github.com/morluto/rea/commit/55e5eb090bb3d6cf347d7cc2f8f028b35b7461ba))
|
||||
* **hopper:** synchronize startup cancellation with launcher readiness ([#662](https://github.com/morluto/rea/issues/662)) ([88a3c3f](https://github.com/morluto/rea/commit/88a3c3f8a4bf5cb31f60bca4696431c4e4512b30))
|
||||
|
||||
## [4.0.1](https://github.com/morluto/rea/compare/rea-agents-4.0.0...rea-agents-4.0.1) (2026-10-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **release:** allow npm propagation time ([2ef02ce](https://github.com/morluto/rea/commit/2ef02ced1603809f0fc6984612de31dce1801dd8))
|
||||
|
||||
## [4.0.0](https://github.com/morluto/rea/compare/rea-agents-3.2.1...rea-agents-4.0.0) (2026-10-05)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* Managed comparison metadata adds exact-signature matching and its count, and name_matching now reports exact-signature-fallback.
|
||||
* **setup:** unscoped setup refreshes only existing REA-owned registrations. Use --all-detected to configure every detected client. Setup JSON contains a compact doctor summary; use rea doctor for full diagnostics. Cancellation and successful dry runs now exit successfully.
|
||||
* Remove controlled replay, Node characterization, finite replay-machine, managed runtime planning, and redundant string-tracing tools. Retire process replay, shims, reactive scenarios, custom checkpoints, and active browser replay and origin-scope inputs.
|
||||
* **tools:** Removed permission configuration and policy commands, approval fields, filesystem scope inputs, and fixed confirmation options. Process capture inherits the host environment and accepts command names; callers must refresh tool schemas and migrate removed fields.
|
||||
|
||||
### Features
|
||||
|
||||
* **ghidra:** add DOS MZ analysis and complete function extents ([#556](https://github.com/morluto/rea/issues/556)) ([4f099a1](https://github.com/morluto/rea/commit/4f099a10f819125d52bd8baed9a9018496212eab))
|
||||
* **native:** add inspection primitives, dispatch traces and approved UI observation ([#500](https://github.com/morluto/rea/issues/500)) ([4fbc501](https://github.com/morluto/rea/commit/4fbc501121e96b33665a76cad2e5c3e8ac2e2c6d))
|
||||
* **setup:** expand agent integrations and verify updates ([#577](https://github.com/morluto/rea/issues/577)) ([6256b68](https://github.com/morluto/rea/commit/6256b6866616fa0c3137a77ebd31cf34155a642f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **artifacts:** break directory child collation ties ([#587](https://github.com/morluto/rea/issues/587)) ([b37e47c](https://github.com/morluto/rea/commit/b37e47c9f729a66008b1dc8b0a1bb266e79c2776))
|
||||
* **artifacts:** exclude removed paths from unchanged counts ([#569](https://github.com/morluto/rea/issues/569)) ([52bf709](https://github.com/morluto/rea/commit/52bf709bcefb17ab07e1f9c47c5fadf5eca9bd4e))
|
||||
* **artifacts:** expand nested ASARs on Windows ([#582](https://github.com/morluto/rea/issues/582)) ([ebb4c45](https://github.com/morluto/rea/commit/ebb4c45b6892ab60f2c81e05fbb4cb528ffbffc4))
|
||||
* **artifacts:** interrupt ZIP extraction under backpressure ([#594](https://github.com/morluto/rea/issues/594)) ([50f7e81](https://github.com/morluto/rea/commit/50f7e81432001239ca8b5de3a9cfe8f5980d2711))
|
||||
* **artifacts:** preserve archive entry types and host paths ([#581](https://github.com/morluto/rea/issues/581)) ([28e0f4e](https://github.com/morluto/rea/commit/28e0f4e9ec09ae35b26920e4c6925f48770bf5ed))
|
||||
* **artifacts:** refresh cached ASAR headers for each inventory ([#574](https://github.com/morluto/rea/issues/574)) ([f5013c5](https://github.com/morluto/rea/commit/f5013c50ca81795f91fd945fae2fb76f9dbe0f97))
|
||||
* **artifacts:** resolve parents after archive enumeration ([#578](https://github.com/morluto/rea/issues/578)) ([8eac290](https://github.com/morluto/rea/commit/8eac290e8d716817526ab7f628e83c75fb33d06b))
|
||||
* **artifacts:** respect XML plist byte-order marks ([#514](https://github.com/morluto/rea/issues/514)) ([23c4eba](https://github.com/morluto/rea/commit/23c4eba5b38b8de4f9547d879f2c3e54f741b1e1))
|
||||
* **artifacts:** retain expanded ASAR container parents ([#583](https://github.com/morluto/rea/issues/583)) ([00fa974](https://github.com/morluto/rea/commit/00fa97422f585db878ffe5d1613c56608916f972))
|
||||
* **artifacts:** retain relative paths in directory identities ([#580](https://github.com/morluto/rea/issues/580)) ([348323d](https://github.com/morluto/rea/commit/348323dd23360bbac3841a9632ffcd08290566d2))
|
||||
* **browser:** capture popup network and report early event gaps ([#614](https://github.com/morluto/rea/issues/614)) ([f557207](https://github.com/morluto/rea/commit/f557207887ca9336f3d3c5fc02c76c354c1b2945))
|
||||
* **browser:** capture selected events in popup pages ([#600](https://github.com/morluto/rea/issues/600)) ([a3bf4a0](https://github.com/morluto/rea/commit/a3bf4a0944ce5a52e100470273b91fdd48ebce49))
|
||||
* **browser:** derive source-map imports from syntax nodes ([#588](https://github.com/morluto/rea/issues/588)) ([bae87b4](https://github.com/morluto/rea/commit/bae87b4ded343b34c4226b5e5d491145db7f85ff))
|
||||
* **browser:** distinguish duplicate screenshot frame events ([#612](https://github.com/morluto/rea/issues/612)) ([1bd641d](https://github.com/morluto/rea/commit/1bd641d39f58461566e4a3f07fa00f84f34e87ad))
|
||||
* **browser:** ignore duplicate frame navigations and read block-form source map directives ([#546](https://github.com/morluto/rea/issues/546)) ([828395d](https://github.com/morluto/rea/commit/828395d510bd4f29525e5a0807a550a2daad885c))
|
||||
* **browser:** keep subresource redirects out of page navigation scope ([#524](https://github.com/morluto/rea/issues/524)) ([9c28f3c](https://github.com/morluto/rea/commit/9c28f3c4c0840fd7424dc669db1fb9baa1dde5d7))
|
||||
* **browser:** preserve RGB PNG transparent samples ([#595](https://github.com/morluto/rea/issues/595)) ([9997152](https://github.com/morluto/rea/commit/9997152c2d3127162161a2b945f506f0e834be38))
|
||||
* **browser:** release cancelled CDP cleanup without waiting for responses ([#517](https://github.com/morluto/rea/issues/517)) ([488fd5f](https://github.com/morluto/rea/commit/488fd5f60be3b2900c27a915c73322b9cb5eabb5))
|
||||
* **browser:** release discarded source-map response bodies ([#611](https://github.com/morluto/rea/issues/611)) ([8642055](https://github.com/morluto/rea/commit/864205570a0a9452f96c11feacd00338d026f17b))
|
||||
* **browser:** resolve redirected source map paths from final URL ([#518](https://github.com/morluto/rea/issues/518)) ([056c123](https://github.com/morluto/rea/commit/056c1237591de3710cb96a51ddd71fa94317b906))
|
||||
* **cli:** fail commands returning projected analysis errors ([#506](https://github.com/morluto/rea/issues/506)) ([2fd1196](https://github.com/morluto/rea/commit/2fd11961cfba0c2d211a1aa643cbf05fa4e8c20d))
|
||||
* **cli:** reject invalid UTF-8 JSON file bytes ([#613](https://github.com/morluto/rea/issues/613)) ([22acb0d](https://github.com/morluto/rea/commit/22acb0d5d730379fbe49e9e491f446e11425190e))
|
||||
* **comparison:** account for unpaired records after explicit bundle pairing ([#603](https://github.com/morluto/rea/issues/603)) ([12a2e37](https://github.com/morluto/rea/commit/12a2e3793af63542448124311e17eacbd5235fc0))
|
||||
* **comparison:** match generated names without swallowing symbol prefixes ([#596](https://github.com/morluto/rea/issues/596)) ([5f3ba3e](https://github.com/morluto/rea/commit/5f3ba3e6886b6b3186711e025da14875dc3c9684))
|
||||
* **comparison:** normalize CFG identity by numeric addresses ([#593](https://github.com/morluto/rea/issues/593)) ([d1e5c0e](https://github.com/morluto/rea/commit/d1e5c0e1fd04d2d5a0d663cbe43787a010e3d783))
|
||||
* **comparison:** require decoded signatures for structural method matches ([#604](https://github.com/morluto/rea/issues/604)) ([4a98c3e](https://github.com/morluto/rea/commit/4a98c3ea035480561d138d79288974a860c2aa22))
|
||||
* **conformance:** preserve own JSON keys during comparison ([#573](https://github.com/morluto/rea/issues/573)) ([3460eda](https://github.com/morluto/rea/commit/3460eda9ca01ccf41e3b9895f3cbe975c0eaed51))
|
||||
* correct native inventories and bridge error classification ([#503](https://github.com/morluto/rea/issues/503)) ([405732a](https://github.com/morluto/rea/commit/405732a7f55e3033c29533b18f7d8313dbd28570))
|
||||
* **docs:** allow README installation wording to vary ([92617f9](https://github.com/morluto/rea/commit/92617f965720aaa74ec20ad67291adc5e83677a1))
|
||||
* **ghidra:** avoid nested quotes in Windows Java options ([a574b74](https://github.com/morluto/rea/commit/a574b744b8fe95e906524684f6a3b28cdc656499))
|
||||
* **ghidra:** recover evidenced switch cases and default targets ([#579](https://github.com/morluto/rea/issues/579)) ([b225b0f](https://github.com/morluto/rea/commit/b225b0f64bfa86586c17f193d30e278d8124b14e))
|
||||
* **inspector:** preserve lossy Node discovery locations ([#542](https://github.com/morluto/rea/issues/542)) ([84e4361](https://github.com/morluto/rea/commit/84e4361c0936a4bb30951699fb68970d4d7a2327)), closes [#530](https://github.com/morluto/rea/issues/530)
|
||||
* **javascript:** avoid invented computed destructuring properties ([#507](https://github.com/morluto/rea/issues/507)) ([80777d9](https://github.com/morluto/rea/commit/80777d96daf64d0266e5cf3f9aad9338b97be4ff))
|
||||
* **javascript:** bind named class expressions in their class scope ([#510](https://github.com/morluto/rea/issues/510)) ([55f0267](https://github.com/morluto/rea/commit/55f0267c409cbf096bcdbd07d7604c44bcde3fbe))
|
||||
* **javascript:** classify assignment targets and retain compound-assignment reads ([#537](https://github.com/morluto/rea/issues/537)) ([333d118](https://github.com/morluto/rea/commit/333d118c0fa65c8c418baaa337270c0ad25cd7cc))
|
||||
* **javascript:** correct lexical and dynamic scope analysis ([#538](https://github.com/morluto/rea/issues/538)) ([6fa6f1f](https://github.com/morluto/rea/commit/6fa6f1f9b17318220abb2683c327146785dcc5d3))
|
||||
* **javascript:** honor computed keys when naming callables, exports, requests and options ([#536](https://github.com/morluto/rea/issues/536)) ([4c8dc39](https://github.com/morluto/rea/commit/4c8dc39f439b5283b077c6aa47f262714d7c4bbd))
|
||||
* **javascript:** honor package exports condition order ([#591](https://github.com/morluto/rea/issues/591)) ([8289595](https://github.com/morluto/rea/commit/82895959d1494f16bc59909a2575facf3ab19d79))
|
||||
* **javascript:** isolate lexical loop bindings ([#511](https://github.com/morluto/rea/issues/511)) ([c30878f](https://github.com/morluto/rea/commit/c30878f616c4496c6cd1993d677aa019b2412b7e))
|
||||
* **javascript:** prefer package entrypoints over directory indexes ([#575](https://github.com/morluto/rea/issues/575)) ([bb91abd](https://github.com/morluto/rea/commit/bb91abd3df52802ec8f9d3d1fb215dce5eb6ea49))
|
||||
* **javascript:** preserve literal CommonJS path punctuation ([#576](https://github.com/morluto/rea/issues/576)) ([063cd25](https://github.com/morluto/rea/commit/063cd25a93ee9f16b45635fea196dde0d9309ab9))
|
||||
* **javascript:** preserve Node exports fallback stop reasons ([#606](https://github.com/morluto/rea/issues/606)) ([adc07f5](https://github.com/morluto/rea/commit/adc07f5eaab005f013c88617aa09b6f2b4af4111))
|
||||
* **javascript:** respect HTML base URL components ([#589](https://github.com/morluto/rea/issues/589)) ([ffbde92](https://github.com/morluto/rea/commit/ffbde92f9ab65a868b660abf17ce9d3c4577d125))
|
||||
* **javascript:** respect lexical shadowing of require ([#509](https://github.com/morluto/rea/issues/509)) ([6a431d8](https://github.com/morluto/rea/commit/6a431d806a1574a660b96ba49412b24df9e93b50))
|
||||
* **javascript:** retain property reads during updates ([#508](https://github.com/morluto/rea/issues/508)) ([6b7923c](https://github.com/morluto/rea/commit/6b7923cdd2a099d1481783f34970a3d1a71a8206))
|
||||
* **managed:** classify truncated signatures as malformed ([#598](https://github.com/morluto/rea/issues/598)) ([1b6f054](https://github.com/morluto/rea/commit/1b6f05470d6fb1903346ef7a25fb5f9d23079cb7))
|
||||
* **managed:** distinguish metadata handles from member accesses ([#607](https://github.com/morluto/rea/issues/607)) ([c73742f](https://github.com/morluto/rea/commit/c73742f3003ec6c1469f77ecc9ccb093df636b4a))
|
||||
* **managed:** recognize PE Thumb and ARMNT machines ([#605](https://github.com/morluto/rea/issues/605)) ([0ce529a](https://github.com/morluto/rea/commit/0ce529af4e0280fcbd4e647d0fcb0a9ca9f3496e))
|
||||
* **mcp:** emit valid schemas for empty arrays ([#535](https://github.com/morluto/rea/issues/535)) ([4007b20](https://github.com/morluto/rea/commit/4007b20fc277b4d20b52cae328dea58ae033be63)), closes [#528](https://github.com/morluto/rea/issues/528)
|
||||
* **mcp:** return complete inline Evidence records ([#585](https://github.com/morluto/rea/issues/585)) ([ea9c3da](https://github.com/morluto/rea/commit/ea9c3dab6b2ff4fa943c8ca4fec7ae8b304dbd93)), closes [#551](https://github.com/morluto/rea/issues/551)
|
||||
* **native:** capture Mach-O header metadata alongside load commands ([#525](https://github.com/morluto/rea/issues/525)) ([a8195ba](https://github.com/morluto/rea/commit/a8195ba2cc34a37f68d7bb8a8087d73ace40984b))
|
||||
* **native:** decode little-endian fat headers ([bede3e3](https://github.com/morluto/rea/commit/bede3e3c60e53d445209096b360a81eaa0c446cc)), closes [#592](https://github.com/morluto/rea/issues/592)
|
||||
* **native:** decode Mach protection bits in their proper positions ([#586](https://github.com/morluto/rea/issues/586)) ([6aba6ae](https://github.com/morluto/rea/commit/6aba6ae2316fe6299c855a3db52b221fff63c00c))
|
||||
* **native:** inspect Apple dispatch metadata in FAT64 binaries ([#590](https://github.com/morluto/rea/issues/590)) ([f3e6582](https://github.com/morluto/rea/commit/f3e6582e9ed327c5accb8f9068ea0eb9bac49b1f))
|
||||
* **native:** inspect the selected universal Mach-O architecture ([#601](https://github.com/morluto/rea/issues/601)) ([2b7bb3b](https://github.com/morluto/rea/commit/2b7bb3bb6a87704c18469768bc82d87e97b5c99c))
|
||||
* **native:** match compound otool keys exactly and badge unknown thread entrypoints ([#534](https://github.com/morluto/rea/issues/534)) ([d40502a](https://github.com/morluto/rea/commit/d40502aa3b9a8c87fde49e5887547066128f19ba))
|
||||
* **native:** normalize otool line endings and strip plist JSON BOM ([#533](https://github.com/morluto/rea/issues/533)) ([3e4d4d9](https://github.com/morluto/rea/commit/3e4d4d93fdc3eb5fdffda3a8dc8bec43201cdd98))
|
||||
* **native:** preserve text-valued load command metadata ([#520](https://github.com/morluto/rea/issues/520)) ([bcfb407](https://github.com/morluto/rea/commit/bcfb4072f2ff665e93477206e14ce4b0581f97b9))
|
||||
* **native:** reject signal-terminated command captures ([#608](https://github.com/morluto/rea/issues/608)) ([b81d18e](https://github.com/morluto/rea/commit/b81d18eeee9d93dafcb15e3d723de951509c2612))
|
||||
* **native:** reject trailing text in lipo integers and keep multi-word otool keys ([#531](https://github.com/morluto/rea/issues/531)) ([9e30d93](https://github.com/morluto/rea/commit/9e30d93af3d2c0cc1a7536a10e3141f9f2fb499c))
|
||||
* **native:** retain lazy dylibs, normalize version-min builds, limit thread entrypoints ([#532](https://github.com/morluto/rea/issues/532)) ([0f77ada](https://github.com/morluto/rea/commit/0f77ada55e6d1d01cb925993d871bf4a94f94bb2))
|
||||
* **native:** retain re-exported Mach-O library dependencies ([#516](https://github.com/morluto/rea/issues/516)) ([4a5673e](https://github.com/morluto/rea/commit/4a5673e1dc6f2213cf94121384cff405e390677a))
|
||||
* **package:** separate agent and Hopper setup support ([#584](https://github.com/morluto/rea/issues/584)) ([205a977](https://github.com/morluto/rea/commit/205a9777776476b4f361e38f25d1c75baaccd420))
|
||||
* **platform:** accept absolute paths for any host and stop blaming the ceiling for a missing grant ([#552](https://github.com/morluto/rea/issues/552)) ([e793e12](https://github.com/morluto/rea/commit/e793e124a27b8087057f58f39028144ea6f6e173))
|
||||
* **process:** align provider detach with configured platform ([3fff74f](https://github.com/morluto/rea/commit/3fff74fb5bb9725be2456e8c39bc0b98181d0687))
|
||||
* repair boundary contracts across CLI and MCP ([#597](https://github.com/morluto/rea/issues/597)) ([9c25dc4](https://github.com/morluto/rea/commit/9c25dc4ed2915a81fc83a1d4dc22e7ba814b8dff))
|
||||
* **replay:** keep __proto__ own properties and reject symbol-keyed results ([#540](https://github.com/morluto/rea/issues/540)) ([05bb630](https://github.com/morluto/rea/commit/05bb630a00aa521229cde53ded9a5345b8bd1a2b))
|
||||
* **replay:** link the complete ESM graph before evaluating ([#515](https://github.com/morluto/rea/issues/515)) ([4200a42](https://github.com/morluto/rea/commit/4200a425d50eab80847edf3baa9ea4c5244e8be8))
|
||||
* **replay:** preserve CommonJS object as ESM default export ([#519](https://github.com/morluto/rea/issues/519)) ([2df6b51](https://github.com/morluto/rea/commit/2df6b51ebb87e8d406d6201d8f3158aefefa7cc5))
|
||||
* **replay:** preserve Date call and explicit constructor semantics ([#512](https://github.com/morluto/rea/issues/512)) ([9975bd2](https://github.com/morluto/rea/commit/9975bd23cdba7e196ca97c3fcec41a6eee925e57))
|
||||
* **replay:** preserve sparse array positions in result projection ([#522](https://github.com/morluto/rea/issues/522)) ([9d1c363](https://github.com/morluto/rea/commit/9d1c3631b8deccb5fb91906130f38e3f24aa254e))
|
||||
* **replay:** share one seeded generator and present builtins with real identity ([#539](https://github.com/morluto/rea/issues/539)) ([5b51fa0](https://github.com/morluto/rea/commit/5b51fa01e2391cd4b6d5e9ba7b6aa7615de891b1))
|
||||
* **repo:** keep dependency links out of version control ([#617](https://github.com/morluto/rea/issues/617)) ([b3223e3](https://github.com/morluto/rea/commit/b3223e3f61a23b79152939ef755aca95e7640f09))
|
||||
* **skill:** remove obsolete grants and audit tool effects ([2919def](https://github.com/morluto/rea/commit/2919def828b3c8fc7947989f1de3f4f4ac4cdd36))
|
||||
* **test:** improve capture reliability and runner cleanup ([#609](https://github.com/morluto/rea/issues/609)) ([d19686a](https://github.com/morluto/rea/commit/d19686a2a5122c691f7885997906ecb31dd9827d))
|
||||
* **tools:** simplify direct invocation and harden boundary contracts ([#555](https://github.com/morluto/rea/issues/555)) ([b5e9891](https://github.com/morluto/rea/commit/b5e98915c60968213141f33839e0c5c4b34df4db))
|
||||
* **web:** retain uncertainty for incomplete capture inventories ([#513](https://github.com/morluto/rea/issues/513)) ([d8c18f6](https://github.com/morluto/rea/commit/d8c18f604f292c95b6b0b185d23517cb6881c5fb))
|
||||
* **win32:** probe .cmd/.bat runtime shims via cmd.exe to avoid spawn EINVAL ([#505](https://github.com/morluto/rea/issues/505)) ([31ed005](https://github.com/morluto/rea/commit/31ed005183c315f1bc72f6e10730a47be73d7f1c))
|
||||
* **workflows:** preserve punctuation in filesystem source paths ([#521](https://github.com/morluto/rea/issues/521)) ([5b28a8b](https://github.com/morluto/rea/commit/5b28a8b331259e026678e681210e9c3c4f48f2fa))
|
||||
* **workflows:** retain container presence in export shape comparisons ([#523](https://github.com/morluto/rea/issues/523)) ([3854458](https://github.com/morluto/rea/commit/385445804ab45605a47edcb996ebbc026e2f0a43))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **config:** take the environment as an input at every configuration read ([#554](https://github.com/morluto/rea/issues/554)) ([53fe7d6](https://github.com/morluto/rea/commit/53fe7d6d3b3630472124563ccafdad3f4964b4bc))
|
||||
* **domain:** consolidate duplicated semantic member and literal helpers ([#550](https://github.com/morluto/rea/issues/550)) ([09830da](https://github.com/morluto/rea/commit/09830dad053f2b1440e0b1cde63747606684be4b))
|
||||
* **domain:** own digest and prefixed identifier shapes in one module ([#547](https://github.com/morluto/rea/issues/547)) ([ea58834](https://github.com/morluto/rea/commit/ea5883494036ddb30d138fdb381d54b2d9808b49))
|
||||
* **host:** inject platform and environment instead of reading them ambiently ([#549](https://github.com/morluto/rea/issues/549)) ([5b59b87](https://github.com/morluto/rea/commit/5b59b87726b459cf9887dd4edc176981ef4e6afc))
|
||||
* simplify runtime tools and remove replay engines ([#572](https://github.com/morluto/rea/issues/572)) ([e07b816](https://github.com/morluto/rea/commit/e07b81649e96668e5ce166cfc6db3b59a7777697))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add boundary contract guidance ([42e6a8a](https://github.com/morluto/rea/commit/42e6a8a6f6da4c3aebb10235a27d471dc2590954))
|
||||
* add Discord community links to README ([79bf26f](https://github.com/morluto/rea/commit/79bf26f33f980c20367986080a16d4a9f7952048))
|
||||
* align roadmap with direct tool use ([eba709e](https://github.com/morluto/rea/commit/eba709e5c096796b0afe3ce9203659df9eaf0a7d))
|
||||
* align translated READMEs and clarify support guides ([5b383b2](https://github.com/morluto/rea/commit/5b383b2ddd4195cb15e412b81853955b38419721))
|
||||
* clarify README wording and analysis tool support ([97822f2](https://github.com/morluto/rea/commit/97822f233be49a8b582b4c2aa982af3854ca0bb2))
|
||||
* move README image above Discord community ([ad86d39](https://github.com/morluto/rea/commit/ad86d39d2cb4f2d1ce0da08a17d5f7948c0797ec))
|
||||
* place community invitation below README navigation ([69a45e0](https://github.com/morluto/rea/commit/69a45e0bbc03d87624de866a821b976e4aaca069))
|
||||
* place Discord community below setup command ([778e464](https://github.com/morluto/rea/commit/778e464c206ba75a2f94edaad3c05e8bf63bed10))
|
||||
* place setup command above README image ([c6d3bdf](https://github.com/morluto/rea/commit/c6d3bdf597d9cd75a6f7396236e1a47f5ee4288d))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **setup:** isolate client fixtures across host platforms ([f73bb0f](https://github.com/morluto/rea/commit/f73bb0fe116678543a73d13f58eaa3b48133afc5))
|
||||
|
||||
## [3.2.1](https://github.com/morluto/rea/compare/rea-agents-3.2.0...rea-agents-3.2.1) (2026-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **release:** tolerate npm registry propagation delay ([726b730](https://github.com/morluto/rea/commit/726b730b84830b208725df50fa0251ece1fa199f))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **release:** accept guarded npm publishing ([b7bc855](https://github.com/morluto/rea/commit/b7bc8554928c8eca9c29842c77e4021c1e42e054))
|
||||
|
||||
## [3.2.0](https://github.com/morluto/rea/compare/rea-agents-3.1.0...rea-agents-3.2.0) (2026-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **native:** add investigation features ([#486](https://github.com/morluto/rea/issues/486)) ([ed933d1](https://github.com/morluto/rea/commit/ed933d18933814e3957a920972ccf91117d4ed87))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **artifacts:** stabilize extraction approval ([7eb1a95](https://github.com/morluto/rea/commit/7eb1a9500c62b66716878c18ee442c324a715a88))
|
||||
* **browser:** reject unknown Electron input fields ([1860d4f](https://github.com/morluto/rea/commit/1860d4f0e531919ee9b10bec7ad8db43e18a291b))
|
||||
* **ci:** retain artifacts for delayed reruns ([07888ec](https://github.com/morluto/rea/commit/07888ec096f69753a1535703fb7b3c3070e786b7))
|
||||
* **cli:** remove ignored result limits ([f3645aa](https://github.com/morluto/rea/commit/f3645aa51e55c5207f7b9b1f0156ffaef7aaed42))
|
||||
* consolidate boundary ownership and cleanup failure paths ([#477](https://github.com/morluto/rea/issues/477)) ([2160d4f](https://github.com/morluto/rea/commit/2160d4fae581243990e539a9c401d1b8824e1763))
|
||||
* **contracts:** align byte read schema with provider ([f580d9d](https://github.com/morluto/rea/commit/f580d9d07382bc9642bce896d431c827955339e4))
|
||||
* **ghidra:** return complete native analysis results ([a66e43a](https://github.com/morluto/rea/commit/a66e43a99dda1604c8250c9eb73aa8ef000371c9))
|
||||
* **managed:** retain every source location ([33b2099](https://github.com/morluto/rea/commit/33b20991c455306d66f8687a9f80ea22073d4207))
|
||||
* **process:** capture complete host probe output ([6c68ff5](https://github.com/morluto/rea/commit/6c68ff565fe1196fd8c7a63598e8bd94098abc3c))
|
||||
* **runtime:** make observed target role optional ([593a8e1](https://github.com/morluto/rea/commit/593a8e12ceac1cfe350493cfe6b0d1360eb85f7c))
|
||||
* **runtime:** retain complete reconciled graph content ([bedd55a](https://github.com/morluto/rea/commit/bedd55a4b4ccc9a0c1441d3836138b323de6d126))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **analysis:** remove caller-selected result caps ([a8c58c6](https://github.com/morluto/rea/commit/a8c58c6605ac37a8b504167732affc5ce4fc772f))
|
||||
* **analysis:** remove fixed result quotas ([20152a9](https://github.com/morluto/rea/commit/20152a9872dd035714beb5ef5b8898a9b4d1d6c4))
|
||||
* **artifacts:** consolidate inventory into inspection ([0fe96d9](https://github.com/morluto/rea/commit/0fe96d92dfefbaf28d2abfd5e237f6d0e82eee7b))
|
||||
* **artifact:** simplify extraction and inventory results ([07ff8b3](https://github.com/morluto/rea/commit/07ff8b30ee497d9f4477a0a9ae97f3f5addbb45a))
|
||||
* **artifacts:** remove duplicate inventory wrapper ([aa6eee1](https://github.com/morluto/rea/commit/aa6eee1a712d1a319bbaed5387aa5f4429755a1f))
|
||||
* **artifacts:** remove selectors and traversal quotas ([8c24272](https://github.com/morluto/rea/commit/8c24272ee7e6d30b1493ee7ccd63e45734fe2f95))
|
||||
* **artifacts:** select extraction by logical path ([7ba3580](https://github.com/morluto/rea/commit/7ba3580c860463adcd5edc73b7a80f34b92480cf))
|
||||
* **browser:** remove arbitrary capture ceilings ([5dd3b46](https://github.com/morluto/rea/commit/5dd3b4655e21222a6598bf8cbc0be90f3cbf5e0f))
|
||||
* **browser:** return complete observation evidence ([0e2738f](https://github.com/morluto/rea/commit/0e2738ffb6aa35bb06fe01d0af864aec968570cf))
|
||||
* **browser:** return complete scenario observations ([e80fe41](https://github.com/morluto/rea/commit/e80fe414b13294446f959b382cb240c806cfe000))
|
||||
* **browser:** share authorized main-frame polling ([eec75ce](https://github.com/morluto/rea/commit/eec75ce918cbac576fd217847911793331aa32a0))
|
||||
* **browser:** share canonical identity digest ([a6d9e49](https://github.com/morluto/rea/commit/a6d9e49ab46150c8b757f5bdd5f80d3a8ff52f26))
|
||||
* **contracts:** remove duplicate schema aliases ([4593c29](https://github.com/morluto/rea/commit/4593c29b64d3f6c43ddb19228da6231f1055eba4))
|
||||
* **contracts:** simplify workflows and drop readiness tool ([7acac61](https://github.com/morluto/rea/commit/7acac615ca2b8e3f4edd65fd8a08b237f85b5ac7))
|
||||
* **coverage:** evaluate inline without workspaces ([9e76054](https://github.com/morluto/rea/commit/9e7605482acf2de77ad04c5c5e03ac4280a11d7f))
|
||||
* **domain:** encode comparison and lifecycle states exactly ([#473](https://github.com/morluto/rea/issues/473)) ([d65730c](https://github.com/morluto/rea/commit/d65730c4c24ab6183426516d4564e6c25294b35d))
|
||||
* **domain:** share AST property name reader ([4f4cc59](https://github.com/morluto/rea/commit/4f4cc59873ec704811649aeca8a49e3bcdbdf324))
|
||||
* **electron:** retain complete active observations ([e8c56e0](https://github.com/morluto/rea/commit/e8c56e0c0aeb114e09636ad1841d8fd73720b5cc))
|
||||
* **errors:** remove stale workspace failures ([f570d40](https://github.com/morluto/rea/commit/f570d40f50bc8621b32ba4e255660ed1b1398356))
|
||||
* **evidence:** remove obsolete ID resolvers ([a3920e2](https://github.com/morluto/rea/commit/a3920e2abd915eba31b59413710315a85caf1afa))
|
||||
* **evidence:** remove session ledger quotas ([dca293a](https://github.com/morluto/rea/commit/dca293a446fa86767fb4bed6fe734f09b6cfafde))
|
||||
* **files:** use caller paths for local evidence files ([c34efca](https://github.com/morluto/rea/commit/c34efca4cd322d4816ab4cd526a7475489edc814))
|
||||
* **ghidra:** remove redundant schema markers ([c2a6f4b](https://github.com/morluto/rea/commit/c2a6f4bc437671c25ba318d21b82818c8e6bf2ce))
|
||||
* **javascript:** remove analysis ceilings ([9aec690](https://github.com/morluto/rea/commit/9aec6908b5c7ad50da6882415700cbe53c3f62ed))
|
||||
* **managed:** derive inspection from PE CLI extents ([dfad09e](https://github.com/morluto/rea/commit/dfad09e3b5769b079bf2d07382aee310d682724f))
|
||||
* **mcp:** simplify evidence and workflows ([6740ed5](https://github.com/morluto/rea/commit/6740ed5215578eaaf33c785d6245649d76a0411c))
|
||||
* **mcp:** simplify tool inputs and inline results ([f55c2b9](https://github.com/morluto/rea/commit/f55c2b92676e4a7eb06efbc8d9a908a7de62541a))
|
||||
* **mcp:** simplify workflow and observation scope ([34d00fd](https://github.com/morluto/rea/commit/34d00fd323c793278c6ed126308c58e2fc7efd54))
|
||||
* **observation:** internalize capture budgets ([7b3c797](https://github.com/morluto/rea/commit/7b3c7972377c7f77b1328e7d1de3f54835bc1397))
|
||||
* **observation:** retain complete tool results ([fa2b6c5](https://github.com/morluto/rea/commit/fa2b6c5a61adcb75c4c2773ed1c9f7dba83fc1d3))
|
||||
* **observation:** return complete inventories ([c523c38](https://github.com/morluto/rea/commit/c523c38027f536e8319eb2acc1840274c5bc5986))
|
||||
* **permission:** remove unused authorization helpers ([1b0ab75](https://github.com/morluto/rea/commit/1b0ab75a84ccdcf036ccfc019d1890b2d626699a))
|
||||
* **process:** remove fixed scenario ceilings ([abf7c11](https://github.com/morluto/rea/commit/abf7c1142e759a291d2267fedf15b0dd444a5684))
|
||||
* **process:** remove redundant schema version markers ([b3aca52](https://github.com/morluto/rea/commit/b3aca5289bf973ffb4072253eebe86e0acced347))
|
||||
* **process:** remove request count ceilings ([f2dc749](https://github.com/morluto/rea/commit/f2dc749c862a16d056de53e95914a9a411592583))
|
||||
* **process:** remove unused paired experiment ([e1d755f](https://github.com/morluto/rea/commit/e1d755f7cd574860f59965a6244dbd43f5032f2c))
|
||||
* **prompts:** make tool workflows optional ([3fd9650](https://github.com/morluto/rea/commit/3fd96506f223889a2d327cc7c97a95bf48ccb594))
|
||||
* **reference:** share graph index projections ([6ee87fb](https://github.com/morluto/rea/commit/6ee87fbf2defcb10a17352b36ebeed67c6ce6dc5))
|
||||
* remove cross-version investigation workflow ([81996df](https://github.com/morluto/rea/commit/81996df7a59ae6efb0f7be6687af89e66c64cdcb))
|
||||
* **replay:** share sandbox probe arguments ([9fca6c0](https://github.com/morluto/rea/commit/9fca6c0296ac360f9d2c838440b24d2ea79234b5))
|
||||
* **replay:** simplify runtime value checks ([60cb68a](https://github.com/morluto/rea/commit/60cb68a276b012964b08c18e9d60af22e64c468f))
|
||||
* return complete analysis results inline ([a2a3243](https://github.com/morluto/rea/commit/a2a3243ea874ee1662c62a83ced63be218e0218e))
|
||||
* return complete tool results inline ([0d96275](https://github.com/morluto/rea/commit/0d96275b6887b72519d2bc54f72012db6a8e482c))
|
||||
* **runtime:** retain complete reconciliation outputs ([79ea571](https://github.com/morluto/rea/commit/79ea571512a1f55200eb5520d6c56104dba54e5b))
|
||||
* **schema:** remove data-only version markers ([5457f4a](https://github.com/morluto/rea/commit/5457f4a2bceccb9c40e19a9986964025f279fc91))
|
||||
* **server:** remove registration alias ([8097ec4](https://github.com/morluto/rea/commit/8097ec456a14992b578057438be8a6e89a076cdf))
|
||||
* **server:** use canonical elicitation type ([bb6a58c](https://github.com/morluto/rea/commit/bb6a58c2a7eec15fb5f224376230398b51d1b952))
|
||||
* **test:** colocate pure-domain tests and reduce test debt ([#471](https://github.com/morluto/rea/issues/471)) ([e91c8c3](https://github.com/morluto/rea/commit/e91c8c39c2acddc4389ed0d01950d1954c472e47))
|
||||
* **test:** keep coverage at behavioral boundaries ([#474](https://github.com/morluto/rea/issues/474)) ([d4dc940](https://github.com/morluto/rea/commit/d4dc940bd871089ffa1b42d3475798b2f4f9c992))
|
||||
* **tools:** remove arbitrary workflow ceilings ([5f73cb0](https://github.com/morluto/rea/commit/5f73cb061b6fec532d1cccf5a8bce59855823fa4))
|
||||
* **tools:** remove artificial analysis ceilings ([2f08c7c](https://github.com/morluto/rea/commit/2f08c7c932d3619a481709935f07da59de3d3b27))
|
||||
* **tools:** remove redundant limits and acknowledgements ([bd0e341](https://github.com/morluto/rea/commit/bd0e34157239613aed7aa9315df0e97d03d8636c))
|
||||
* **tools:** remove remaining arbitrary workflow caps ([ecf3ed8](https://github.com/morluto/rea/commit/ecf3ed8bdb9ef7a83e7a2fb23443eced6c3824b0))
|
||||
* **tools:** return complete analysis results inline ([2a6eb41](https://github.com/morluto/rea/commit/2a6eb41147e496b9e044011fa3b755ef74388c51))
|
||||
* **workflows:** simplify evidence reference inputs ([24e1fa9](https://github.com/morluto/rea/commit/24e1fa9a76e447cff318da9f48c7adb659a1f8ab))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* add REA tool design guidance ([#485](https://github.com/morluto/rea/issues/485)) ([36aeec9](https://github.com/morluto/rea/commit/36aeec95863ca28d3e3aa5bec75a45d3824e17f5))
|
||||
* clarify aggregate context tools ([c7e207c](https://github.com/morluto/rea/commit/c7e207c290e376db5c2f210f9ac08fe170ef3ec4))
|
||||
* clarify MCP tool design guidance ([36aeec9](https://github.com/morluto/rea/commit/36aeec95863ca28d3e3aa5bec75a45d3824e17f5))
|
||||
* correct inline evidence and Hopper behavior ([bf9cce1](https://github.com/morluto/rea/commit/bf9cce1aef89a769320d217e82b895b086880e69))
|
||||
* describe complete application analysis outputs ([271b162](https://github.com/morluto/rea/commit/271b1620e68f3a6bbc69f28aa4c4d18d5c453baf))
|
||||
* generalize reverse-engineering workflow ([#488](https://github.com/morluto/rea/issues/488)) ([4ecc7a7](https://github.com/morluto/rea/commit/4ecc7a72f2d6d78b8d9c59a146e532fe552c40bb))
|
||||
* improve GitHub issue and pull request templates ([c490d85](https://github.com/morluto/rea/commit/c490d85045de07488e01747db9c9f77d264b2e36))
|
||||
* prioritize reusable analysis primitives ([#493](https://github.com/morluto/rea/issues/493)) ([9dd72c4](https://github.com/morluto/rea/commit/9dd72c4911841671594570677f5ba6e3992d076d))
|
||||
* refresh managed conformance manifest ([23e56c5](https://github.com/morluto/rea/commit/23e56c517c08abcafe3a81919215433458aef27e))
|
||||
* refresh tool catalog and cleanup audit fixtures ([53db3a4](https://github.com/morluto/rea/commit/53db3a476b2881f498a5d6c87195e783268c1988))
|
||||
* regenerate tool and evidence catalogs ([2f604a3](https://github.com/morluto/rea/commit/2f604a3c3ad037a68da4c33a30cb5316983b070f))
|
||||
* regenerate tool and evidence catalogs ([f2bbd59](https://github.com/morluto/rea/commit/f2bbd5957eccac6164b3f12db87921ffb09bdcf2))
|
||||
* **tools:** clarify complete inline results ([3b64c81](https://github.com/morluto/rea/commit/3b64c813cf8bcd0cdd262d0a3d5950819f320a12))
|
||||
* **tools:** clarify complete inline workflows ([4168174](https://github.com/morluto/rea/commit/41681742ccea83a7956e7bc303da4aa223ed4877))
|
||||
* **tools:** describe uncapped tool behavior accurately ([4cea84d](https://github.com/morluto/rea/commit/4cea84d7cbb0508e6c0dde36a2a0542855681f95))
|
||||
* **tools:** update inline workflow guidance ([4ad937c](https://github.com/morluto/rea/commit/4ad937c6caeec89ded592ea82dec4fa13ea17d8c))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **artifacts:** exercise argument-free extraction preflight ([a6d5b3e](https://github.com/morluto/rea/commit/a6d5b3ea9d1f53afeda33ef53fe50f42feac2343))
|
||||
* **browser:** assert strict inputs without legacy fields ([9ddf22e](https://github.com/morluto/rea/commit/9ddf22e1962803bf06d54b2e577a0c03a3f3ad48))
|
||||
* **browser:** cover complete screenshot output ([3f547ea](https://github.com/morluto/rea/commit/3f547eafa4c6f27b8264025c754392161687908e))
|
||||
* **browser:** remove stale input field assertion ([60c110d](https://github.com/morluto/rea/commit/60c110d97b1b22c010b8597965b3ad09f4f51480))
|
||||
* **cli:** consolidate duplicate setup journey ([9bd7e2c](https://github.com/morluto/rea/commit/9bd7e2cb459d0354ee2c406555de9e427c549086))
|
||||
* **cli:** remove duplicate browser discovery case ([dcb0362](https://github.com/morluto/rea/commit/dcb036292e975c81780dbf7a711fd1a565a13a10))
|
||||
* **conformance:** remove fixture generator test option ([ea853ce](https://github.com/morluto/rea/commit/ea853cebde5641dc9c12ef59005de84197463467))
|
||||
* **contracts:** remove duplicate tool inventories ([0ae3c08](https://github.com/morluto/rea/commit/0ae3c0837b387e53686bcd6e8aa03cb29f80458a))
|
||||
* **hopper:** name regex search assertion accurately ([9240bd8](https://github.com/morluto/rea/commit/9240bd8d166ffee93a90fd55c5de4b58d8ff6ad4))
|
||||
* **hopper:** remove dossier identity assertion ([b5c0b5b](https://github.com/morluto/rea/commit/b5c0b5b53e2d15dd6da9d4e8b1d3c3c21a16c409))
|
||||
* **mcp:** align workflow assertions with current guidance ([f8bb113](https://github.com/morluto/rea/commit/f8bb113c22cf23916a8cd8a5d77734ccaf63fd08))
|
||||
* **mcp:** consolidate catalog inventory checks ([3afcd64](https://github.com/morluto/rea/commit/3afcd641462bbd99ebdc3bf2fc8264e2c3344c09))
|
||||
* **mcp:** validate defaults at tool boundary ([21241d2](https://github.com/morluto/rea/commit/21241d2323d323f405b3cff4692e01364b6ddbde))
|
||||
* remove duplicate export schema acceptance ([b783e7f](https://github.com/morluto/rea/commit/b783e7febef8c22b41817a61a7f8293393ac6425))
|
||||
* remove obsolete schema version assertions ([fe342fa](https://github.com/morluto/rea/commit/fe342faa832ec9b17a6fb1dd85b1cf269b33cf1b))
|
||||
* remove stale limits and handoff expectations ([acf33d6](https://github.com/morluto/rea/commit/acf33d6df272873728aaa7527c5d43fa2a13132b))
|
||||
* remove vacuous and duplicate assertions ([56e2bf7](https://github.com/morluto/rea/commit/56e2bf7696e6ccbc3e4b5baf9249c6d4152f4a43))
|
||||
* remove Vitest config test exports ([d6e545c](https://github.com/morluto/rea/commit/d6e545cc4832ed47cc98d974aa8e4aa9453afb98))
|
||||
* replace brittle workflow and readme checks ([b3529aa](https://github.com/morluto/rea/commit/b3529aa327026472798a5f0971dcb325d221ddc6))
|
||||
* replace implementation lock-in with behavioural assertions ([#498](https://github.com/morluto/rea/issues/498)) ([7ee3f23](https://github.com/morluto/rea/commit/7ee3f236a9f753dfc90d84f95f20598e4e45fd0d))
|
||||
* **replay:** remove duplicate schema coverage ([4ec2343](https://github.com/morluto/rea/commit/4ec2343d91925e583b15d5404af2eabaf6ffed0f))
|
||||
|
||||
## [3.1.0](https://github.com/morluto/rea/compare/rea-agents-3.0.0...rea-agents-3.1.0) (2026-08-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **electron:** add autonomous Electron runtime analysis ([#464](https://github.com/morluto/rea/issues/464)) ([8f229ed](https://github.com/morluto/rea/commit/8f229ede70c0a4e224c1cf71d61487e17b779d6a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ci:** guard Release Please PR branch parsing ([84cc819](https://github.com/morluto/rea/commit/84cc819284b9ce7ee648850c85d5daf3a33e5a24))
|
||||
* **ci:** guard release PR branch parsing ([6838b6b](https://github.com/morluto/rea/commit/6838b6bff9ab5c400432caefd8c8737b86c41f07))
|
||||
* harden evidence lifecycle and MCP contracts ([5155924](https://github.com/morluto/rea/commit/51559245d2e1c2d7550cfe489716950fe6154bd0))
|
||||
* harden structured configuration comparisons ([#467](https://github.com/morluto/rea/issues/467)) ([6901edc](https://github.com/morluto/rea/commit/6901edc05d56063bdbe8be96511b1b21863630b5))
|
||||
* **npm:** preserve explicit setup versions ([#465](https://github.com/morluto/rea/issues/465)) ([1783bf6](https://github.com/morluto/rea/commit/1783bf6dfdd4ec11e824870ce5b1a2f5a321450e))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **mcp:** make analysis states unrepresentable ([#468](https://github.com/morluto/rea/issues/468)) ([b8a9da0](https://github.com/morluto/rea/commit/b8a9da0c0f9f0b2b022a350d2c7c671d1fa3f886))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* clarify agent reverse-engineering promise ([ed4a628](https://github.com/morluto/rea/commit/ed4a62871f7924948e3774ae6cf880514a560ff4))
|
||||
* clarify the agent reverse-engineering promise ([#466](https://github.com/morluto/rea/issues/466)) ([ed4a628](https://github.com/morluto/rea/commit/ed4a62871f7924948e3774ae6cf880514a560ff4))
|
||||
* **npm:** simplify current setup command ([#469](https://github.com/morluto/rea/issues/469)) ([8b62b1a](https://github.com/morluto/rea/commit/8b62b1a71586e9409e4dd645f3d4947ef61e59b1))
|
||||
|
||||
## [3.0.0](https://github.com/morluto/rea/compare/rea-agents-2.7.0...rea-agents-3.0.0) (2026-08-01)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* export_evidence_bundle now requires a filesystem path and no longer returns an inline bundle.
|
||||
|
||||
### Features
|
||||
|
||||
* make evidence bundles canonical MCP resources ([1712e6d](https://github.com/morluto/rea/commit/1712e6d821965ce5edc3880fe4ebab2e3cc02fd4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* address MCP resource review feedback ([f3b3fb0](https://github.com/morluto/rea/commit/f3b3fb07ded3d1ad72c0a4f24ca3147ef9722c5d))
|
||||
* align aggregate tools with session capabilities ([ede91ff](https://github.com/morluto/rea/commit/ede91ff1f04563001f695ec3869daeb3c826154a))
|
||||
* **ci:** align Vitest project worker budgets ([f47ee1a](https://github.com/morluto/rea/commit/f47ee1abb4fc8dbd5da3fa13c5870d6580b28bfa))
|
||||
* **ci:** allow documented public type aliases ([97f6a0f](https://github.com/morluto/rea/commit/97f6a0f3fd2ebf8fa884d7f7ba6e1c66ecfe80e0))
|
||||
* **ci:** make coverage report merging composable ([788f405](https://github.com/morluto/rea/commit/788f4057aee65dfae441249d2c9789bb996912a7))
|
||||
* **ci:** stabilize Vitest project routing and shards ([f07acde](https://github.com/morluto/rea/commit/f07acde513d4c74d027b66e143cf5072246b412b))
|
||||
* **errors:** preserve actionable analysis diagnostics ([0f96bf3](https://github.com/morluto/rea/commit/0f96bf365c330a7eb121a173bfa140cc2b652764))
|
||||
* **mcp:** clarify routing and advertise input examples ([969519c](https://github.com/morluto/rea/commit/969519c00f31b5bc0f23dcffe8867e091461fda6))
|
||||
* **mcp:** clarify target routing and caller diagnostics ([3e1e8cd](https://github.com/morluto/rea/commit/3e1e8cd3f829e6d3ac8b3c63d979f9db84dec826))
|
||||
* **test:** bound boundary worker pressure ([935721d](https://github.com/morluto/rea/commit/935721df10d25d23621dbc2be90d334644e0858c))
|
||||
* **test:** honor boundary ownership and public aliases ([92a1c33](https://github.com/morluto/rea/commit/92a1c33625ff88d81581554687e7708dd3c91e2d))
|
||||
* **test:** retain subprocess coverage and cleanup ([7f46bb1](https://github.com/morluto/rea/commit/7f46bb18241bf3815e5e01816277d0cafe8f923b))
|
||||
* **test:** retain subprocess coverage and cleanup ([dae2b9c](https://github.com/morluto/rea/commit/dae2b9cc4e8753827702ada6e51f2ee01811cfed))
|
||||
* **test:** stabilize boundary timing ([f0d8cda](https://github.com/morluto/rea/commit/f0d8cdafa86bafdc6ad5d8c2d23a97ef583b53d5))
|
||||
* **verify:** follow dynamic Hopper tool availability ([281e41f](https://github.com/morluto/rea/commit/281e41f7660066addcc7a204128fb1f49bf7a674))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* preserve provider-neutral runtime seams ([a8161f0](https://github.com/morluto/rea/commit/a8161f0e5540f3261a08015dfe56a4254980c064))
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* document resource snapshot workflow ([2a45d8d](https://github.com/morluto/rea/commit/2a45d8de7d52d0a234672d03c7dae794859f8ced))
|
||||
* refresh generated SDK metadata ([c824db1](https://github.com/morluto/rea/commit/c824db1ab7aed2886e9050c1b7fe2136b77e3698))
|
||||
* refresh managed completion manifest ([59d4659](https://github.com/morluto/rea/commit/59d465971ea86f933e9037fa32ac2787d935d076))
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
* **contracts:** cover validation branches ([8ccdb33](https://github.com/morluto/rea/commit/8ccdb33dd25922e039140bcd1e3cbf3d5acee837))
|
||||
* derive MCP SDK identity expectations ([ab4193c](https://github.com/morluto/rea/commit/ab4193c1f48020ff6baa42cb75e033110ee72b36))
|
||||
* **eval:** cover navigation and address tool routing ([ba098f2](https://github.com/morluto/rea/commit/ba098f24e59efbd567ae8438f65e31af0f0b8050))
|
||||
* overhaul deterministic suite architecture ([48053d1](https://github.com/morluto/rea/commit/48053d1032324cce6e2fe782ecba43d1b91a1bba))
|
||||
* overhaul deterministic suite architecture ([513d9b1](https://github.com/morluto/rea/commit/513d9b15558f9174a8e2c0ab00e31cbc05dbaba7))
|
||||
* **package:** verify npx upgrade on published releases ([7b6f7be](https://github.com/morluto/rea/commit/7b6f7becd257170cebf80f4f9f9c385b0000f074))
|
||||
* **package:** verify npx upgrade on published releases ([1f8ad7b](https://github.com/morluto/rea/commit/1f8ad7bc68f50d396715116467f19524da7e05e6))
|
||||
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
* document deterministic suite ownership ([3c42403](https://github.com/morluto/rea/commit/3c42403b78b53e7b5358c2bca9bc156ef2c32efd))
|
||||
|
||||
## [2.7.0](https://github.com/morluto/rea/compare/rea-agents-2.6.0...rea-agents-2.7.0) (2026-07-28)
|
||||
|
||||
|
||||
|
||||
+67
-49
@@ -2,13 +2,19 @@
|
||||
|
||||
REA welcomes focused bug fixes, documentation improvements, tests, and reverse-engineering workflow enhancements. Open an issue before a large contract or architecture change so its scope can be agreed before implementation.
|
||||
|
||||
For capability organization and provider composition, follow the incremental
|
||||
[migration guide](docs/capability-migration.md). Run `npm run verify:test-discovery`
|
||||
after adding or moving tests.
|
||||
|
||||
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
|
||||
|
||||
## Development setup
|
||||
|
||||
REA development requires Node.js 24.18.x and npm 11.16.x. Real-Hopper verification additionally requires either macOS 12+ or an officially supported Linux host (Ubuntu 24.04+, Fedora 41+, or 64-bit Arch) and an installed Hopper application. Linux demo verification uses its own private Xvfb display and does not require a desktop session. Run `nvm use` before installing dependencies.
|
||||
REA development requires Node.js 24.18.x and npm 11.16.x (pinned toolchain via `nvm use`; the supported runtime range is Node.js ^22.19 || ^24.11 || >=26, as the README badge states). Real-Hopper verification additionally requires either macOS 12+ or an officially supported Linux host (Ubuntu 24.04+, Fedora 41+, 64-bit Arch, or CachyOS) and an installed Hopper application. Linux demo verification uses its own private Xvfb display and does not require a desktop session. Run `nvm use` before installing dependencies.
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
npm test
|
||||
npm run check:fast
|
||||
```
|
||||
|
||||
`npm ci` installs the exact dependencies and prepares the Husky hooks without
|
||||
@@ -18,40 +24,62 @@ Turbo. Turbo caches deterministic builds and static checks across Git
|
||||
worktrees. After a package, lockfile, or managed-skill version change, run
|
||||
`npm run metadata:generate` before building.
|
||||
|
||||
Keep dependencies flowing inward through the existing domain, contracts, provider, application, server, and adapter layers. Parse unknown values at process and protocol boundaries, model expected failures with `Result`, and preserve the canonical tool inventory defined by `TOOL_CONTRACTS` unless a deliberate contract change updates every verifier, generated catalog artifact, and snapshot. Prefer capability- and session-scoped tool advertisement over schema truncation.
|
||||
Keep dependencies flowing inward through the existing domain, contracts, provider, application, server, and adapter layers. Parse unknown values at process and protocol boundaries, model expected failures with `Result`, and preserve the canonical tool inventory defined by `TOOL_CONTRACTS` unless a deliberate contract change updates every verifier, generated catalog artifact, and snapshot. Keep tool discovery complete and report capability- and session-scoped availability through `binary_session`.
|
||||
|
||||
Before submitting a pull request, run:
|
||||
## Documentation website
|
||||
|
||||
The VitePress site uses the Markdown files in `docs/` and deploys to
|
||||
<https://morluto.github.io/rea/>. Run `npm run docs:dev` for live editing,
|
||||
`npm run docs:build` to check the production build and links, and
|
||||
`npm run docs:preview` to preview that build at `/rea/`.
|
||||
|
||||
Site navigation lives in `docs/.vitepress/config.ts`. Keep links to guides
|
||||
relative so they work on GitHub and the website; link to repository files
|
||||
outside `docs/` using their full GitHub URLs. Generated reference documents
|
||||
still use `npm run docs:generate`; `docs:build` only builds the website.
|
||||
|
||||
The documentation workflow builds pull requests and deploys changes on `main`.
|
||||
The repository's **Settings → Pages → Build and deployment → Source** must be
|
||||
set to **GitHub Actions** before the first deployment.
|
||||
|
||||
## Development feedback and PR verification
|
||||
|
||||
For an ordinary edit, run the relevant regression and cached static checks:
|
||||
|
||||
```bash
|
||||
npm run check:pr
|
||||
npm run verify:package
|
||||
npm pack --dry-run
|
||||
npm run test:focused -- src/config.test.ts
|
||||
npm run check:fast
|
||||
```
|
||||
|
||||
`npm run check:fast` runs cached typecheck and lint for rapid local feedback.
|
||||
`npm run check` adds formatting, dead-code, and package-metadata freshness
|
||||
checks; use `npm run check:test` when the complete test suite is relevant.
|
||||
Pre-commit formats then lints staged source files, and pre-push runs
|
||||
`check:fast`. `check:pr` additionally renders API documentation and checks all
|
||||
committed generated metadata. Real
|
||||
provider and replay execution remains uncached, but their deterministic build
|
||||
prerequisite uses Turbo. CI uploads the rendered TypeDoc site as an `api-docs`
|
||||
artifact; the generated HTML is not committed.
|
||||
`test:local` selects dirty source tests without building; explicit source test
|
||||
paths run even on a clean tree. `check:changed` adds source tests affected since
|
||||
the branch merge base with `origin/main`. Use `npm run test:changed -- --base
|
||||
REVISION` to choose another base. Changed-test selection follows the import
|
||||
graph and is feedback, not complete correctness evidence. Inspect relevant
|
||||
boundary and provider behavior explicitly; see [docs/testing.md](docs/testing.md).
|
||||
|
||||
Local `npm test` runs without coverage, retries, or verbose output. Use `npm run
|
||||
test:fast` for pure and subprocess groups, `npm run test:integration` for serial
|
||||
filesystem/process/CLI cases. `npm run test:changed` runs changed and related
|
||||
non-serial tests once; `npm run test:watch` keeps that selection live. Pure tests
|
||||
use Vitest threads, while subprocess tests stay in isolated forks. CI splits the
|
||||
complete suite across two native Vitest
|
||||
shards, then merges their coverage and JUnit reports. `npm run test:ci` runs the
|
||||
equivalent unsharded gate locally. Coverage thresholds remain in
|
||||
`vitest.config.ts`.
|
||||
Before handing off a PR, run the relevant tests, `npm run check`, and
|
||||
`npm run docs:check` when contracts or generated metadata change. Record which
|
||||
checks ran. CI owns complete deterministic tests and aggregate coverage. Use
|
||||
`npm run check:pr` for a deliberate full local gate on broad changes or when
|
||||
investigating CI failures; it is not required after each edit, rebase, or push.
|
||||
Packaging, setup, installation, or distribution changes also require
|
||||
`npm run verify:package` and `npm pack --dry-run`. Provider behavior changes
|
||||
require the matching real-provider `verify:*` lane.
|
||||
|
||||
CI installs dependencies once for all static checks, cancels superseded PR
|
||||
runs, and skips package, Windows, and full test lanes for documentation-only
|
||||
pull requests. TypeDoc renders only in pull-request CI; it does not run in local
|
||||
commit or pre-push hooks, or in post-merge `main` CI.
|
||||
`check:fast` runs cached typecheck and lint, reporting diagnostics on failure.
|
||||
`check` adds formatting, dead-code, and package-metadata freshness checks.
|
||||
Formatting uses Oxfmt and the committed `.oxfmtrc.json`; generated sources use
|
||||
the same configuration. Pre-commit formats and lints staged files; pre-push runs
|
||||
`check:fast`.
|
||||
`docs:check` checks committed generated metadata. `docs:generate` regenerates
|
||||
those files, and the docs CI lane checks them.
|
||||
Real-provider execution remains uncached; deterministic builds use Turbo.
|
||||
|
||||
Local `npm test` runs every deterministic Vitest project without coverage or
|
||||
retries. CI runs four coverage shards and merges JUnit and timing reports.
|
||||
CI cancels superseded PR runs and skips package, Windows, and full test lanes
|
||||
for documentation-only PRs. Coverage thresholds remain in `vitest.config.ts`.
|
||||
|
||||
Tests that need a temporary directory must use
|
||||
`createTestTempDirectory` from `tests/fixtures/temporaryDirectory.ts`. The
|
||||
@@ -92,24 +120,14 @@ Describe the behavior change and verification performed in the pull request. Nev
|
||||
|
||||
## Maintainer release checklist
|
||||
|
||||
Run the full checks, isolated package verifier, package dry run, and two-target real-Hopper verifier described above. Build a local tarball and exercise the executable through the package boundary:
|
||||
Use the [checkpoint release guide](docs/releasing.md). Releases start from an
|
||||
explicit `release/VERSION` branch cut at a recorded commit. Pushing the branch prepares
|
||||
the bot PR. Wait for its exact-head CI and review; merging into that frozen
|
||||
branch triggers publication through the official Release workflow. Main
|
||||
pushes do not update release PRs or publish packages. Both publishers build
|
||||
the exact SHA tagged by Release Please.
|
||||
|
||||
```bash
|
||||
npm pack
|
||||
npm exec --yes --package ./morluto-rea-0.1.0.tgz -- rea --help
|
||||
```
|
||||
|
||||
Publish the public package:
|
||||
|
||||
```bash
|
||||
npm publish --access public
|
||||
```
|
||||
|
||||
After npm registry propagation, verify the published CLI and connect the client SDK version pinned in `package.json` to the published server to confirm the canonical tool catalog:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest --help
|
||||
npx -y rea-agents@latest doctor
|
||||
npx -y rea-agents@latest setup --yes
|
||||
npx -y rea-agents@latest mcp
|
||||
```
|
||||
Keep new implementation commits on main for the next release. The workflow
|
||||
owns packaged-artifact verification, npm publication, the published CLI/MCP
|
||||
canary, and MCP Registry publication. Sync release metadata back to main after
|
||||
publication; see the guide for partial-publication recovery and verification.
|
||||
|
||||
+135
-80
@@ -2,9 +2,9 @@
|
||||
|
||||
[English](README.md) · [简体中文](README_zh.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · **العربية**
|
||||
|
||||
# REA: هندسة أي شيء عكسيًا
|
||||
# REA: اعكس هندسة أي شيء
|
||||
|
||||
### واجهة CLI وخادم MCP واحدان يمكّنان وكلاء البرمجة من إجراء هندسة عكسية لأي شيء
|
||||
### وسيط MCP واحد للهندسة العكسية عبر الملفات التنفيذية والتطبيقات وسلوك وقت التشغيل.
|
||||
|
||||
**اعثر على ميزة تعجبك. افهم آلية عملها. ابنها بالطريقة التي تريدها.**
|
||||
|
||||
@@ -13,109 +13,163 @@
|
||||
[](#منصة-أدوات-التحقيق)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[البدء السريع](#البدء-السريع) · [من الملف التنفيذي إلى السلوك](#من-الملف-التنفيذي-إلى-السلوك) · [دليل الأدوات](#منصة-أدوات-التحقيق) · [كيف يعمل](#كيف-يعمل) · [الأسئلة الشائعة](#الأسئلة-الشائعة)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[الموقع (بالإنجليزية)](https://morluto.github.io/rea/) · [أدلة الاستخدام](https://morluto.github.io/rea/guides/) · [دراسة حالة DX-Ball](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[البدء السريع](#البدء-السريع) · [حالة الدعم](#حالة-الدعم) · [من الملف التنفيذي إلى السلوك](#من-الملف-التنفيذي-إلى-السلوك) · [منصة أدوات التحقيق](#منصة-أدوات-التحقيق) · [خطة العمل](#خطة-العمل) · [كيف يعمل؟](#كيف-يعمل)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>انضم إلى مجتمع الهندسة العكسية</strong>
|
||||
</a><br />
|
||||
<sub>Discord · أسئلة وأجوبة · شارك أعمالك</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<code>curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash</code>
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA يشغّل جسر التحليل داخل Hopper لفحص ملف تنفيذي أصلي" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
هل وجدت في تطبيق ما ميزة تريدها في منتجك؟ أعط التطبيق إلى وكيل البرمجة حتى من دون شيفرته المصدرية. باستخدام REA، يستطيع الوكيل استقصاء الميزة وفهم آلية عملها ثم بناء نسخة ملائمة لتقنياتك وتصميمك ومتطلباتك.
|
||||
---
|
||||
|
||||
يوفر REA هذه العملية عبر CLI وخادم MCP واحدين. يستطيع الوكيل قراءة شيفرة التطبيق وتتبع طريقة عمل الميزات ثم استخدام الأدلة مباشرة في عمله البرمجي المعتاد. ويتولى REA أدوات الهندسة العكسية المعقدة خلف واجهة واحدة.
|
||||
هل وجدت في تطبيق ميزة تريدها في منتجك؟ اطلب من وكيلك استقصاءها باستخدام REA. يستطيع فحص التطبيق من دون شيفرته المصدرية، وشرح طريقة عمل الميزة وعرض الأدلة، ثم بناء نسخة تناسب مشروعك.
|
||||
|
||||
يوفر REA أدوات لتحليل الملفات التنفيذية الأصلية وتطبيقات JavaScript وElectron وتجميعات .NET والمواقع. يمكنك استخدامها من وكيلك أو من الطرفية. يعمل التحليل محليًا، وتتضمن النتائج الأدلة والقيود.
|
||||
|
||||
يضبط Setup إعدادات الوكيل ويربطه بتثبيت موجود من Hopper أو Ghidra، ويثبّت سير عمل REA الموجّه. ويمكنه تثبيت Hopper بعد موافقتك. ولا يحتاج التحليل الثابت لتطبيقات JavaScript إلى أي محرك.
|
||||
|
||||
## اطلب من وكيلك مباشرة
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
ثم اطلب:
|
||||
بعد [الإعداد](#البدء-السريع)، أعد تشغيل الوكيل ثم اطلب:
|
||||
|
||||
```text
|
||||
أعد REA وأجر هندسة عكسية لتطبيق Notes. اشرح طريقة عمل البحث وكيف توصلت إلى ذلك،
|
||||
ثم ابنِ ميزة مشابهة لمشروعي.
|
||||
استقصِ طريقة عمل البحث في تطبيق Notes، واعرض الأدلة، ثم ابنِ ميزة مشابهة لمشروعي.
|
||||
```
|
||||
|
||||
تطبيق Notes مجرد مثال. يمكنك تسمية أي تطبيق تريد فهمه أو طلب البدء بنظرة عامة.
|
||||
استبدل Notes بالتطبيق الذي تريد فهمه، أو اطلب نظرة عامة أولًا.
|
||||
|
||||
## من الملف التنفيذي إلى السلوك
|
||||
|
||||
| فك الترجمة | الفهم | إعادة الإنشاء |
|
||||
| ----------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| فك الترجمة | الفهم | إعادة الإنشاء |
|
||||
| ---------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| استخرج الشيفرة شبه المصدرية، والتعليمات، والسلاسل النصية، والرموز | تتبع تدفق التحكم والمراجع المتبادلة والاستدعاءات والأنواع | حوّل ما تعلمه الوكيل إلى ميزة تناسب تقنياتك وواجهتك ومتطلباتك |
|
||||
|
||||
يوضح REA كيف وصل إلى نتائجه. وهو لا يدّعي استعادة الشيفرة المصدرية الأصلية أو استنساخ التطبيق تلقائيًا.
|
||||
|
||||
## لماذا REA؟
|
||||
|
||||
| | |
|
||||
| -------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| **مصمم للوكلاء** | اسأل عما يفعله تطبيق مترجم ودع الوكيل يجمع الأدلة بدلًا من التخمين. |
|
||||
| **CLI وMCP** | استخدم قدرات الهندسة العكسية نفسها من الطرفية أو وكيل البرمجة. |
|
||||
| **يتولى التعقيد** | يدير REA إعداد الأدوات وفتح التطبيق واستمرار الاستقصاء والتنظيف بعد الانتهاء. |
|
||||
| **سير عمل كامل** | انتقل من النظرة الأولى إلى الشيفرة شبه المصدرية وعلاقات الاستدعاء والأنواع وأدلة التنفيذ. |
|
||||
| **محلي حسب التصميم** | يجري التحليل على جهاز Mac ولا يرفع REA الملف التنفيذي إلى خدمة تحليل مستضافة. |
|
||||
| **يحافظ على السياق** | استقصِ عدة ملفات تنفيذية من دون بدء التحليل من جديد عند كل سؤال. |
|
||||
| | |
|
||||
| ------------------------ | ---------------------------------------------------------------------------- |
|
||||
| **مصمم للوكلاء** | اسأل عما يفعله التطبيق ودع الوكيل يفحصه بدلًا من التخمين. |
|
||||
| **CLI وMCP** | استخدم قدرات الهندسة العكسية نفسها من الطرفية أو الوكيل. |
|
||||
| **إعداد موجّه** | اضبط وكيلك، واربط أداة تحليل موجودة، أو ثبّت Hopper بموافقتك. |
|
||||
| **من الفهم إلى الشيفرة** | افهم الميزة ثم ابنِ نسختك الخاصة في جلسة البرمجة نفسها. |
|
||||
| **محلي حسب التصميم** | يجري التحليل على مضيفك المحلي المدعوم، ولا يرفع REA التطبيق إلى خدمة مستضافة. |
|
||||
| **يحافظ على السياق** | استقصِ عدة تطبيقات دون بدء التحليل من جديد عند كل سؤال. |
|
||||
|
||||
## البدء السريع
|
||||
|
||||
### باستخدام وكيل برمجة — موصى به
|
||||
### تشغيل الإعداد (موصى به)
|
||||
|
||||
اربط REA بوكيلك:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
اطلب من الوكيل إعداد REA. سيفحص جهاز Mac ويشرح ما يحتاج إلى تثبيته ويطلب الموافقة ويرشدك خلال رسائل النظام. أعد تشغيل الوكيل بعد الإعداد إذا طلب ذلك لتحميل مجموعة الأدوات الكاملة.
|
||||
يعرض Setup أولاً قائمة متعددة الاختيار للوكلاء الذين تريد ربطهم. تكون تسجيلات REA الموجودة محددة افتراضياً، ولا يؤدي اكتشاف عميل جديد وحده إلى تحديده؛ ويمكنك اختيار عميل غير مهيأ. راجع المسارات والتغييرات المحددة قبل الموافقة. يثبت Setup سير عمل REA للوكلاء المحددين افتراضياً. Hopper خيار منفصل يتطلب موافقة مستقلة، ويمكن أيضاً حفظ مسارات تثبيت Ghidra الموجودة.
|
||||
|
||||
### قبل البدء
|
||||
يعرض التغييرات قبل تطبيقها وينشئ نسخة احتياطية من الإعدادات الموجودة. راجع [دليل التثبيت والإعداد](docs/installation.md) للمتطلبات والخيارات الإضافية.
|
||||
|
||||
- macOS 12 أو أحدث
|
||||
- Ubuntu 24.04+ أو Fedora 41+ أو Arch Linux 64 بت
|
||||
- Node.js 22.19+ أو 24.11+ وnpm المرفق مع Node
|
||||
### الاستخدام مع وكيل
|
||||
|
||||
يعرض `rea setup` خطة التغييرات كاملة ويطلب التأكيد قبل تطبيقها. لا يثبت أو يحدّث Homebrew أو Node.js أو npm. إذا كان [Hopper](https://www.hopperapp.com/) مفقودًا، يقترح Setup الحزمة الرسمية. Hopper برنامج منفصل يحتاج إلى ترخيص خاص به.
|
||||
بعد الإعداد، أعد تشغيل الوكيل واشرح التطبيق أو الميزة التي تريد استقصاءها. يدعم REA Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin وOpenCode وAntigravity وGitHub Copilot CLI وCommand Code وVS Code. تكون تسجيلات REA الموجودة محددة افتراضياً، ويجب اختيار العملاء المكتشفين الآخرين يدوياً. يمكن للوكلاء الآخرين استخدام إعداد MCP أدناه.
|
||||
|
||||
إذا كان Ghidra 12.1.2 PUBLIC وJDK 21 الكامل بنواة 64 بت مثبتين مسبقًا على Linux 64 بت، فيمكن لـ Setup أيضًا تسجيل `GHIDRA_INSTALL_DIR` و`JAVA_HOME` الاختياري بعد الموافقة. لا ينزّل REA أو يثبت أو يعدّل Ghidra أو Java. يوفّر مزوّد Ghidra ضمن جلسة headless معزولة وللقراءة فقط الجرد، وفك الترجمة، والتجميع، وعلاقات الاستدعاء، والمراجع ذات الأنواع، وxref، وCFG، وملفات تحليل الدوال؛ وتبقى حالة الواجهة الرسومية وعمليات التعديل غير متاحة.
|
||||
يمكن تشغيل Hopper في الوضع التجريبي. إذا ظهرت رسالة عند التشغيل الأول، فاختر التجربة أو أدخل ترخيصًا موجودًا.
|
||||
|
||||
#### التثبيت على Linux واستكشاف الأخطاء
|
||||
### مهارة مساعد البرمجة (اختياري)
|
||||
|
||||
على Ubuntu 24.04+ وFedora 41+ وArch Linux 64 بت، ينزّل REA حزمة DEB أو RPM أو Arch الرسمية، ويتحقق من الحجم وقيمة التحقق المنشورين، ثم يستخدم `apt-get` أو `dnf` أو `pacman` لحل الاعتماديات. عند التشغيل دون root يعرض `pkexec` طلب تفويض النظام. لا يستدعي REA الأمر `sudo`.
|
||||
أضف المهارة إلى مساعد البرمجة للحصول على سياق أغنى:
|
||||
|
||||
مسار المشغّل الافتراضي هو `/opt/hopper/bin/Hopper`. استخدم `HOPPER_LAUNCHER_PATH` للمسارات الأخرى. إذا أبلغ Doctor عن غياب محرك التحليل، شغّل `ldd /opt/hopper/bin/Hopper | grep 'not found'`، وثبّت المكتبات الناقصة ثم أعد `rea setup`. يشغّل REA إصدار Hopper التجريبي المدعوم على شاشة Xvfb خاصة ويختار وضع التجربة الذي يقدمه Hopper لكل جلسة تحليل؛ لا يلزم `DISPLAY` لسطح المكتب ولا ترخيص مدفوع، مع بقاء حدود الإصدار التجريبي التي يحددها المورّد. أضف `~/.local/bin` إلى `PATH` أيضًا.
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
### 1. إعداد REA
|
||||
توفر المهارة سير عمل التحقيق في REA. نفّذ الإعداد أعلاه لربط REA بالوكيل وضبط أدوات التحليل. يثبّت الإعداد افتراضيًا مهارة مطابقة للإصدار؛ وهذا الأمر يثبّت نسخة المستودع.
|
||||
|
||||
لشجرة JavaScript/Electron المستخرجة أو ملف ASAR، لا يلزم إعداد MCP ولا محرك أصلي:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
استبدل المسار بهدفك (مثل `"D:/apps/example"` على Windows). يدعم سير العمل الثابت هذه المسارات، بينما يتطلب التحليل الأصلي محركه الخاص.
|
||||
|
||||
### تثبيت أمر rea
|
||||
|
||||
ثبت واجهة سطر الأوامر:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
إذا طلب macOS أو برنامج التثبيت تأكيدًا، فأكمل الخطوة ثم شغّل الأمر نفسه مرة أخرى.
|
||||
يجب تثبيت Node.js وnpm مسبقًا. عند تشغيله من طرفية، يثبت البرنامج `rea` ويبدأ الإعداد.
|
||||
|
||||
### 2. إعادة تشغيل وكيل البرمجة
|
||||
|
||||
يكتشف Setup كلاً من Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin. ويهيئ تلقائيًا أول ستة عملاء عند اكتشافهم؛ أما Devin فيُبلّغ عن اكتشافه فقط ولا يُعدّله لعدم وجود حد موثق لإعداد MCP المحلي. أعد تشغيل العملاء الذين جرى إعدادهم كي يحمّلوا REA.
|
||||
|
||||
### 3. اطلب من الوكيل
|
||||
|
||||
يمكنك ذكر التطبيق باسمه. سيعثر وكيل البرمجة عليه ويمرر ملف البرنامج الذي يحتاجه REA.
|
||||
|
||||
```text
|
||||
استخدم REA لإجراء هندسة عكسية لتطبيق Notes. اشرح طريقة عمل البحث واعرض الأدلة،
|
||||
ثم ابنِ ميزة مشابهة لمشروعي باستخدام SQLite.
|
||||
```
|
||||
|
||||
إذا واجهت مشكلة، فشغّل:
|
||||
يمكنك بدلًا من ذلك التثبيت باستخدام npm ثم تشغيل الإعداد:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
rea uninstall
|
||||
rea uninstall --purge-data
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### المتطلبات
|
||||
|
||||
- macOS 12 أو أحدث
|
||||
- Ubuntu 24.04+ أو Fedora 41+ أو Arch Linux بنواة 64 بت أو CachyOS
|
||||
- Node.js 22.x (>=22.19) أو 24.x (>=24.11) أو 26+ وnpm
|
||||
|
||||
يتطلب تحليل الملفات التنفيذية الأصلية Hopper أو Ghidra. Hopper برنامج منفصل، وللوضع التجريبي قيود يحددها المورّد، لكن الترخيص المدفوع ليس إلزاميًا.
|
||||
|
||||
يدعم Ghidra نظامي Linux x64 وmacOS x64/arm64. ثبت Ghidra 12.1.x وJDK الكامل بنواة 64 بت الذي يعلنه ذلك التثبيت (`application.java.min` حتى `application.java.max`) بصورة منفصلة، ثم اضبط REA لاستخدامهما. تتطلب إصدارات 12.1 الحالية JDK 21 أو أحدث ولا تحدد حدًا أعلى. جرى التحقق من الجسر مع Ghidra 12.1.4 وJDK 21. يحتاج macOS أيضًا إلى أداة فك الترجمة الأصلية المطابقة لمعمارية الجهاز.
|
||||
|
||||
يستطيع Setup التحقق من التثبيت وحفظ المسارات؛ ولا يثبت أو يحدّث Ghidra أو Java أو Node.js أو npm أو Homebrew.
|
||||
|
||||
يتضمن فرع main والحزمة npm 4.1.0 دعمًا تجريبيًا لـ Windows x64 Ghidra P0 لتطبيقات PE الأصلية x86-64 (غير المُدارة وغير DLL) على NTFS محلي، مع ضوابط Job Object وقوائم DACL الخاصة وقبول المسارات المجمّعة. تحقق من [حد الإصدار](docs/installation.md#released-package-and-main) قبل توقع ذلك من حزمة npm أقدم. راجع [دليل Windows Ghidra P0](docs/windows-ghidra-p0.md) للمتطلبات والنطاق الموثق.
|
||||
|
||||
### تشخيص المشكلات
|
||||
|
||||
يشخّص `npx -y rea-agents@latest doctor` الجهاز والاعتماديات وأدوات التحليل وإعدادات الوكيل من دون تغييرها. أضف `--json` للحصول على تشخيص منظم.
|
||||
|
||||
على Linux، يفضّل REA الملف التنفيذي `/opt/hopper/bin/Hopper`، وإذا لم يكن متاحًا يتحقق تلقائيًا من `~/.local/share/rea/hopper/bin/Hopper`. استخدم `HOPPER_LAUNCHER_PATH` للمسارات الأخرى. إذا كان الملف موجودًا لكن محرك التحليل لا يظهر، فشغّل `ldd /absolute/path/to/Hopper | grep 'not found'` على مسار Hopper الفعلي للتحقق من المكتبات الناقصة. راجع [دليل Hopper](docs/installation.md#hopper).
|
||||
|
||||
### التحديث والإزالة
|
||||
|
||||
- يحدّث `rea update` تثبيت REA الحالي.
|
||||
- يزيل `rea uninstall` تسجيلات الوكلاء وملفات سير العمل التي يديرها REA، ويترك Hopper مثبتًا.
|
||||
- يزيل `rea uninstall --purge-data` أيضًا ذاكرة REA المؤقتة وحالته. استخدمه فقط إذا أردت حذف هذه البيانات.
|
||||
|
||||
## حالة الدعم
|
||||
|
||||
تُوصف قدرات المستودع الحالية ومتطلبات الأنظمة في [دليل الدعم الإنجليزي](README.md#current-status). قد يسبق فرع main [إصدار npm](docs/installation.md#released-package-and-main).
|
||||
|
||||
- يوفر Ghidra 25 عملية للقراءة فقط على Linux x64 وmacOS x64/arm64 وحدود Windows x64 P0 التجريبية. يدعم Linux/macOS أيضًا تعليقات الدوال الذرية على مستوى الجلسة. نظام Windows P0 للقراءة فقط، ولا يوفر Ghidra أي تحكم في الواجهة.
|
||||
- يتطلب الفحص الثابت لنظام Android تجهيز JADX/Java بشكل منفصل؛ ويغطي التحقق بالموفر الحقيقي نظام Linux. راجع [تحليل Android](docs/android-analysis.md).
|
||||
- تحدد طلبات المتصفح وElectron والعمليات هدفها وإجراءاتها ودورة حياتها مباشرة، وتعتمد على وصول المضيف الفعلي دون منح أذونات منفصلة من REA. ما زالت كتابة إعدادات Setup وتثبيت Hopper تتطلب الموافقة على خطتها الدقيقة.
|
||||
- يصف `rea capabilities` عمليات جلسة الملف التنفيذي لا كل أدوات التطبيقات ووقت التشغيل. راجع قائمة أدوات MCP الكاملة والدليل ذا الصلة.
|
||||
|
||||
## استقصاء كامل بطلب واحد
|
||||
|
||||
بعد الإعداد، يمكنك أن تطلب من وكيلك:
|
||||
@@ -159,22 +213,28 @@ rea uninstall --purge-data
|
||||
|
||||
## منصة أدوات التحقيق
|
||||
|
||||
| عائلة الأدوات | العدد | أمثلة |
|
||||
| --------------------- | ----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| فحص الملفات التنفيذية | 36 | الدوال، والشيفرة شبه المصدرية، والتعليمات، والسلاسل، والأسماء، والمراجع، والتعليقات |
|
||||
| التحليل المركب | 13 | النظرة العامة، و`inspect_native_api`، وفك الترجمة الدفعي، ومخططات الاستدعاء، والمراجع، واكتشاف الأنواع |
|
||||
| أدوات macOS الأصلية | 5 | بيانات Mach-O والتوقيعات وملفات plist والمعماريات وفك رموز Swift دون تشغيل Hopper |
|
||||
| رسم القطع الأثرية | 3 | جرد حتمي للدلائل وZIP/APK/IPA وASAR واستخراج معاملاتي محدد صراحة |
|
||||
| Managed PE/CLI | 8 | هوية PE/CLI، أعضاء البيانات الوصفية، بصمات CIL، تصريحات P/Invoke/الحدود الأصلية والتحقق منها، إسقاط رسم التطبيق، استيراد إعادة البناء من فك الترجمة، إعادة ربط الرموز، خطط ترابط وقت التشغيل ومقارنة الإصدارات |
|
||||
| مراقبة المتصفح | 9 | التقاط CDP محدود الأصل وتحليل الحزم وخرائط المصدر واكتشاف WebMCP والفروق والأدلة المرئية |
|
||||
| تحليل Electron | 4 | مراقبة سلبية ضمن جذور canonical، ورسم ثابت ومحدود للتطبيق، ومطابقة ثابتة/وقت تشغيل قائمة على Evidence |
|
||||
| وقت تشغيل JavaScript | 2 | اكتشاف أهداف Node/Electron Inspector المعتمدة للاتصال فقط، ومراقبة محدودة للبرامج النصية وسياقات التنفيذ دون تقييم أو تجهيز |
|
||||
| سير عمل التطبيقات | 12 | تتبع محدود ومقارنة إصدارات فريدة ومقارنة ثابتة لأشكال قيم إرجاع الصادرات وإعادة تشغيل وحدات معزولة ومعتمدة على Linux وتوصيف وقت التشغيل المُدار وإغلاق تغطية إعادة البناء ودفاتر التزامات حتمية والتحقق الشامل من جاهزية إعادة البناء |
|
||||
| جلسة الملف التنفيذي | 19 | فتح الأهداف وحزم الأدلة وتشغيل آلة إعادة محدودة ومقارنة العمليات والقطع والدوال وسجل المجهولات المتبقية |
|
||||
| عائلة الأدوات | العدد | الاستخدام |
|
||||
| -------------------- | ----: | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| الفحص الأصلي | 41 | الدوال والشيفرة شبه المصدرية والتعليمات والسلاسل والرموز والاستدعاءات والمراجع والتعليقات وقراءة البايتات ومواضع الملفات |
|
||||
| سير عمل التحقيق | 14 | نظرات عامة على التطبيقات وملفات الدوال وواجهات API الأصلية والتوزيع وفك الترجمة الدفعي وتتبع الميزات ومسارات الاستدعاء ومخططاته واكتشاف Swift وObjective-C |
|
||||
| أدوات macOS الأصلية | 7 | بيانات Mach-O والتوقيعات وplist والمعماريات واستعادة أسماء Swift دون تشغيل Hopper |
|
||||
| مخطط المخرجات | 5 | جرد الدلائل والحزم وملفات Interface Builder المترجمة وكتالوجات موارد Apple والاستخراج |
|
||||
| PE/CLI المُدار | 7 | هوية تجميعات .NET وبياناتها الوصفية وتعليمات CIL والاعتماديات الأصلية واستيراد إعادة البناء ومقارنة الإصدارات |
|
||||
| البرامج الثابتة | 2 | فحص مناطق البرامج الثابتة على Linux واستخراجها الصريح |
|
||||
| Android APK | 5 | بيانات الحزمة وmanifest والبحث عن الأصناف وقوائم الأعضاء وفك ترجمة الأساليب والمراجع الثابتة |
|
||||
| مراقبة المتصفح | 11 | بنية الصفحات وبيانات الشبكة والبرامج النصية وخرائط المصدر واكتشاف WebMCP والصور ومقارنة الالتقاط |
|
||||
| تحليل Electron | 5 | مراقبة العارضين وخريطة التطبيق الثابتة والمواءمة بين نتائج الثبات والتشغيل |
|
||||
| وقت تشغيل JavaScript | 2 | اكتشاف أهداف Node/Electron Inspector ومواضع البرامج وأحداث سياقات التنفيذ |
|
||||
| سير عمل التطبيقات | 13 | تصدير النصوص البرمجية الملتقطة، تتبع الميزات عبر الطبقات ومقارنة الإصدارات ومواءمة المصدر التاريخي ومقارنة أشكال القيم المعادة والتحقق من إعادة الإنشاء |
|
||||
| مساحة العمل والملاحظة | 21 | الجلسات وحزم الأدلة وسياق التنقل ومقارنة العمليات والمخرجات والدوال وتسجيل الأسئلة غير المحسومة |
|
||||
|
||||
## خطة العمل
|
||||
|
||||
يشمل العمل التالي توسيع التحقق من الأهداف الأصلية، ومقارنة إصدارات JavaScript و.NET، ومراقبة وقت التشغيل والتحقق من إعادة الإنشاء. يتيح Setup بالفعل اختيار تكامل الوكيل وتثبيت Hopper. تثبيت أدوات تحليل أخرى ما زال عملًا لاحقًا. راجع [خطة التثبيت](docs/roadmap.md) و[تقييم أدوات التحليل](docs/provider-evaluation.md).
|
||||
|
||||
## استخدام REA مع وكلاء برمجة آخرين
|
||||
|
||||
يكتشف Setup كلاً من Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin، ويهيئ تلقائيًا أول ستة عملاء. أما Devin فيُبلّغ عن اكتشافه فقط ولا يُعدّله. يمكن لأي وكيل برمجة يدعم خوادم MCP المحلية استخدام REA بالإعداد التالي:
|
||||
يدعم Setup Claude Code وClaude Desktop وCodex وCursor وGemini CLI وWindsurf وDevin وOpenCode وAntigravity وGitHub Copilot CLI وCommand Code وVS Code. تكون تسجيلات REA الموجودة محددة افتراضياً، ويجب اختيار العملاء المكتشفين الآخرين يدوياً. يمكن لأي وكيل يدعم خوادم MCP المحلية الاتصال باستخدام الإعداد التالي:
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
@@ -183,7 +243,7 @@ rea uninstall --purge-data
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@2.7.0", "mcp"]
|
||||
"args": ["-y", "rea-agents@5.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,7 +264,7 @@ flowchart LR
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
يستخدم CLI وخادم MCP محرك التحليل نفسه. تغلق أوامر الطرفية التطبيق عند انتهائها، بينما تبقيه جلسة الوكيل مفتوحًا أثناء الاستقصاء.
|
||||
يستخدم CLI وخادم MCP سير عمل التحليل نفسه. تحرر أوامر الطرفية جلسة الجسر الخاصة بها عند انتهائها، بينما يمكن لجلسة الوكيل الحفاظ على الاتصال أثناء الاستقصاء. إغلاق جلسة REA لا يغلق تطبيق Hopper الذي يستخدمه المستخدم.
|
||||
|
||||
## أوامر CLI
|
||||
|
||||
@@ -221,20 +281,15 @@ npx -y rea-agents@latest compare /absolute/path/to/left-evidence.json /absolute/
|
||||
|
||||
## سلوك Hopper
|
||||
|
||||
يستطيع REA تشغيل Hopper تلقائيًا؛ لا حاجة إلى افتراض أنه يعمل مسبقًا. لكنه تطبيق macOS رسومي، لذلك قد يطلب التركيز أو يعرض حوارًا عندما يفرض Hopper قرارًا تفاعليًا. لا يمكن لـ REA ضمان تشغيل غير مرئي تمامًا لأن هذه الواجهة يملكها Hopper وmacOS.
|
||||
يشغّل REA برنامج Hopper عند الحاجة. على macOS قد تظهر نافذته في المقدمة أو يحتاج أحد الحوارات إلى استجابة منك. لا يمكن ضمان بقاء النافذة في الخلفية.
|
||||
|
||||
لتجنب مربع اختيار معمارية ملف شامل، حدد معطيات المحمل مسبقًا:
|
||||
|
||||
```bash
|
||||
export HOPPER_LOADER_ARGS_JSON='["-l", "Mach-O", "--aarch64"]'
|
||||
```
|
||||
|
||||
يدير REA دورة حياة Hopper ويحاول إغلاق العمليات التي بدأها عند انتهاء الأمر أو جلسة MCP.
|
||||
يحدد REA تنسيق الهدف ومعماريته لتجنب حوارات الاختيار المعتادة. عند إغلاق الجلسة، يوقف الجسر وينظف موارده الخاصة، لكنه لا يغلق تطبيق Hopper الذي تستخدمه.
|
||||
|
||||
## الأمان والخصوصية
|
||||
|
||||
- يبقى تحليل الملف التنفيذي محليًا بين REA ومزوّد التحليل المختار.
|
||||
- يبقى التحليل محليًا بين REA وموفر التحليل المختار عبر مقابس محلية خاصة، ومع IDA عبر تسجيل MCP المحلي المضبوط.
|
||||
- لا يرفع REA الأهداف إلى خدمة مستضافة.
|
||||
- تعمل أدوات التحليل والأهداف المطلقة بصلاحيات المستخدم نفسه؛ وما زال التقاط واجهة النظام على macOS يعتمد على الوصول إلى تسهيلات الاستخدام وتسجيل الشاشة. ولا ينفّذ التحليل الثابت للـ JavaScript الوحدات المستخرجة.
|
||||
- تستخدم جلسات Ghidra مشروعًا مؤقتًا معزولًا ولا تفتح مشاريع Ghidra التي يملكها المستخدم أو تعدّلها.
|
||||
- راجع الملفات التنفيذية غير الموثوقة واعزلها كما تفعل مع أي مدخل أصلي قد يكون ضارًا.
|
||||
- أبلغ عن الثغرات وفق [سياسة الأمان](SECURITY.md)، وليس عبر قضية عامة.
|
||||
@@ -258,7 +313,7 @@ export HOPPER_LOADER_ARGS_JSON='["-l", "Mach-O", "--aarch64"]'
|
||||
<details>
|
||||
<summary><strong>هل يعمل REA على Linux أو Windows؟</strong></summary>
|
||||
|
||||
نعم، يعمل REA على macOS 12+ وUbuntu 24.04+ وFedora 41+ وArch Linux 64 بت؛ Windows غير مدعوم حاليًا. يستخدم التحليل العميق Hopper أو مزوّد Ghidra للقراءة فقط الذي يختاره المستدعي؛ ويتطلب Ghidra على Linux الإصدار 12.1.2 وJDK 21 الكامل.
|
||||
يدعم REA نظام macOS 12+ وUbuntu 24.04+ وFedora 41+ وArch Linux بنواة 64 بت وCachyOS. يعمل تحليل Ghidra للقراءة فقط على Linux x64 وmacOS x64/arm64 مع Ghidra 12.1.x وJDK الكامل الذي يعلنه التثبيت (JDK 21 أو أحدث دون حد أعلى في إصدارات 12.1 الحالية)، إضافة إلى حد Windows x64 P0 التجريبي لتطبيقات PE الأصلية x86-64 على NTFS محلي؛ راجع [دليل Windows Ghidra P0](docs/windows-ghidra-p0.md) للنطاق الموثق.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
+128
-69
@@ -4,7 +4,7 @@
|
||||
|
||||
# REA:あらゆるものをリバースエンジニアリング
|
||||
|
||||
### コーディングエージェントがあらゆるものをリバースエンジニアリングするための、ひとつの CLI / MCP サーバー
|
||||
### エージェントでアプリの動作からネイティブバイナリまで調べる。
|
||||
|
||||
**気になる機能を見つけ、仕組みを理解し、自分の形で実装する。**
|
||||
|
||||
@@ -13,35 +13,53 @@
|
||||
[](#調査ツールカタログ)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[クイックスタート](#クイックスタート) · [バイナリから動作へ](#バイナリから動作へ) · [ツールカタログ](#調査ツールカタログ) · [仕組み](#仕組み) · [FAQ](#faq)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[ウェブサイト(英語)](https://morluto.github.io/rea/) · [ガイド](https://morluto.github.io/rea/guides/) · [DX-Ball の事例](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[クイックスタート](#クイックスタート) · [現在の対応状況](#現在の対応状況) · [バイナリから動作へ](#バイナリから動作へ) · [調査ツールカタログ](#調査ツールカタログ) · [ロードマップ](#ロードマップ) · [仕組み](#仕組み)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>リバースエンジニアリングのコミュニティに参加</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 質問と回答 · 成果の共有</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<code>curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash</code>
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="Hopper 内で解析ブリッジを起動し、ネイティブバイナリを調べる REA" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
アプリの気になる機能を自分のプロダクトにも取り入れたいと思ったことはありませんか。ソースコードがなくても、そのアプリをコーディングエージェントに渡せます。REA を使えば、エージェントが機能を調査して仕組みを理解し、あなたの技術スタック、デザイン、要件に合わせた形で実装できます。
|
||||
アプリの機能を自分のプロダクトにも取り入れたいときは、REA を使ってエージェントに調査を依頼できます。ソースコードがなくても、アプリを調べ、仕組みと根拠を示し、あなたのプロジェクト向けに同様の機能を実装できます。
|
||||
|
||||
REA はこの流れをひとつの CLI / MCP サーバーで実現します。エージェントはコンパイル済みのアプリを調べ、機能の流れを追い、分かったことを通常のコーディング作業に活用できます。複雑なリバースエンジニアリングツールは REA がひとつのインターフェースの裏側で扱います。
|
||||
REA はネイティブバイナリ、JavaScript/Electron アプリ、.NET アセンブリ、Web サイトの解析ツールを提供します。エージェントからもターミナルからも使えます。解析はローカルで実行され、結果には根拠と制限が含まれます。
|
||||
|
||||
Setup はエージェントを設定し、既存の Hopper または Ghidra に接続します。承認後に Hopper をインストールすることもできます。
|
||||
|
||||
## エージェントに頼むだけ
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
次のように依頼します:
|
||||
[セットアップ](#クイックスタート)後にエージェントを再起動し、次のように依頼します:
|
||||
|
||||
```text
|
||||
メモアプリをリバースエンジニアリングしてください。
|
||||
検索の仕組みと判断の理由を示し、私のプロジェクト向けに同様の機能を実装してください。
|
||||
メモアプリの検索機能を調べて根拠を示し、私のプロジェクト向けに同様の機能を実装してください。
|
||||
```
|
||||
|
||||
メモアプリは例にすぎません。調べたいアプリを指定するか、まず概要から始めるよう依頼できます。
|
||||
メモアプリを調べたいアプリに置き換えるか、まず概要を依頼してください。
|
||||
|
||||
## バイナリから動作へ
|
||||
|
||||
@@ -53,69 +71,104 @@ REA は調査をバイナリ上の根拠に結び付けます。元のソース
|
||||
|
||||
## REA を選ぶ理由
|
||||
|
||||
| | |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------- |
|
||||
| **エージェント向け** | コンパイル済みアプリについて質問し、推測ではなく根拠を集めさせることができます。 |
|
||||
| **CLI と MCP** | ターミナルとコーディングエージェントから同じリバースエンジニアリング機能を使えます。 |
|
||||
| **複雑さを処理** | ツール設定、アプリの読み込み、調査の維持、終了後のクリーンアップを REA が担います。 |
|
||||
| **一連の調査に対応** | 最初の概要から疑似コード、呼び出し関係、型、実装の手掛かりまで掘り下げられます。 |
|
||||
| **ローカルで解析** | 解析は Mac 上で実行され、REA がバイナリをホスト型解析サービスへ送ることはありません。 |
|
||||
| **コンテキストを維持** | 質問ごとに解析を最初からやり直さず、複数のバイナリを続けて調査できます。 |
|
||||
| | |
|
||||
| -------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| **エージェント向け** | アプリの動作について質問し、推測ではなくエージェントに調査させることができます。 |
|
||||
| **CLI と MCP** | ターミナルとエージェントから同じリバースエンジニアリング機能を使えます。 |
|
||||
| **ガイド付きセットアップ** | エージェントの設定、既存の解析ツールへの接続、承認後の Hopper インストールに対応します。 |
|
||||
| **洞察からコードへ** | 機能を理解したら、同じコーディングセッションで自分のプロダクト向けに実装できます。 |
|
||||
| **ローカルで解析** | 解析は対応するローカルホストで実行され、REA がバイナリをホスト型解析サービスへ送ることはありません。 |
|
||||
| **コンテキストを維持** | 質問ごとに解析を最初からやり直さず、複数のアプリを続けて調査できます。 |
|
||||
|
||||
## クイックスタート
|
||||
|
||||
### コーディングエージェントから(推奨)
|
||||
### セットアップを実行(推奨)
|
||||
|
||||
REA をエージェントで使うための設定を行います:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
エージェントに REA のセットアップを依頼してください。Mac を確認し、必要なインストールを説明して承認を求め、システムプロンプトを案内します。完全なツールを読み込むため再起動を求められた場合は、セットアップ後に再起動してください。
|
||||
Setup は最初に連携するエージェントを複数選択できるようにします。既存の REA 登録は初期選択されますが、検出されただけのクライアントは自動選択されず、未設定のクライアントも選べます。具体的なパスと変更内容を確認してから承認してください。選択したエージェントには通常 REA のワークフローをインストールします。Hopper は別の任意操作で、個別の承認が必要です。既存の Ghidra のパスも登録できます。
|
||||
|
||||
### 始める前に
|
||||
変更は事前に表示され、既存の設定はバックアップされます。要件と追加オプションは[インストールガイド](docs/installation.md)を参照してください。
|
||||
|
||||
### エージェントで使う
|
||||
|
||||
設定後にエージェントを再起動し、調べたいアプリや機能を説明します。REA は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code、VS Code に対応します。既存の REA 登録は初期選択され、それ以外の検出済みクライアントは選択が必要です。その他のエージェントは下記の MCP 設定を使えます。
|
||||
|
||||
Hopper はデモモードで使えます。初回起動の画面ではデモを選択するか、既存のライセンスを入力してください。
|
||||
|
||||
### AI コーディングアシスタント向けスキル(任意)
|
||||
|
||||
AI コーディングアシスタントにスキルを追加すると、より豊富なコンテキストが得られます:
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
このスキルは REA の調査ワークフローを提供します。上記の setup を実行して REA をエージェントに接続し、解析ツールを設定してください。Setup は既定でバージョンに合ったスキルをインストールします。このコマンドではリポジトリ版のスキルをインストールします。
|
||||
|
||||
展開済みの JavaScript/Electron アプリのツリーや ASAR であれば、MCP の設定もネイティブエンジンも不要です:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
パスを調査対象に置き換えてください(Windows では `"D:/apps/example"` のようなパス)。この静的ワークフローではそのようなパスも扱えます。ネイティブ解析には専用のエンジン設定が必要です。
|
||||
|
||||
### rea コマンドをインストール
|
||||
|
||||
コマンドラインツールをインストールします:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
Node.js と npm を先にインストールしてください。ターミナルで実行すると、インストーラーは `rea` を追加してセットアップを開始します。
|
||||
|
||||
npm でインストールし、セットアップを実行することもできます:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 要件
|
||||
|
||||
- macOS 12 以降
|
||||
- Ubuntu 24.04+、Fedora 41+、または 64 ビット Arch Linux
|
||||
- Node.js 22.19+ または 24.11+ と、Node に付属する npm
|
||||
- Ubuntu 24.04+、Fedora 41+、64 ビット Arch Linux、または CachyOS
|
||||
- Node.js 22.x (>=22.19)、24.x (>=24.11)、または 26+ と npm
|
||||
|
||||
`rea setup` は変更計画を表示し、確認後に適用します。Homebrew、Node.js、npm をインストールまたは更新しません。[Hopper](https://www.hopperapp.com/) がない場合は公式パッケージを提案します。Hopper は別製品で、ライセンスは別途必要です。
|
||||
ネイティブバイナリ解析には Hopper または Ghidra が必要です。Hopper は別製品です。デモにはベンダー所定の制限がありますが、有料ライセンスは必須ではありません。
|
||||
|
||||
64 ビット Linux に Ghidra 12.1.2 PUBLIC と完全な 64 ビット JDK 21 がすでにある場合、Setup は承認後に `GHIDRA_INSTALL_DIR` と任意の `JAVA_HOME` も登録できます。REA は Ghidra や Java をダウンロード、インストール、変更しません。Ghidra プロバイダーは隔離された読み取り専用 headless セッションで、インベントリ、逆コンパイル、アセンブリ、呼び出し関係、型付き参照、xref、CFG、関数 dossier を提供します。GUI 状態と変更操作は引き続き利用できません。
|
||||
Ghidra は Linux x64 と macOS x64/arm64 に対応します。Ghidra 12.1.x と、そのインストールが宣言する完全な 64 ビット JDK(`application.java.min` から `application.java.max` まで)を別途インストールし、REA で使うように設定してください。現行の 12.1 リリースは JDK 21 以降を要求し、上限は設定しません。ブリッジは Ghidra 12.1.4 と JDK 21 で検証しています。macOS ではホストのアーキテクチャに合うネイティブデコンパイラーも必要です。
|
||||
|
||||
#### Linux のインストールとトラブルシューティング
|
||||
Setup はインストールを確認し、パスを保存できます。Ghidra、Java、Node.js、npm、Homebrew のインストールや更新は行いません。
|
||||
|
||||
Ubuntu 24.04+、Fedora 41+、64 ビット Arch Linux では、REA が公式の DEB、RPM、または Arch パッケージを取得し、公開されたサイズとチェックサムを検証してから `apt-get`、`dnf`、または `pacman` で依存関係を解決します。root 以外では `pkexec` がシステム承認を表示します。REA は `sudo` を呼び出しません。
|
||||
リポジトリの main と npm 4.1.0 には、ローカル NTFS 上のネイティブ x86-64 PE アプリケーション(非マネージド、非 DLL 向け)を対象とする実験的な Windows x64 Ghidra P0 対応が含まれています。Job Object、プライベート DACL、パス許可の各制御が同梱されています。古い npm パッケージに期待する前に[リリース境界](docs/installation.md#released-package-and-main)を確認してください。前提条件と検証済みの範囲は [Windows Ghidra P0](docs/windows-ghidra-p0.md) を参照してください。
|
||||
|
||||
既定のランチャーは `/opt/hopper/bin/Hopper` です。別の場所では `HOPPER_LAUNCHER_PATH` を設定してください。Doctor が解析エンジン不足を報告した場合は `ldd /opt/hopper/bin/Hopper | grep 'not found'` を実行し、不足ライブラリを入れて `rea setup` を再実行します。REA は対応する Hopper デモ版を専用 Xvfb ディスプレイで起動し、解析セッションごとに Hopper が提示するデモモードを選択します。デスクトップの `DISPLAY` や有料ライセンスは不要ですが、デモ版にはベンダー所定の制限があります。`~/.local/bin` も `PATH` に追加してください。
|
||||
### トラブルシューティング
|
||||
|
||||
```bash
|
||||
# 1. REA をセットアップ
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
`npx -y rea-agents@latest doctor` はホスト、依存関係、解析ツール、エージェント設定を変更せずに確認します。構造化された診断には `--json` を追加してください。
|
||||
|
||||
macOS やインストーラーから確認を求められた場合は、その操作を完了してから同じコマンドをもう一度実行してください。
|
||||
Linux では実行可能な `/opt/hopper/bin/Hopper` を優先し、利用できない場合は `~/.local/share/rea/hopper/bin/Hopper` を自動的に確認します。それ以外の場所では `HOPPER_LAUNCHER_PATH` を設定します。ファイルがあるのに解析エンジンが見つからない場合は、実際の Hopper パスに対して `ldd /absolute/path/to/Hopper | grep 'not found'` を実行し、不足ライブラリを確認してください。詳しくは [Hopper ガイド](docs/installation.md#hopper)を参照してください。
|
||||
|
||||
### 2. コーディングエージェントを再起動
|
||||
### 更新とアンインストール
|
||||
|
||||
Setup は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin を検出します。検出した最初の 6 クライアントは自動設定しますが、Devin はローカル MCP 設定境界が文書化されていないため、検出結果を報告するだけで変更しません。設定されたクライアントを再起動して REA を読み込んでください。
|
||||
- `rea update` は現在の REA インストールを更新します。
|
||||
- `rea uninstall` は REA が管理するエージェント登録とワークフローファイルを削除します。Hopper は残ります。
|
||||
- `rea uninstall --purge-data` は REA のキャッシュと状態も削除します。それらを削除したい場合にだけ使ってください。
|
||||
|
||||
### 3. エージェントに依頼
|
||||
## 現在の対応状況
|
||||
|
||||
アプリは名前で指定できます。コーディングエージェントがアプリを探し、REA に必要なプログラムファイルを渡します。
|
||||
リポジトリの現在の機能とプラットフォーム要件は[英語の対応状況ガイド](README.md#current-status)で説明しています。main は [npm リリース](docs/installation.md#released-package-and-main)より先行する場合があります。
|
||||
|
||||
```text
|
||||
REA でメモアプリをリバースエンジニアリングしてください。検索機能の仕組みと
|
||||
根拠を示し、SQLite を使って私のプロジェクト向けに同様の機能を実装してください。
|
||||
```
|
||||
|
||||
問題がある場合は次を実行します:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
rea uninstall
|
||||
rea uninstall --purge-data
|
||||
```
|
||||
- Ghidra は Linux x64、macOS x64/arm64、実験的な Windows x64 P0 境界で 25 件の読み取り専用操作を提供します。Linux/macOS ではアトミックなセッション内関数注釈にも対応します。Windows P0 は読み取り専用であり、Ghidra に GUI 操作はありません。
|
||||
- 静的な Android 検査には持ち込みの JADX/Java が別途必要です。実プロバイダー検証は Linux を対象とします。詳しくは[Android 解析](docs/android-analysis.md)を参照してください。
|
||||
- ブラウザー、Electron、プロセスの各リクエストは対象・操作・ライフサイクルを直接指定し、ホストの実際のアクセスに依存します。REA 側の個別の許可付与は不要です。Setup による設定書き込みと Hopper のインストールは、引き続き正確な計画の承認が必要です。
|
||||
- `rea capabilities` はバイナリセッション操作の説明であり、すべてのアプリ/ランタイムツールの目録ではありません。接続済み MCP ツール一覧と関連ガイドを参照してください。
|
||||
|
||||
## ひとつのプロンプトで調査を完結
|
||||
|
||||
@@ -149,22 +202,28 @@ REA は手順 1〜5 のバイナリ解析を処理し、手順 6 はエージェ
|
||||
|
||||
## 調査ツールカタログ
|
||||
|
||||
| ツール群 | 数 | 例 |
|
||||
| ---------------------- | --: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| バイナリ調査 | 36 | プロシージャ、疑似コード、アセンブリ、文字列、名前、セグメント、callers、callees、xrefs、注釈 |
|
||||
| 合成解析 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift / ObjC 検出 |
|
||||
| macOS ネイティブ | 5 | Mach-O メタデータ、署名、plist、アーキテクチャ、Swift デマングル。Hopper 起動不要 |
|
||||
| アーティファクトグラフ | 3 | ディレクトリ、ZIP/APK/IPA、ASAR の決定的一覧と明示選択されたトランザクション抽出 |
|
||||
| Managed PE/CLI | 8 | PE/CLI 識別、メタデータメンバー、CIL ハッシュ、P/Invoke/ネイティブ境界宣言と検証、アプリケーショングラフ投影、逆コンパイル再構成インポート、構造的 token 再マッピング、ランタイム相関計画、バージョン比較 |
|
||||
| ブラウザ観察 | 9 | origin 限定 CDP 取得、bundle/source map 解析、WebMCP 検出、セッション、capture diff、視覚証拠 |
|
||||
| Electron 解析 | 4 | canonical ファイルルート内の受動観察、有界な静的アプリマッピング、Evidence に基づく静的/実行時の照合 |
|
||||
| JavaScript ランタイム | 2 | 承認済みのアタッチ専用 Node/Electron Inspector ターゲット検出と、評価や計装を行わない有界なスクリプト/実行コンテキスト観察 |
|
||||
| アプリケーション | 12 | 有界なクロスレイヤー追跡、一意限定のバージョン比較、静的なエクスポート戻り値形状比較、承認済み Linux 隔離 extracted-module replay、マネージドランタイム特性評価、再構築カバレッジ閉包、決定論的な義務台帳、エンドツーエンドの準備適合性検証 |
|
||||
| バイナリセッション | 19 | `open_binary`、`binary_session`、証拠バンドル、有限リプレイ状態機械、プロセス/成果物/関数比較、残余未知レジストリ |
|
||||
| ツール分類 | 数 | 用途 |
|
||||
| -------------------- | --: | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ネイティブ検査 | 41 | 関数、疑似コード、アセンブリ、文字列、シンボル、呼び出し、参照、注釈、バイト読み取り、ファイルオフセット |
|
||||
| 調査ワークフロー | 14 | アプリ概要、関数ドシエ、ネイティブ API とディスパッチ、一括逆コンパイル、機能トレース、コールパス、コールグラフ、Swift と Objective-C の検出 |
|
||||
| macOS ネイティブ | 7 | Hopper を起動せずに扱える Mach-O メタデータ、署名、plist、アーキテクチャ、Swift 名の復元 |
|
||||
| 成果物グラフ | 5 | ディレクトリとパッケージの目録、コンパイル済み Interface Builder ファイル、Apple アセットカタログ、抽出 |
|
||||
| マネージド PE/CLI | 7 | .NET の識別、メタデータ、CIL 命令、ネイティブ依存関係、再構築インポート、ビルド比較 |
|
||||
| ファームウェア | 2 | Linux ファームウェア領域の検査と明示的な展開 |
|
||||
| Android APK | 5 | パッケージと manifest の宣言、クラス検索、メンバー一覧、メソッド逆コンパイル、静的参照 |
|
||||
| ブラウザー観察 | 11 | ページ構成、ネットワークメタデータ、スクリプト、ソースマップ、WebMCP 検出、スクリーンショット、キャプチャ比較 |
|
||||
| Electron 解析 | 5 | レンダラー観察、静的アプリマッピング、静的/実行時結果の対応付け |
|
||||
| JavaScript 実行時 | 2 | Node/Electron Inspector 対象の検出、スクリプト位置、実行コンテキストイベント |
|
||||
| アプリワークフロー | 13 | 取得した Web スクリプトのエクスポート、層をまたぐ機能トレース、ビルド比較、履歴ソース対応、静的戻り値形状の比較、再実装検証 |
|
||||
| ワークスペースと観察 | 21 | セッション、証跡バンドル、ナビゲーション文脈、プロセス・成果物・関数比較、未解決事項の記録 |
|
||||
|
||||
## ロードマップ
|
||||
|
||||
次の作業はネイティブ対象の検証拡大、JavaScript と .NET のバージョン比較、実行時の観察と再実装の検証です。Setup はエージェント連携と Hopper のインストールを選択できます。他の解析ツールのインストールは今後の作業です。[インストールのロードマップ](docs/roadmap.md)と[解析ツールの評価](docs/provider-evaluation.md)を参照してください。
|
||||
|
||||
## 他のコーディングエージェントで使う
|
||||
|
||||
Setup は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin を検出し、最初の 6 クライアントを自動設定します。Devin は検出結果を報告するだけで変更しません。ローカル MCP サーバーに対応するコーディングエージェントなら、次の設定で REA を利用できます。
|
||||
Setup は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code、VS Code に対応します。既存の REA 登録は初期選択され、それ以外の検出済みクライアントは選択が必要です。ローカル MCP サーバーに対応するエージェントは、次の設定でも接続できます。
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
@@ -173,7 +232,7 @@ Setup は Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@2.7.0", "mcp"]
|
||||
"args": ["-y", "rea-agents@5.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,7 +252,7 @@ flowchart LR
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI と MCP サーバーは同じ解析エンジンを使います。ターミナルのコマンドは終了時にアプリを閉じ、エージェントのセッションは調査中アプリを開いたままにします。
|
||||
CLI と MCP サーバーは同じ解析処理を使います。ターミナルコマンドは完了後に自分のブリッジセッションを解放し、エージェントセッションは調査中の接続を維持できます。REA セッションを閉じても、ユーザーが利用中の Hopper は終了しません。
|
||||
|
||||
## CLI
|
||||
|
||||
@@ -229,7 +288,7 @@ REA は必要なときに Hopper を起動します。Hopper のランチャー
|
||||
|
||||
<details><summary><strong>Hopper を先に起動する必要がありますか?</strong></summary>
|
||||
|
||||
いいえ。REA が必要時に起動します。すでに起動している Hopper にも対応します。
|
||||
いいえ。REA が必要時に起動します。Hopper が起動済みでも使えますが、既存の GUI ドキュメントには接続せず、新しい解析ドキュメントを開きます。
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
+129
-69
@@ -4,7 +4,7 @@
|
||||
|
||||
# REA: 무엇이든 리버스 엔지니어링
|
||||
|
||||
### 코딩 에이전트가 무엇이든 리버스 엔지니어링할 수 있게 하는 하나의 CLI 및 MCP 서버
|
||||
### 에이전트로 앱의 동작부터 네이티브 바이너리까지 조사하세요.
|
||||
|
||||
**마음에 드는 기능을 찾고, 작동 방식을 이해하고, 원하는 방식으로 구현하세요.**
|
||||
|
||||
@@ -13,35 +13,54 @@
|
||||
[](#조사-도구-카탈로그)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[빠른 시작](#빠른-시작) · [바이너리에서 동작까지](#바이너리에서-동작까지) · [도구 카탈로그](#조사-도구-카탈로그) · [작동 방식](#작동-방식) · [FAQ](#faq)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[웹사이트(영문)](https://morluto.github.io/rea/) · [가이드](https://morluto.github.io/rea/guides/) · [DX-Ball 사례](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[빠른 시작](#빠른-시작) · [현재 지원 범위](#현재-지원-범위) · [바이너리에서 동작까지](#바이너리에서-동작까지) · [조사 도구 카탈로그](#조사-도구-카탈로그) · [로드맵](#로드맵) · [작동 방식](#작동-방식)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>리버스 엔지니어링 커뮤니티에 참여하세요</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 질문과 답변 · 작업 공유</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<code>curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash</code>
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="Hopper에서 분석 브리지를 시작하고 네이티브 바이너리를 조사하는 REA" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
어떤 앱에서 마음에 드는 기능을 자신의 제품에도 넣고 싶나요? 소스 코드가 없어도 앱을 코딩 에이전트에 전달할 수 있습니다. REA를 사용하면 에이전트가 기능을 조사하고 작동 방식을 이해한 뒤, 사용자의 기술 스택, 디자인, 요구 사항에 맞는 버전을 구현할 수 있습니다.
|
||||
앱에서 마음에 드는 기능을 찾았다면 에이전트에게 REA로 조사해 달라고 요청하세요. 소스 코드가 없어도 앱을 분석하고, 기능의 동작과 근거를 설명한 뒤, 프로젝트에 맞는 기능을 구현할 수 있습니다.
|
||||
|
||||
REA는 이 과정을 하나의 CLI 및 MCP 서버로 제공합니다. 에이전트는 컴파일된 앱을 조사하고 기능의 작동 방식을 추적한 뒤 알아낸 내용을 일반 코딩 작업에 활용할 수 있습니다. 복잡한 리버스 엔지니어링 도구는 REA가 하나의 인터페이스 뒤에서 처리합니다.
|
||||
REA는 네이티브 바이너리, JavaScript/Electron 앱, .NET 어셈블리, 웹사이트 분석 도구를 제공합니다. 에이전트와 터미널에서 같은 도구를 사용할 수 있습니다. 분석은 로컬에서 실행되며 결과에는 근거와 제한 사항이 포함됩니다.
|
||||
|
||||
Setup은 에이전트를 설정하고 기존 Hopper 또는 Ghidra 설치에 연결합니다. 승인 후 Hopper를 설치할 수도 있습니다.
|
||||
|
||||
## 에이전트에게 바로 요청하기
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
그런 다음 요청하세요.
|
||||
[설정](#빠른-시작)을 마친 뒤 에이전트를 다시 시작하고 요청하세요.
|
||||
|
||||
```text
|
||||
REA를 설정하고 메모 앱을 리버스 엔지니어링해 주세요. 검색 기능의 작동 방식과
|
||||
그렇게 판단한 이유를 보여 주고, 제 프로젝트에 비슷한 기능을 구현해 주세요.
|
||||
메모 앱의 검색 기능이 어떻게 동작하는지 조사하고 근거를 보여 주세요.
|
||||
그다음 제 프로젝트에 비슷한 기능을 구현해 주세요.
|
||||
```
|
||||
|
||||
메모 앱은 예시일 뿐입니다. 이해하고 싶은 앱을 지정하거나 먼저 개요부터 살펴보라고 요청할 수 있습니다.
|
||||
메모 앱 대신 조사할 앱을 지정하거나 먼저 개요를 요청할 수 있습니다.
|
||||
|
||||
## 바이너리에서 동작까지
|
||||
|
||||
@@ -53,69 +72,104 @@ REA는 분석을 바이너리 증거에 근거하게 합니다. 원본 소스
|
||||
|
||||
## REA를 사용하는 이유
|
||||
|
||||
| | |
|
||||
| ------------------ | ---------------------------------------------------------------------------------- |
|
||||
| **에이전트용** | 컴파일된 앱에 관해 질문하고 추측 대신 증거를 수집하게 합니다. |
|
||||
| **CLI와 MCP** | 터미널과 코딩 에이전트에서 동일한 리버스 엔지니어링 기능을 사용합니다. |
|
||||
| **복잡성 처리** | 도구 설정, 앱 열기, 조사 유지, 작업 후 정리를 REA가 처리합니다. |
|
||||
| **전체 조사 과정** | 첫 개요에서 의사 코드, 호출 관계, 타입, 구현 단서까지 이어서 조사합니다. |
|
||||
| **로컬 분석** | 분석은 Mac에서 실행되며 REA는 바이너리를 호스팅 분석 서비스에 업로드하지 않습니다. |
|
||||
| **컨텍스트 유지** | 질문마다 분석을 처음부터 시작하지 않고 여러 바이너리를 연속으로 조사합니다. |
|
||||
| | |
|
||||
| ------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| **에이전트용** | 앱의 동작에 대해 질문하고 추측 대신 에이전트가 조사하게 합니다. |
|
||||
| **CLI와 MCP** | 터미널과 에이전트에서 동일한 리버스 엔지니어링 기능을 사용합니다. |
|
||||
| **안내식 설정** | 에이전트 설정, 기존 분석 도구 연결, 승인 후 Hopper 설치를 지원합니다. |
|
||||
| **통찰에서 코드로** | 기능을 이해한 뒤 같은 코딩 세션에서 자신의 제품에 맞는 버전을 구현합니다. |
|
||||
| **로컬 분석** | 분석은 지원되는 로컬 호스트에서 실행되며 REA는 바이너리를 호스팅 분석 서비스에 업로드하지 않습니다. |
|
||||
| **컨텍스트 유지** | 질문마다 분석을 처음부터 시작하지 않고 여러 앱을 연속으로 조사합니다. |
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
### 코딩 에이전트에서 시작하기(권장)
|
||||
### 설정 실행하기(권장)
|
||||
|
||||
REA를 에이전트에 연결하세요.
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
에이전트에게 REA 설정을 요청하세요. Mac을 확인하고 필요한 설치 내용을 설명한 뒤 승인을 요청하며 시스템 프롬프트를 안내합니다. 전체 도구를 불러오기 위해 재시작하라고 하면 설정 후 에이전트를 재시작하세요.
|
||||
Setup은 먼저 연결할 에이전트를 여러 개 선택하도록 안내합니다. 기존 REA 등록은 기본 선택되며, 감지만 된 클라이언트는 자동 선택되지 않습니다. 아직 설정되지 않은 클라이언트도 직접 선택할 수 있습니다. 정확한 경로와 변경 사항을 검토한 뒤 승인하세요. 선택한 에이전트에는 기본적으로 REA 워크플로가 설치됩니다. Hopper는 별도의 선택 사항이며 따로 승인해야 합니다. 기존 Ghidra 경로도 등록할 수 있습니다.
|
||||
|
||||
### 시작하기 전에
|
||||
변경 전에 계획을 표시하고 기존 설정을 백업합니다. 요구 사항과 추가 옵션은 [설치 안내](docs/installation.md)를 참고하세요.
|
||||
|
||||
### 에이전트에서 사용하기
|
||||
|
||||
설정 후 에이전트를 다시 시작하고 조사할 앱이나 기능을 설명하세요. REA는 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI, Command Code, VS Code를 지원합니다. 기존 REA 등록은 기본 선택되고, 그 밖의 감지된 클라이언트는 직접 선택해야 합니다. 다른 에이전트는 아래 MCP 설정을 사용할 수 있습니다.
|
||||
|
||||
Hopper는 데모 모드로 사용할 수 있습니다. 첫 실행 안내가 나오면 데모를 선택하거나 기존 라이선스를 입력하세요.
|
||||
|
||||
### AI 코딩 어시스턴트용 스킬(선택)
|
||||
|
||||
AI 코딩 어시스턴트에 스킬을 추가하면 더 풍부한 컨텍스트를 얻을 수 있습니다.
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
이 스킬은 REA의 조사 워크플로를 제공합니다. 위 setup을 실행해 REA를 에이전트에 연결하고 분석 도구를 설정하세요. Setup은 기본적으로 버전에 맞는 스킬을 설치합니다. 이 명령은 저장소 버전의 스킬을 설치합니다.
|
||||
|
||||
압축을 해제한 JavaScript/Electron 앱 트리나 ASAR라면 MCP 설정이나 네이티브 엔진 없이 바로 분석할 수 있습니다.
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
경로를 조사 대상으로 바꾸세요(Windows에서는 `"D:/apps/example"` 같은 경로). 이 정적 워크플로에서는 이런 경로도 지원됩니다. 네이티브 분석에는 전용 엔진 설정이 필요합니다.
|
||||
|
||||
### rea 명령 설치하기
|
||||
|
||||
명령줄 도구를 설치하세요.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
Node.js와 npm이 먼저 설치되어 있어야 합니다. 터미널에서 실행하면 설치 프로그램이 `rea`를 설치하고 설정을 시작합니다.
|
||||
|
||||
npm으로 설치한 뒤 설정을 실행할 수도 있습니다.
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 요구 사항
|
||||
|
||||
- macOS 12 이상
|
||||
- Ubuntu 24.04+, Fedora 41+ 또는 64비트 Arch Linux
|
||||
- Node.js 22.19+ 또는 24.11+ 및 Node와 함께 제공되는 npm
|
||||
- Ubuntu 24.04+, Fedora 41+, 64비트 Arch Linux 또는 CachyOS
|
||||
- Node.js 22.x (>=22.19), 24.x (>=24.11) 또는 26+와 npm
|
||||
|
||||
`rea setup`은 전체 변경 계획을 표시하고 확인 후 적용합니다. Homebrew, Node.js 또는 npm을 설치하거나 업데이트하지 않습니다. [Hopper](https://www.hopperapp.com/)가 없으면 공식 패키지 설치를 제안합니다. Hopper는 별도 라이선스가 필요한 독립 소프트웨어입니다.
|
||||
네이티브 바이너리 분석에는 Hopper 또는 Ghidra가 필요합니다. Hopper는 별도 소프트웨어입니다. 데모에는 공급업체의 제한이 있지만 유료 라이선스가 필수는 아닙니다.
|
||||
|
||||
64비트 Linux에 Ghidra 12.1.2 PUBLIC과 완전한 64비트 JDK 21이 이미 설치되어 있다면 Setup은 승인 후 `GHIDRA_INSTALL_DIR`과 선택 사항인 `JAVA_HOME`도 등록할 수 있습니다. REA는 Ghidra나 Java를 다운로드, 설치 또는 수정하지 않습니다. Ghidra 공급자는 격리된 읽기 전용 headless 세션에서 인벤토리, 디컴파일, 어셈블리, 호출 관계, 형식이 지정된 참조, xref, CFG 및 함수 dossier를 제공합니다. GUI 상태와 변경 작업은 계속 사용할 수 없습니다.
|
||||
Ghidra는 Linux x64와 macOS x64/arm64를 지원합니다. Ghidra 12.1.x와 그 설치본이 선언한 완전한 64비트 JDK(`application.java.min`부터 `application.java.max`까지)를 별도로 설치한 뒤 REA가 사용하도록 설정하세요. 현재 12.1 릴리스는 JDK 21 이상을 요구하고 상한은 없습니다. 브리지는 Ghidra 12.1.4와 JDK 21에서 검증됩니다. macOS에서는 호스트 아키텍처에 맞는 네이티브 디컴파일러도 필요합니다.
|
||||
|
||||
#### Linux 설치 및 문제 해결
|
||||
Setup은 설치를 확인하고 경로를 저장할 수 있습니다. Ghidra, Java, Node.js, npm, Homebrew를 설치하거나 업데이트하지 않습니다.
|
||||
|
||||
Ubuntu 24.04+, Fedora 41+, 64비트 Arch Linux에서 REA는 공식 DEB, RPM 또는 Arch 패키지를 내려받아 게시된 크기와 체크섬을 검증한 뒤 `apt-get`, `dnf` 또는 `pacman`으로 의존성을 설치합니다. root가 아니면 `pkexec`가 시스템 승인 창을 표시합니다. REA는 `sudo`를 호출하지 않습니다.
|
||||
저장소 main과 npm 4.1.0에는 로컬 NTFS상의 네이티브 x86-64 PE 애플리케이션(비관리, 비 DLL 대상)을 위한 실험적인 Windows x64 Ghidra P0 지원이 포함되어 있습니다. Job Object, 비공개 DACL, 경로 승인 제어가 함께 제공됩니다. 이전 npm 패키지에 이 기능이 있을 것으로 기대하기 전에 [릴리스 경계](docs/installation.md#released-package-and-main)를 확인하세요. 전제 조건과 검증 범위는 [Windows Ghidra P0](docs/windows-ghidra-p0.md)를 참고하세요.
|
||||
|
||||
기본 실행 파일은 `/opt/hopper/bin/Hopper`입니다. 다른 위치에 설치했다면 `HOPPER_LAUNCHER_PATH`를 설정하세요. Doctor가 분석 엔진 누락을 보고하면 `ldd /opt/hopper/bin/Hopper | grep 'not found'`를 실행하고 누락된 라이브러리를 설치한 다음 `rea setup`을 다시 실행하세요. REA는 지원되는 Hopper 데모 빌드를 전용 Xvfb 디스플레이에서 실행하고 분석 세션마다 Hopper가 제공하는 데모 모드를 선택합니다. 데스크톱 `DISPLAY`나 유료 라이선스는 필요하지 않지만 데모에는 공급업체가 정한 제한이 있습니다. `~/.local/bin`도 `PATH`에 추가해야 합니다.
|
||||
### 문제 해결
|
||||
|
||||
```bash
|
||||
# 1. REA 설정
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
`npx -y rea-agents@latest doctor`는 호스트, 의존성, 분석 도구, 에이전트 설정을 변경 없이 확인합니다. 구조화된 진단에는 `--json`을 추가하세요.
|
||||
|
||||
macOS나 설치 프로그램이 확인을 요청하면 해당 절차를 완료하고 같은 명령을 다시 실행하세요.
|
||||
Linux에서는 실행 가능한 `/opt/hopper/bin/Hopper`를 우선 사용하고, 사용할 수 없으면 `~/.local/share/rea/hopper/bin/Hopper`를 자동으로 확인합니다. 다른 위치에는 `HOPPER_LAUNCHER_PATH`를 설정하세요. 파일이 있는데도 분석 엔진이 없다고 하면 실제 Hopper 경로에 대해 `ldd /absolute/path/to/Hopper | grep 'not found'`를 실행해 누락된 라이브러리를 확인하세요. 자세한 내용은 [Hopper 안내](docs/installation.md#hopper)를 참고하세요.
|
||||
|
||||
### 2. 코딩 에이전트 재시작
|
||||
### 업데이트와 제거
|
||||
|
||||
Setup은 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin을 감지합니다. 감지된 앞의 여섯 클라이언트는 자동으로 구성하지만, Devin은 문서화된 로컬 MCP 구성 경계가 없어 감지만 보고하고 변경하지 않습니다. 구성된 클라이언트를 다시 시작해 REA를 불러오세요.
|
||||
- `rea update`는 현재 REA 설치를 업데이트합니다.
|
||||
- `rea uninstall`은 REA가 관리하는 에이전트 등록과 워크플로 파일을 제거합니다. Hopper는 유지합니다.
|
||||
- `rea uninstall --purge-data`는 REA 캐시와 상태도 삭제합니다. 해당 데이터를 지우려는 경우에만 사용하세요.
|
||||
|
||||
### 3. 에이전트에게 요청
|
||||
## 현재 지원 범위
|
||||
|
||||
앱 이름만 말해도 됩니다. 코딩 에이전트가 앱을 찾아 REA에 필요한 프로그램 파일을 전달합니다.
|
||||
저장소의 현재 기능과 플랫폼 요구 사항은 [영문 지원 가이드](README.md#current-status)에 설명되어 있습니다. main은 [npm 릴리스](docs/installation.md#released-package-and-main)보다 앞설 수 있습니다.
|
||||
|
||||
```text
|
||||
REA로 메모 앱을 리버스 엔지니어링해 주세요. 검색 기능의 작동 방식과 증거를 보여 주고,
|
||||
SQLite를 사용해 제 프로젝트에 비슷한 기능을 구현해 주세요.
|
||||
```
|
||||
|
||||
문제가 있으면 다음을 실행하세요.
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
rea uninstall
|
||||
rea uninstall --purge-data
|
||||
```
|
||||
- Ghidra는 Linux x64, macOS x64/arm64, 실험적인 Windows x64 P0 경계에서 25개의 읽기 전용 작업을 제공합니다. Linux/macOS에서는 원자적 세션 함수 주석도 추가로 지원합니다. Windows P0는 읽기 전용이며 Ghidra에는 GUI 제어가 없습니다.
|
||||
- 정적 Android 검사는 별도로 준비한 JADX/Java가 필요합니다. 실제 제공자 검증은 Linux를 대상으로 합니다. [Android 분석](docs/android-analysis.md)을 참고하세요.
|
||||
- 브라우저, Electron, 프로세스 요청은 대상·동작·수명 주기를 직접 지정하며 호스트의 실제 접근에 의존합니다. 별도의 REA 권한 승인은 필요 없습니다. Setup의 설정 쓰기와 Hopper 설치는 여전히 정확한 계획에 대한 승인이 필요합니다.
|
||||
- `rea capabilities`는 모든 앱/런타임 도구의 목록이 아니라 바이너리 세션 작업을 설명합니다. 연결된 MCP 도구 목록과 관련 가이드를 참고하세요.
|
||||
|
||||
## 하나의 프롬프트로 전체 조사
|
||||
|
||||
@@ -149,22 +203,28 @@ REA는 1–5단계의 바이너리 분석을 처리합니다. 6단계는 에이
|
||||
|
||||
## 조사 도구 카탈로그
|
||||
|
||||
| 도구 그룹 | 수 | 예시 |
|
||||
| ------------------- | --: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 바이너리 검사 | 36 | 프로시저, 의사 코드, 어셈블리, 문자열, 이름, 세그먼트, callers, callees, xrefs, 주석 |
|
||||
| 합성 분석 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`, Swift 및 ObjC 탐색 |
|
||||
| macOS 네이티브 도구 | 5 | Mach-O 메타데이터, 코드 서명, plist, 아키텍처, Swift 디맹글링. Hopper 실행 불필요 |
|
||||
| 아티팩트 그래프 | 3 | 디렉터리, ZIP/APK/IPA, ASAR 결정적 인벤토리와 명시적으로 선택한 트랜잭션 추출 |
|
||||
| Managed PE/CLI | 8 | PE/CLI ID, 메타데이터 멤버, CIL 해시, P/Invoke/네이티브 경계 선언 및 검증, 애플리케이션 그래프 투영, 디컴파일 재구성 가져오기, 구조적 token 재매핑, 런타임 상관 계획, 버전 비교 |
|
||||
| 브라우저 관찰 | 9 | origin 제한 CDP 캡처, bundle/source map 분석, WebMCP 검색, 세션 타임라인, capture diff 및 시각 증거 |
|
||||
| Electron 분석 | 4 | canonical 파일 루트 내 수동 관찰, 제한된 정적 앱 매핑, Evidence 기반 정적/런타임 조정 |
|
||||
| JavaScript 런타임 | 2 | 승인된 연결 전용 Node/Electron Inspector 대상 탐색과 평가·계측 없는 제한된 스크립트 및 실행 컨텍스트 관찰 |
|
||||
| 애플리케이션 | 12 | 제한된 교차 계층 추적, 고유 일치 버전 비교, 정적 export 반환 형태 비교, 승인된 Linux 격리 extracted-module replay, 관리형 런타임 특성화, 재구성 커버리지 폐쇄, 결정론적 의무 원장, 엔드투엔드 준비 적합성 검증 |
|
||||
| 바이너리 세션 | 19 | `open_binary`, `binary_session`, 증거 번들, 유한 replay 상태 머신, 프로세스·아티팩트·함수 비교, 잔여 미지수 레지스트리 |
|
||||
| 도구 분류 | 수 | 용도 |
|
||||
| ----------------- | --: | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 네이티브 검사 | 41 | 함수, 의사 코드, 어셈블리, 문자열, 심볼, 호출, 참조, 주석, 바이트 읽기, 파일 오프셋 |
|
||||
| 조사 워크플로 | 14 | 앱 개요, 함수 기록서, 네이티브 API와 디스패치, 일괄 디컴파일, 기능 추적, 호출 경로, 호출 그래프, Swift와 Objective-C 탐색 |
|
||||
| macOS 네이티브 | 7 | Hopper 실행 없이 처리하는 Mach-O 메타데이터, 서명, plist, 아키텍처, Swift 이름 복원 |
|
||||
| 아티팩트 그래프 | 5 | 디렉터리와 패키지 목록, 컴파일된 Interface Builder 파일, Apple 애셋 카탈로그, 추출 |
|
||||
| 관리 PE/CLI | 7 | .NET 식별, 메타데이터, CIL 명령어, 네이티브 종속성, 재구성 가져오기, 빌드 비교 |
|
||||
| 펌웨어 | 2 | Linux 펌웨어 영역 검사 및 명시적 추출 |
|
||||
| Android APK | 5 | 패키지와 manifest 선언, 클래스 검색, 멤버 목록, 메서드 디컴파일, 정적 참조 |
|
||||
| 브라우저 관찰 | 11 | 페이지 구조, 네트워크 메타데이터, 스크립트, 소스 맵, WebMCP 탐색, 스크린샷, 캡처 비교 |
|
||||
| Electron 분석 | 5 | 렌더러 관찰, 정적 앱 매핑, 정적·런타임 결과 연결 |
|
||||
| JavaScript 런타임 | 2 | Node/Electron Inspector 대상 탐색, 스크립트 위치, 실행 컨텍스트 이벤트 |
|
||||
| 앱 워크플로 | 13 | 캡처한 웹 스크립트 내보내기, 계층 간 기능 추적, 빌드 비교, 히스토리 소스 매핑, 정적 반환 구조 비교, 재구현 검증 |
|
||||
| 작업 공간과 관찰 | 21 | 세션, 근거 번들, 탐색 컨텍스트, 프로세스·아티팩트·함수 비교, 미해결 항목 기록 |
|
||||
|
||||
## 로드맵
|
||||
|
||||
다음 작업은 네이티브 대상 검증 확대, JavaScript와 .NET의 버전 비교, 런타임 관찰과 재구현 검증입니다. Setup은 에이전트 연결과 Hopper 설치 선택을 이미 지원합니다. 다른 분석 도구 설치는 향후 작업입니다. [설치 로드맵](docs/roadmap.md)과 [분석 도구 평가](docs/provider-evaluation.md)를 참고하세요.
|
||||
|
||||
## 다른 코딩 에이전트에서 사용하기
|
||||
|
||||
Setup은 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin을 감지하고 앞의 여섯 클라이언트를 자동으로 구성합니다. Devin은 감지만 보고하고 변경하지 않습니다. 로컬 MCP 서버를 지원하는 코딩 에이전트라면 다음 설정으로 REA를 사용할 수 있습니다.
|
||||
Setup은 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI, Command Code, VS Code를 지원합니다. 기존 REA 등록은 기본 선택되고, 그 밖의 감지된 클라이언트는 직접 선택해야 합니다. 로컬 MCP 서버를 지원하는 에이전트는 다음 설정으로 연결할 수 있습니다.
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
@@ -173,7 +233,7 @@ Setup은 Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@2.7.0", "mcp"]
|
||||
"args": ["-y", "rea-agents@5.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,7 +253,7 @@ flowchart LR
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI와 MCP 서버는 같은 분석 엔진을 사용합니다. 터미널 명령은 끝날 때 앱을 닫고, 에이전트 세션은 조사하는 동안 앱을 열어 둡니다.
|
||||
CLI와 MCP 서버는 같은 분석 워크플로를 사용합니다. 터미널 명령은 완료 후 자신의 브리지 세션을 해제하고, 에이전트 세션은 조사 중 연결을 유지할 수 있습니다. REA 세션을 닫아도 사용자가 이용 중인 Hopper 앱은 종료하지 않습니다.
|
||||
|
||||
## CLI
|
||||
|
||||
@@ -229,7 +289,7 @@ REA는 필요할 때 Hopper를 시작합니다. Hopper 런처는 내부적으로
|
||||
|
||||
<details><summary><strong>Hopper를 미리 실행해야 하나요?</strong></summary>
|
||||
|
||||
아니요. REA가 필요할 때 시작하며 이미 실행 중인 Hopper도 지원합니다.
|
||||
아니요. REA가 필요할 때 시작합니다. Hopper가 실행 중이어도 사용할 수 있지만 기존 GUI 문서에 연결하는 대신 새 분석 문서를 엽니다.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
+129
-70
@@ -4,7 +4,7 @@
|
||||
|
||||
# REA:逆向工程一切
|
||||
|
||||
### 一个 CLI 与 MCP 服务器,让编程智能体逆向工程任何程序
|
||||
### 使用智能体逆向工程任何程序,从应用行为到原生二进制文件。
|
||||
|
||||
**看到喜欢的功能。理解它的原理。按照你的方式实现。**
|
||||
|
||||
@@ -13,35 +13,53 @@
|
||||
[](#调查工具目录)
|
||||
[](https://nodejs.org/)
|
||||
[](LICENSE)
|
||||
[](https://discord.gg/GkcryMnJDM)
|
||||
|
||||
[快速开始](#快速开始) · [从二进制到行为](#从二进制到行为) · [工具目录](#调查工具目录) · [工作原理](#工作原理) · [常见问题](#常见问题)
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[网站(英文)](https://morluto.github.io/rea/) · [使用指南](https://morluto.github.io/rea/guides/) · [DX-Ball 案例](https://morluto.github.io/rea/showcase/dx-ball/)**
|
||||
|
||||
[快速开始](#快速开始) · [当前状态](#当前状态) · [从二进制到行为](#从二进制到行为) · [调查工具目录](#调查工具目录) · [路线图](#路线图) · [工作原理](#工作原理)
|
||||
|
||||
<table aria-label="REA community">
|
||||
<tr>
|
||||
<td align="center" width="360">
|
||||
<a href="https://discord.gg/GkcryMnJDM">
|
||||
<img src="docs/assets/discord.svg" height="42" alt="Discord" /><br />
|
||||
<strong>加入逆向工程社区</strong>
|
||||
</a><br />
|
||||
<sub>Discord · 问答 · 成果分享</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
<code>curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash</code>
|
||||
<code>npx rea-agents setup</code>
|
||||
|
||||
<br />
|
||||
|
||||
<img src="docs/assets/rea-hopper-analysis.png" alt="REA 在 Hopper 中启动分析桥并检查原生二进制文件" width="1200" />
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
看到某个应用中想加入自己产品的功能?即使没有源代码,也可以把应用交给编程智能体。借助 REA,智能体能够调查该功能、理解其工作原理,并按照你的技术栈、设计和需求构建适合你产品的版本。
|
||||
看到某个应用中想加入自己产品的功能?让智能体用 REA 调查它。即使没有源代码,智能体也可以检查应用、解释功能的工作方式、展示证据,并为你的项目实现类似功能。
|
||||
|
||||
REA 通过一个 CLI 与 MCP 服务器实现这套流程。智能体可以检查编译后的应用、追踪功能的工作方式,并把了解到的内容用于日常编码。REA 在一个接口背后处理复杂的逆向工程工具。
|
||||
REA 提供原生二进制、JavaScript 与 Electron 应用、.NET 程序集和网站的分析工具。你可以从智能体或终端使用它们。分析在本机运行,结果会说明证据和限制。
|
||||
|
||||
Setup 可以配置智能体、连接已安装的 Hopper 或 Ghidra,也可以在你批准后安装 Hopper。
|
||||
|
||||
## 直接询问智能体
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
然后说:
|
||||
完成[设置](#快速开始)后,重启智能体并提出问题:
|
||||
|
||||
```text
|
||||
逆向工程“备忘录”应用。解释搜索功能的工作方式,
|
||||
说明你是如何得出结论的,然后为我的项目构建类似功能。
|
||||
解释“备忘录”应用的搜索功能,展示证据,然后为我的项目实现类似功能。
|
||||
```
|
||||
|
||||
“备忘录”只是示例。你可以说出任何想了解的应用,也可以让智能体先从概览开始。
|
||||
把“备忘录”替换成你想了解的应用,也可以先请求概览。
|
||||
|
||||
## 从二进制到行为
|
||||
|
||||
@@ -53,69 +71,104 @@ REA 让调查始终以二进制证据为依据。它不会声称能恢复原始
|
||||
|
||||
## 为什么选择 REA
|
||||
|
||||
| | |
|
||||
| ---------------- | -------------------------------------------------------------- |
|
||||
| **为智能体设计** | 直接询问编译后应用的行为,让智能体搜集证据,而不是猜测。 |
|
||||
| **CLI 与 MCP** | 在终端或编程智能体中使用同一套逆向工程能力。 |
|
||||
| **处理复杂流程** | REA 负责工具设置、打开应用、维持调查过程,并在完成后清理资源。 |
|
||||
| **完整工作流** | 从初步概览推进到伪代码、调用关系、类型和实现线索。 |
|
||||
| **本地运行** | 分析在你的 Mac 上运行;REA 不会把二进制上传到托管式分析服务。 |
|
||||
| **保留上下文** | 连续调查多个二进制文件,无需为每个问题重新开始整个分析过程。 |
|
||||
| | |
|
||||
| ---------------- | -------------------------------------------------------------------- |
|
||||
| **为智能体设计** | 直接询问应用的行为,让智能体去检查,而不是猜测。 |
|
||||
| **CLI 与 MCP** | 在终端或智能体中使用同一套逆向工程能力。 |
|
||||
| **引导式设置** | 配置智能体、连接已有分析工具,或在你批准后安装 Hopper。 |
|
||||
| **从洞察到代码** | 理解功能后,在同一次编程会话中构建属于你自己的版本。 |
|
||||
| **本地运行** | 分析在受支持的本机系统上运行;REA 不会把二进制上传到托管式分析服务。 |
|
||||
| **保留上下文** | 连续调查多个应用,无需为每个问题重新开始整个分析过程。 |
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 使用编程智能体(推荐)
|
||||
### 运行设置(推荐)
|
||||
|
||||
配置 REA 与智能体的连接:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
让智能体设置 REA。它会检查你的 Mac,说明需要安装的内容,先征得同意,并引导你处理系统提示。如果智能体要求重启以加载完整工具,请在设置后重启。
|
||||
Setup 首先让你多选要连接的智能体。已有 REA 注册默认选中;仅被检测到的客户端不会自动选中,也可以手动选择尚未配置的客户端。查看具体路径和变更后再批准。所选智能体默认安装 REA 工作流;Hopper 是单独的可选操作,需要单独批准。Setup 也可以记录现有 Ghidra 的路径。
|
||||
|
||||
### 开始之前
|
||||
Setup 会先展示变更并备份已有配置。要求与更多选项见[安装与设置](docs/installation.md)。
|
||||
|
||||
### 使用智能体
|
||||
|
||||
设置完成后,重启智能体并描述你想了解的应用或功能。REA 支持 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code 和 VS Code。已有 REA 注册默认选中;其他检测到的客户端需手动选择。其他智能体可以使用下方的 MCP 配置。
|
||||
|
||||
Hopper 支持演示模式。如果首次启动时出现提示,选择演示模式或输入已有许可证。
|
||||
|
||||
### AI 编程助手技能(可选)
|
||||
|
||||
为你的 AI 编程助手添加 skill,以获得更丰富的上下文:
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
该 skill 提供 REA 的调查工作流。先运行上文的 setup,把 REA 连接到你的智能体并配置分析工具。Setup 默认会安装版本匹配的 skill;这条命令安装的是仓库版本。
|
||||
|
||||
对于已解包的 JavaScript/Electron 应用目录或 ASAR,无需 MCP 配置,也无需原生引擎:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
把路径换成你的目标(例如 Windows 上的 `"D:/apps/example"`)。这条静态流程支持此类路径。原生分析仍需要先配置对应的分析引擎。
|
||||
|
||||
### 安装 rea 命令
|
||||
|
||||
安装命令行工具:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
```
|
||||
|
||||
需要预先安装 Node.js 和 npm。在终端中运行时,安装程序会安装 `rea` 并启动设置。
|
||||
|
||||
也可以用 npm 安装,再运行设置:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
rea setup
|
||||
```
|
||||
|
||||
### 系统要求
|
||||
|
||||
- macOS 12 或更高版本
|
||||
- Ubuntu 24.04+、Fedora 41+ 或 64 位 Arch Linux
|
||||
- Node.js 22.19+ 或 24.11+,以及随 Node 安装的 npm
|
||||
- Ubuntu 24.04+、Fedora 41+、64 位 Arch Linux 或 CachyOS
|
||||
- Node.js 22.x (>=22.19)、24.x (>=24.11) 或 26+,以及 npm
|
||||
|
||||
`rea setup` 会先显示完整变更计划并请求确认。它不会安装或更新 Homebrew、Node.js 或 npm。缺少 [Hopper](https://www.hopperapp.com/) 时,Setup 会提议安装官方软件包;Hopper 是需要单独许可证的独立软件。
|
||||
原生二进制分析需要 Hopper 或 Ghidra。Hopper 是独立软件;演示模式有厂商规定的限制,不要求购买许可证。
|
||||
|
||||
如果你已在 64 位 Linux 上安装 Ghidra 12.1.2 PUBLIC 和完整的 64 位 JDK 21,Setup 也可以在批准后登记 `GHIDRA_INSTALL_DIR` 和可选的 `JAVA_HOME`。REA 不会下载、安装或修改 Ghidra 或 Java。Ghidra provider 在隔离的只读 headless 会话中提供清单、反编译、汇编、调用关系、带类型的引用、xref、CFG 与函数 dossier;GUI 状态和修改操作仍不可用。
|
||||
Ghidra 支持 Linux x64 和 macOS x64/arm64。单独安装 Ghidra 12.1.x,以及该安装在 `application.java.min` 到 `application.java.max` 中声明的完整 64 位 JDK,然后配置 REA 使用它们。当前 12.1 发行版要求 JDK 21 或更新版本,并且不设置上限。桥接在 Ghidra 12.1.4 和 JDK 21 上完成验证。macOS 还需要对应架构的原生反编译器。
|
||||
|
||||
#### Linux 安装与故障排除
|
||||
Setup 可以验证安装并保存路径,不会安装或升级 Ghidra、Java、Node.js、npm 或 Homebrew。
|
||||
|
||||
在 Ubuntu 24.04+、Fedora 41+ 和 64 位 Arch Linux 上,REA 会下载 Hopper 官方的 DEB、RPM 或 Arch 软件包,验证发布方提供的大小和校验和,然后通过 `apt-get`、`dnf` 或 `pacman` 安装依赖。非 root 运行时,`pkexec` 会显示系统授权提示;REA 不调用 `sudo`。
|
||||
仓库 main 分支与 npm 4.1.0 已包含实验性的 Windows x64 Ghidra P0 支持,适用于本地 NTFS 上的原生 x86-64 PE 应用(非托管、非 DLL),并附带 Job Object、私有 DACL 与路径准入控制。请先确认[发布边界](docs/installation.md#released-package-and-main),再假定旧版 npm 包具备该能力。详见 [Windows Ghidra P0](docs/windows-ghidra-p0.md) 的前置条件与已验证范围。
|
||||
|
||||
默认启动器是 `/opt/hopper/bin/Hopper`。自定义安装可设置 `HOPPER_LAUNCHER_PATH`。如果 Doctor 报告缺少分析引擎,请运行 `ldd /opt/hopper/bin/Hopper | grep 'not found'`,安装缺少的系统库后重新运行 `rea setup`。REA 会在私有 Xvfb 显示器上运行受支持的 Hopper 演示版,并为每个分析会话选择 Hopper 提供的演示模式;无需桌面 `DISPLAY` 或付费许可证,但演示版受厂商限制。Linux 上还应确保 `~/.local/bin` 位于 `PATH` 中。
|
||||
### 故障排除
|
||||
|
||||
```bash
|
||||
# 1. 安装和配置 REA
|
||||
curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh | bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
运行 `npx -y rea-agents@latest doctor`,检查主机、依赖、分析工具和智能体配置。该命令不会修改文件。添加 `--json` 可获取结构化诊断。
|
||||
|
||||
如果 macOS 或安装程序要求确认,请完成提示,然后再次运行同一命令。
|
||||
Linux 上 REA 会优先使用可执行的 `/opt/hopper/bin/Hopper`;如果不可用,则自动检查 `~/.local/share/rea/hopper/bin/Hopper`。其他路径可通过 `HOPPER_LAUNCHER_PATH` 指定。如果文件存在但仍报告缺少分析引擎,请对实际 Hopper 路径运行 `ldd /absolute/path/to/Hopper | grep 'not found'` 检查缺少的系统库。安装详情见 [Hopper 指南](docs/installation.md#hopper)。
|
||||
|
||||
### 2. 重启编程智能体
|
||||
### 更新与卸载
|
||||
|
||||
Setup 会检测 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf 和 Devin。它会自动配置检测到的前六种客户端;由于目前没有明确的本地 MCP 配置边界,检测到 Devin 时只会报告而不会修改。重启已配置的客户端,让它加载 REA。
|
||||
- `rea update` 更新当前 REA 安装。
|
||||
- `rea uninstall` 移除 REA 管理的智能体配置项和工作流文件,保留 Hopper。
|
||||
- `rea uninstall --purge-data` 还会删除 REA 的缓存和状态。仅在需要移除这些数据时使用。
|
||||
|
||||
### 3. 直接询问智能体
|
||||
## 当前状态
|
||||
|
||||
你可以直接说出应用名称。编程智能体会找到应用,并把 REA 所需的程序文件交给它。
|
||||
仓库当前能力与平台要求见[英文支持指南](README.md#current-status)。main 分支可能领先于 [npm 发布版](docs/installation.md#released-package-and-main)。
|
||||
|
||||
```text
|
||||
使用 REA 逆向工程“备忘录”应用。解释搜索功能的工作方式,展示证据,
|
||||
然后使用 SQLite 为我的项目构建一个类似功能。
|
||||
```
|
||||
|
||||
如果遇到问题,请运行:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest doctor
|
||||
rea uninstall
|
||||
rea uninstall --purge-data
|
||||
```
|
||||
- Ghidra 在 Linux x64、macOS x64/arm64 以及实验性的 Windows x64 P0 边界上提供 25 个只读操作。Linux/macOS 额外支持原子化的会话级函数注释。Windows P0 为只读;Ghidra 不提供 GUI 控制。
|
||||
- 静态 Android 检查需要自备 JADX/Java,前置条件独立;真机验证覆盖 Linux。详见 [Android 分析](docs/android-analysis.md)。
|
||||
- 浏览器、Electron 与进程请求需直接声明目标、动作与生命周期,并依赖宿主的实际访问权限,无需 REA 另行授权。Setup 的配置写入与 Hopper 安装仍需逐项批准确切计划。
|
||||
- `rea capabilities` 描述的是二进制会话操作,而非全部应用/运行时工具。请以已连接 MCP 工具列表与相关指南为准。
|
||||
|
||||
## 一个提示词,完成一次完整调查
|
||||
|
||||
@@ -149,22 +202,28 @@ REA 负责第 1–5 步中的二进制分析。第 6 步由智能体使用其常
|
||||
|
||||
## 调查工具目录
|
||||
|
||||
| 工具类别 | 数量 | 示例 |
|
||||
| ----------------- | ---: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 二进制检查 | 36 | 过程、伪代码、汇编、字符串、名称、段、调用者、被调用者、交叉引用、注释 |
|
||||
| 组合分析 | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `get_call_graph`, `find_xrefs_to_name`、Swift 与 ObjC 发现 |
|
||||
| macOS 原生工具 | 5 | Mach-O 元数据、代码签名、plist、架构与 Swift 符号还原,无需启动 Hopper |
|
||||
| 制品图谱 | 3 | 确定性目录、ZIP/APK/IPA 与 ASAR 清单,以及显式选择的事务式提取 |
|
||||
| Managed PE/CLI | 8 | PE/CLI 身份、元数据成员、CIL 哈希、P/Invoke/原生边界声明与验证、应用图谱投影、反编译重建导入、结构化 token 重映射、运行时关联计划与跨版本比较 |
|
||||
| 浏览器观察 | 9 | 精确来源 CDP 抓取、bundle/source map 分析、WebMCP 发现、会话时间线、capture diff 与视觉证据 |
|
||||
| Electron 分析 | 4 | 限定 canonical 文件根的被动观察、有界静态应用映射,以及基于 Evidence 的静态/运行时对账 |
|
||||
| JavaScript 运行时 | 2 | 经批准的只附加 Node/Electron Inspector 目标发现,以及不执行求值或插桩的有界脚本和执行上下文观察 |
|
||||
| 应用工作流 | 12 | 有界跨层追踪、唯一匹配跨版本图比较、静态导出返回形状比较、经批准的 Linux 隔离 extracted-module replay、托管运行时表征、重建覆盖闭合、确定性义务账本和端到端就绪性一致性验证 |
|
||||
| 二进制会话 | 19 | `open_binary`、`binary_session`、证据包、有限重放状态机、进程、制品与函数比较、残余未知项注册表 |
|
||||
| 工具类别 | 数量 | 用途 |
|
||||
| ----------------- | ---: | ------------------------------------------------------------------------------------------------------ |
|
||||
| 原生检查 | 41 | 函数、伪代码、汇编、字符串、符号、调用、引用、注释、字节读取与文件偏移 |
|
||||
| 调查工作流 | 14 | 应用概览、函数档案、原生 API 与分发、批量反编译、功能追踪、调用路径与调用图、Swift 与 Objective-C 发现 |
|
||||
| macOS 原生工具 | 7 | Mach-O 元数据、代码签名、plist、架构与 Swift 符号还原,无需启动 Hopper |
|
||||
| 产物图 | 5 | 目录与软件包清单、编译后的 Interface Builder 文件、Apple 资源目录与提取 |
|
||||
| 托管 PE/CLI | 7 | .NET 程序集身份、元数据、CIL 指令、原生依赖声明、重建导入与构建比较 |
|
||||
| 固件 | 2 | Linux 固件区域检查与显式提取 |
|
||||
| Android APK | 5 | 包与 manifest 声明、类搜索、成员清单、方法反编译与静态引用 |
|
||||
| 浏览器观察 | 11 | 页面结构、网络元数据、脚本、来源映射、WebMCP 发现、截图与捕获比较 |
|
||||
| Electron 分析 | 5 | 渲染进程观察、静态应用映射、静态/运行时结果关联 |
|
||||
| JavaScript 运行时 | 2 | Node/Electron Inspector 目标发现、脚本位置与执行上下文事件 |
|
||||
| 应用工作流 | 13 | 捕获的网站脚本导出、跨层功能追踪、构建比较、历史源码映射、静态返回结构比较与重建验证 |
|
||||
| 工作区与观察 | 21 | 会话、证据包、导航上下文、进程/产物/函数比较与待解决问题跟踪 |
|
||||
|
||||
## 路线图
|
||||
|
||||
接下来的工作包括更多原生目标验证、JavaScript 与 .NET 跨版本比较,以及运行时观察和重建验证。当前设置已支持选择智能体集成和 Hopper 安装;安装其他分析工具仍是后续工作。详见[安装路线图](docs/roadmap.md)和[分析工具评估](docs/provider-evaluation.md)。
|
||||
|
||||
## 与其他编程智能体一起使用
|
||||
|
||||
Setup 会检测 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf 和 Devin,并自动配置检测到的前六种客户端;检测到 Devin 时只会报告而不会修改。任何支持本地 MCP 服务器的编程智能体都可以使用以下配置连接 REA。
|
||||
Setup 支持 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Windsurf、Devin、OpenCode、Antigravity、GitHub Copilot CLI、Command Code 和 VS Code。已有 REA 注册默认选中;其他检测到的客户端需手动选择。任何支持本地 MCP 服务器的智能体都可以使用以下配置连接 REA。
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
@@ -173,7 +232,7 @@ Setup 会检测 Claude Code、Claude Desktop、Codex、Cursor、Gemini CLI、Win
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@2.7.0", "mcp"]
|
||||
"args": ["-y", "rea-agents@5.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,7 +252,7 @@ flowchart LR
|
||||
Ghidra --> App
|
||||
```
|
||||
|
||||
CLI 与 MCP 服务器使用相同的分析引擎。终端命令完成后会关闭应用;智能体会话则会在调查期间保持应用打开。
|
||||
CLI 与 MCP 服务器使用相同的分析流程。终端命令完成后释放自己创建的桥接会话;智能体会话可以在调查期间保持连接。关闭 REA 会话不会退出用户正在使用的 Hopper 应用。
|
||||
|
||||
## CLI
|
||||
|
||||
@@ -223,7 +282,7 @@ REA 会推导明确的格式和架构参数,以避免常见的 FAT 与 ARM 选
|
||||
|
||||
## 安全模型
|
||||
|
||||
每个桥会话都使用随机能力令牌和仅限当前用户的 Unix 套接字。协议消息有大小限制,面向调用者的错误不会暴露启动器 stderr 或内部异常原因。Ghidra 会话还使用隔离的临时项目,不会打开或修改用户自己的 Ghidra 项目。
|
||||
每个桥会话都使用随机能力令牌和仅限当前用户的 Unix 套接字。诊断保留本机路径、摘要和错误位置等排查信息,同时移除凭据和认证令牌。Ghidra 会话还使用隔离的临时项目,不会打开或修改用户自己的 Ghidra 项目。
|
||||
|
||||
这不是沙箱,也无法防御以同一操作系统用户身份运行的恶意进程。打开不可信二进制文件会让所选本地提供商以当前用户权限进行解析和分析。请按照 [SECURITY.md](SECURITY.md) 中的私密流程报告漏洞。
|
||||
|
||||
@@ -231,7 +290,7 @@ REA 会推导明确的格式和架构参数,以避免常见的 FAT 与 ARM 选
|
||||
|
||||
<details><summary><strong>Hopper 是否需要提前运行?</strong></summary>
|
||||
|
||||
不需要。REA 会在操作需要时启动 Hopper,也支持已经运行的 Hopper。
|
||||
不需要。REA 会在操作需要时启动 Hopper。Hopper 已经运行时也可以使用,但 REA 会打开新的分析文档,不会连接并复用现有 GUI 文档。
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
import com.atxx.jhmcp.*;
|
||||
import com.google.gson.*;
|
||||
import io.modelcontextprotocol.kotlin.sdk.server.*;
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.*;
|
||||
import jadx.api.*;
|
||||
import jadx.core.dex.nodes.*;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
import kotlin.Unit;
|
||||
import kotlin.coroutines.EmptyCoroutineContext;
|
||||
import kotlinx.coroutines.BuildersKt;
|
||||
import kotlinx.io.CoreKt;
|
||||
import kotlinx.io.JvmCoreKt;
|
||||
import kotlinx.serialization.json.JsonObject;
|
||||
|
||||
/** REA-owned metadata boundary for the unmodified jadx-headless-mcp 0.7.1 JAR. */
|
||||
class ReaJadxBridge {
|
||||
private static final Gson JSON = new Gson();
|
||||
private final SessionHolder holder;
|
||||
private JadxSession indexedSession;
|
||||
private Map<String, List<ClassSnapshot>> classes = Map.of();
|
||||
|
||||
private record MethodSnapshot(String name, String shortId, Map<String, Object> summary) {}
|
||||
private record ClassSnapshot(JavaClass node, List<MethodSnapshot> methods, Map<String, Object> summary) {}
|
||||
|
||||
private ReaJadxBridge(SessionHolder holder) { this.holder = holder; }
|
||||
|
||||
private static CallToolResult text(String value, boolean failed) {
|
||||
return new CallToolResult(List.of(new TextContent(value, null, null)), failed, null, null);
|
||||
}
|
||||
|
||||
private static CallToolResult json(Object value) { return text(JSON.toJson(value), false); }
|
||||
|
||||
private static String string(JsonObject args, String name) {
|
||||
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
|
||||
if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
|
||||
throw new IllegalArgumentException(name + " must be a string");
|
||||
return value.getAsString();
|
||||
}
|
||||
|
||||
private static int integer(JsonObject args, String name, int fallback) {
|
||||
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
|
||||
if (value == null) return fallback;
|
||||
if (!value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber())
|
||||
throw new IllegalArgumentException(name + " must be a nonnegative integer");
|
||||
try {
|
||||
int result = value.getAsBigDecimal().intValueExact();
|
||||
if (result < 0) throw new ArithmeticException();
|
||||
return result;
|
||||
} catch (ArithmeticException error) {
|
||||
throw new IllegalArgumentException(name + " must be a nonnegative integer", error);
|
||||
}
|
||||
}
|
||||
|
||||
private JadxSession session() {
|
||||
JadxSession current = holder.current();
|
||||
if (current == null) throw new IllegalArgumentException("No APK loaded");
|
||||
return current;
|
||||
}
|
||||
|
||||
private ClassSnapshot selected(JsonObject args) {
|
||||
JadxSession current = session();
|
||||
if (indexedSession != current) {
|
||||
Map<String, List<ClassSnapshot>> index = new LinkedHashMap<>();
|
||||
if (!current.getClasses().isEmpty()) {
|
||||
JadxDecompiler decompiler = current.getClasses().get(0).getClassNode().root().getDecompiler();
|
||||
// JavaClass.getMethods/getFields/getInnerClasses all load code. Raw nodes do not.
|
||||
// Snapshot every identity before code generation can rewrite aliases or members.
|
||||
for (JavaClass cls : decompiler.getClassesWithInners()) {
|
||||
ClassNode node = cls.getClassNode();
|
||||
List<MethodNode> ordered = new ArrayList<>(node.getMethods());
|
||||
ordered.sort(Comparator.comparing(MethodNode::getAlias));
|
||||
List<MethodSnapshot> methods = ordered.stream().map(method -> {
|
||||
String name = method.getAlias();
|
||||
String signature = name + "(" + method.getArgTypes().stream()
|
||||
.map(Object::toString).collect(Collectors.joining(", "))
|
||||
+ "): " + method.getReturnType().toString();
|
||||
return new MethodSnapshot(name, method.getMethodInfo().getShortId(), Map.of(
|
||||
"name", name, "signature", signature,
|
||||
"is_constructor", method.isConstructor(), "def_pos", 0));
|
||||
}).toList();
|
||||
List<Map<String, Object>> fields = node.getFields().stream()
|
||||
.sorted(Comparator.comparing(FieldNode::getAlias))
|
||||
.map(field -> Map.<String, Object>of("name", field.getAlias(),
|
||||
"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(),
|
||||
"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));
|
||||
}
|
||||
}
|
||||
classes = Collections.unmodifiableMap(index);
|
||||
indexedSession = current;
|
||||
}
|
||||
String name = string(args, "class_name");
|
||||
List<ClassSnapshot> candidates = classes.get(name);
|
||||
if (candidates == null) throw new IllegalArgumentException("Exact class not found: " + name);
|
||||
if (candidates.size() != 1) throw new IllegalArgumentException("Ambiguous metadata class identity: " + name
|
||||
+ "; raw identities: " + candidates.stream().map(candidate -> candidate.node().getRawName()).toList());
|
||||
return candidates.get(0);
|
||||
}
|
||||
|
||||
private List<MethodSnapshot> methods(ClassSnapshot cls, JsonObject args) {
|
||||
String name = string(args, "method_name");
|
||||
List<MethodSnapshot> result = cls.methods().stream().filter(method -> method.name().equals(name)).toList();
|
||||
if (result.isEmpty()) throw new IllegalArgumentException("Method not found: " + name);
|
||||
return result;
|
||||
}
|
||||
|
||||
private JavaMethod method(ClassSnapshot cls, MethodSnapshot selected) {
|
||||
JavaMethod result = cls.node().searchMethodByShortId(selected.shortId());
|
||||
if (result == null) throw new IllegalArgumentException("Method identity no longer available: " + selected.shortId());
|
||||
return result;
|
||||
}
|
||||
|
||||
private CallToolResult handle(String name, JsonObject args) {
|
||||
if (name.equals("rea_jvm_status")) return json(Map.of(
|
||||
"engine_reported_version", BuildInfo.VERSION,
|
||||
"max_heap_bytes", Runtime.getRuntime().maxMemory(),
|
||||
"available_processors", Runtime.getRuntime().availableProcessors(),
|
||||
"java_version", System.getProperty("java.version"),
|
||||
"metadata_scope", "parsed_members"));
|
||||
JadxSession current = session();
|
||||
if (name.equals("list_classes")) {
|
||||
List<String> names = current.getClassFqns();
|
||||
int offset = integer(args, "offset", 0);
|
||||
int limit = integer(args, "limit", 200);
|
||||
int start = Math.min(offset, names.size());
|
||||
int end = (int) Math.min((long) start + limit, names.size());
|
||||
return json(Map.of("total", names.size(), "offset", offset, "limit", limit,
|
||||
"items", names.subList(start, end)));
|
||||
}
|
||||
ClassSnapshot cls = selected(args);
|
||||
if (name.equals("get_class_summary")) return json(cls.summary());
|
||||
if (name.equals("get_method_body")) {
|
||||
List<MethodSnapshot> overloads = methods(cls, args);
|
||||
int index = integer(args, "overload_index", 0);
|
||||
if (index >= overloads.size()) throw new IllegalArgumentException("overload_index out of range: " + index);
|
||||
JavaMethod method = method(cls, overloads.get(index));
|
||||
JadxSession.SmartCode code = current.getMethodBodySmart(method, current.getMaxSourceBytes(), true);
|
||||
return json(Map.of("class_name", cls.summary().get("full_name"),
|
||||
"method_name", overloads.get(index).name(), "full_name", method.getFullName(),
|
||||
"overload_index", index, "overload_count", overloads.size(),
|
||||
"mode", code.getKind(), "fell_back", code.getFellBack(),
|
||||
"markers", code.getMarkers(), "body", code.getText()));
|
||||
}
|
||||
List<JavaNode> uses;
|
||||
String target = string(args, "class_name");
|
||||
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
|
||||
else {
|
||||
List<MethodSnapshot> overloads = methods(cls, args);
|
||||
if (overloads.size() != 1) throw new IllegalArgumentException("Method references require a unique overload");
|
||||
target += "." + overloads.get(0).name();
|
||||
uses = method(cls, overloads.get(0)).getUseIn();
|
||||
}
|
||||
// REA consumes all references without materializing source line information.
|
||||
List<Map<String, Object>> items = uses.stream().map(node -> current.describeUsage(node, false)).toList();
|
||||
return json(Map.of("target", target, "total", uses.size(), "count", items.size(),
|
||||
"resolve_line", false, "items", items));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
PrintStream protocolOut = System.out;
|
||||
System.setOut(System.err);
|
||||
SessionHolder holder = new SessionHolder(new SessionConfig(
|
||||
1048576, 0, 90000L, 1, List.of(), List.of(), 64, ResourceMode.FULL));
|
||||
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", "1", 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);
|
||||
ReaJadxBridge bridge = new ReaJadxBridge(holder);
|
||||
Set<String> consumed = Set.of("load_apk", "get_app_info", "get_android_manifest",
|
||||
"list_classes", "get_class_summary", "get_method_body",
|
||||
"get_xrefs_to_class", "get_xrefs_to_method");
|
||||
for (String name : new ArrayList<>(server.getTools().keySet()))
|
||||
if (!consumed.contains(name)) server.removeTool(name);
|
||||
Tool diagnostics = new Tool("rea_jvm_status",
|
||||
new ToolSchema(null, new 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("list_classes", "get_class_summary",
|
||||
"get_method_body", "get_xrefs_to_class", "get_xrefs_to_method")) {
|
||||
Tool tool = server.getTools().get(name).getTool();
|
||||
server.removeTool(name);
|
||||
List<String> fields = switch (name) {
|
||||
case "list_classes" -> List.of("offset", "limit");
|
||||
case "get_method_body" -> List.of("class_name", "method_name", "overload_index");
|
||||
case "get_xrefs_to_method" -> List.of("class_name", "method_name");
|
||||
default -> List.of("class_name");
|
||||
};
|
||||
Map<String, kotlinx.serialization.json.JsonElement> properties = new LinkedHashMap<>();
|
||||
for (String field : fields) properties.put(field, tool.getInputSchema().getProperties().get(field));
|
||||
String description = switch (name) {
|
||||
case "list_classes" -> "Indexed pages of all top-level class names, with offset and limit.";
|
||||
case "get_class_summary" -> "Parsed member metadata for one exact class name; no source generation.";
|
||||
case "get_method_body" -> "Source for an exact class and stable same-name overload index, with smali fallback.";
|
||||
default -> "Complete static references to an exact class or unique method; source lines are unresolved.";
|
||||
};
|
||||
Tool narrowed = new Tool(name, new ToolSchema(null, new 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 (IllegalArgumentException error) { return text(error.getMessage(), true); }
|
||||
});
|
||||
}
|
||||
CountDownLatch done = new CountDownLatch(1);
|
||||
server.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
|
||||
StdioServerTransport transport = new StdioServerTransport(
|
||||
CoreKt.buffered(JvmCoreKt.asSource(System.in)),
|
||||
CoreKt.buffered(JvmCoreKt.asSink(protocolOut)));
|
||||
transport.onClose(() -> { done.countDown(); return Unit.INSTANCE; });
|
||||
ServerSession connection = null;
|
||||
try {
|
||||
connection = BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> server.createSession(transport, continuation));
|
||||
done.await();
|
||||
} finally {
|
||||
try {
|
||||
if (connection != null) {
|
||||
ServerSession connected = connection;
|
||||
// Unsubscribe the connection before the SDK joins its notification service.
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> connected.close(continuation));
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> server.close(continuation));
|
||||
} finally {
|
||||
BuildersKt.runBlocking(EmptyCoroutineContext.INSTANCE,
|
||||
(scope, continuation) -> holder.unload(continuation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1463
-865
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Session-owned extension loading; analyzer algorithms stay behind each adapter ABI. */
|
||||
final class ReaGhidraExtensions implements AutoCloseable {
|
||||
private record Loaded(URLClassLoader loader, Object instance, String sha256) {}
|
||||
private final List<Loaded> loaded = new ArrayList<>();
|
||||
private final JsonArray reports = new JsonArray();
|
||||
|
||||
void analyze(JsonArray descriptors, Program program, TaskMonitor monitor) throws Exception {
|
||||
for (var item : descriptors) {
|
||||
monitor.checkCancelled();
|
||||
JsonObject descriptor = item.getAsJsonObject();
|
||||
String id = descriptor.get("id").getAsString();
|
||||
String entry = descriptor.get("entry_class").getAsString();
|
||||
String digest = descriptor.get("sha256").getAsString();
|
||||
JsonObject report = new JsonObject();
|
||||
report.addProperty("id", id);
|
||||
report.addProperty("sha256", digest);
|
||||
URLClassLoader loader = null;
|
||||
try {
|
||||
if (!id.equals("nativeaot") || !entry.equals("rea.extensions.nativeaot.NativeAotExtension") ||
|
||||
descriptor.get("integration_api").getAsInt() != 1)
|
||||
throw new IllegalArgumentException("Unregistered Ghidra extension ABI: " + id);
|
||||
Path path = Path.of(descriptor.get("path").getAsString());
|
||||
if (!path.isAbsolute() || !Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS) || Files.size(path) > 8 * 1024 * 1024)
|
||||
throw new IllegalArgumentException("Invalid private extension artifact: " + path);
|
||||
String measured = java.util.HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(path)));
|
||||
if (!measured.equals(digest)) throw new IllegalArgumentException("Private extension digest mismatch: " + id);
|
||||
loader = new URLClassLoader(new java.net.URL[] { path.toUri().toURL() }, getClass().getClassLoader());
|
||||
Class<?> type = Class.forName(entry, true, loader);
|
||||
Object instance = type.getConstructor().newInstance();
|
||||
Object response = type.getMethod("analyze", Program.class, TaskMonitor.class).invoke(instance, program, monitor);
|
||||
if (!(response instanceof JsonObject result)) throw new IllegalArgumentException("Invalid Ghidra extension result: " + id);
|
||||
report.addProperty("status", result.get("status").getAsString());
|
||||
report.add("reason", result.get("reason").deepCopy());
|
||||
report.add("result", result);
|
||||
loaded.add(new Loaded(loader, instance, digest));
|
||||
loader = null;
|
||||
} catch (InvocationTargetException ex) {
|
||||
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
||||
failure(report, ex.getCause());
|
||||
} catch (Exception | LinkageError ex) {
|
||||
failure(report, ex);
|
||||
} finally {
|
||||
if (loader != null) loader.close();
|
||||
}
|
||||
reports.add(report);
|
||||
}
|
||||
}
|
||||
|
||||
JsonArray reports() { return reports.deepCopy(); }
|
||||
|
||||
JsonArray inspectImage() throws Exception {
|
||||
JsonArray summaries = new JsonArray();
|
||||
for (Loaded extension : loaded) {
|
||||
Object response = extension.instance.getClass().getMethod("inspectImage").invoke(extension.instance);
|
||||
if (!(response instanceof JsonObject summary)) throw new IllegalArgumentException("Invalid extension image summary");
|
||||
summary.addProperty("analysis_artifact_sha256", extension.sha256);
|
||||
summaries.add(summary);
|
||||
}
|
||||
return summaries;
|
||||
}
|
||||
|
||||
JsonObject inspectDataType(Program program, DataType type, Address address, TaskMonitor monitor) throws Exception {
|
||||
for (Loaded extension : loaded) {
|
||||
try {
|
||||
Object response = extension.instance.getClass().getMethod("inspectDataType", Program.class, DataType.class, Address.class, TaskMonitor.class)
|
||||
.invoke(extension.instance, program, type, address, monitor);
|
||||
if (response instanceof JsonObject result) return result;
|
||||
if (response != null) throw new IllegalArgumentException("Invalid extension type metadata result");
|
||||
} catch (InvocationTargetException ex) {
|
||||
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
||||
throw new IllegalStateException("Ghidra extension type inspection failed: " + ex.getCause(), ex.getCause());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void failure(JsonObject report, Throwable ex) {
|
||||
String reason = "Extension loading/analysis failed: " + ex;
|
||||
if (ex.getCause() != null) reason += "; caused by " + ex.getCause();
|
||||
report.addProperty("status", "failed");
|
||||
report.addProperty("reason", reason);
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("loader_failure", reason);
|
||||
report.add("result", result);
|
||||
}
|
||||
|
||||
@Override public void close() throws Exception {
|
||||
Exception failure = null;
|
||||
for (Loaded extension : loaded) {
|
||||
try { extension.loader.close(); }
|
||||
catch (Exception ex) { if (failure == null) failure = ex; else failure.addSuppressed(ex); }
|
||||
}
|
||||
loaded.clear();
|
||||
if (failure != null) throw failure;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/** Seed the declared DOS COM analysis context before default auto-analysis. */
|
||||
public final class ReaGhidraPrepareCom extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (!currentProgram.getLanguageID().getIdAsString().equals("x86:LE:16:Real Mode") ||
|
||||
!currentProgram.getExecutableFormat().equals("Raw Binary")) {
|
||||
throw new IllegalArgumentException("DOS COM preparation requires raw x86 real-mode import");
|
||||
}
|
||||
Address entry = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress("1000:0100");
|
||||
Address start = currentProgram.getMinAddress();
|
||||
Address end = currentProgram.getMaxAddress();
|
||||
if (!entry.equals(start) || end.subtract(start) + 1 > 0xff00) {
|
||||
throw new IllegalArgumentException("DOS COM import must map 1..65280 bytes at 1000:0100");
|
||||
}
|
||||
for (String name : new String[] { "CS", "DS", "ES", "SS" }) {
|
||||
monitor.checkCancelled();
|
||||
currentProgram.getProgramContext().setValue(
|
||||
currentProgram.getRegister(name), start, end, BigInteger.valueOf(0x1000)
|
||||
);
|
||||
}
|
||||
addEntryPoint(entry);
|
||||
if (!disassemble(entry) || createFunction(entry, "entry") == null) {
|
||||
throw new IllegalStateException("DOS COM entry cannot be decoded as a function");
|
||||
}
|
||||
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA DOS COM prepared", true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import java.util.LinkedHashSet;
|
||||
import nativeaot.rtr.ReadyToRunDirectory;
|
||||
import nativeaot.rtr.ReadyToRunSection;
|
||||
import nativeaot.rtr.SignatureReadyToRunLocator;
|
||||
import nativeaot.rtr.SymbolReadyToRunLocator;
|
||||
|
||||
/** Validate the producer's NativeAOT row layout before database mutation. */
|
||||
final class NativeAotDirectory {
|
||||
record Located(Address address, ReadyToRunDirectory directory, String discovery) {}
|
||||
|
||||
static Located locate(Program program, TaskMonitor monitor, MessageLog log) throws Exception {
|
||||
Address[] symbols = SymbolReadyToRunLocator.instance.locateModules(program, monitor, log);
|
||||
String discovery = symbols.length == 0 ? "signature-heuristic" : "symbol";
|
||||
Address[] found = symbols.length == 0
|
||||
? SignatureReadyToRunLocator.instance.locateModules(program, monitor, log) : symbols;
|
||||
var candidates = new LinkedHashSet<Address>();
|
||||
for (Address address : found) if (address.getOffset() != 0) candidates.add(address);
|
||||
if (candidates.isEmpty()) return null;
|
||||
if (candidates.size() != 1)
|
||||
throw new UnsupportedOperationException("Ambiguous NativeAOT directory candidates: " + candidates);
|
||||
Address address = candidates.iterator().next();
|
||||
var memory = program.getMemory();
|
||||
byte[] header = new byte[16];
|
||||
if (memory.getBytes(address, header) != header.length)
|
||||
throw new IllegalArgumentException("Incomplete NativeAOT header at " + address);
|
||||
int count = Short.toUnsignedInt(memory.getShort(address.add(12)));
|
||||
if (count < 1 || count > 80 || Byte.toUnsignedInt(header[14]) != 24 || header[15] != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT row layout at " + address +
|
||||
": sections=" + count + ", entry_size=" + Byte.toUnsignedInt(header[14]) + ", entry_type=" + Byte.toUnsignedInt(header[15]));
|
||||
byte[] rows = new byte[count * 24];
|
||||
if (memory.getBytes(address.add(16), rows) != rows.length)
|
||||
throw new IllegalArgumentException("Incomplete NativeAOT directory rows at " + address);
|
||||
ReadyToRunDirectory directory = ReadyToRunDirectory.readAtAddress(program, address);
|
||||
if (directory.getMajorVersion() != 9 || directory.getMinorVersion() != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT RTR format " +
|
||||
directory.getMajorVersion() + "." + directory.getMinorVersion() + " at " + address + "; verified format is 9.1 (.NET 8.0.22 Linux x64 fixture).");
|
||||
var types = new LinkedHashSet<Integer>();
|
||||
for (ReadyToRunSection section : directory.getSections()) {
|
||||
monitor.checkCancelled();
|
||||
if (!types.add(section.getType())) throw new IllegalArgumentException("Duplicate NativeAOT section " + section.getType() + " at " + address);
|
||||
// ModuleInfoRow flags bit 0 declares an end pointer. ThreadStaticIndex
|
||||
// (204) is a single-address entry with flags=0 and an absent end.
|
||||
if (section.getFlags() != 0 && section.getFlags() != 1)
|
||||
throw new UnsupportedOperationException("Unsupported NativeAOT section flags " + section.getFlags() + " for section " + section.getType());
|
||||
boolean hasEnd = section.getFlags() == 1;
|
||||
if (!hasEnd && section.getEnd() != 0)
|
||||
throw new IllegalArgumentException("NativeAOT single-address section " + section.getType() + " reports an unexpected end");
|
||||
if (hasEnd && Long.compareUnsigned(section.getStart(), section.getEnd()) > 0)
|
||||
throw new IllegalArgumentException("Reversed NativeAOT section " + section.getType() + " at " + address);
|
||||
if (section.getStart() != 0 && (!hasEnd || section.getStart() != section.getEnd()) &&
|
||||
(!memory.contains(address.getNewAddress(section.getStart())) ||
|
||||
(hasEnd && !memory.contains(address.getNewAddress(section.getEnd() - 1)))))
|
||||
throw new IllegalArgumentException("Unmapped NativeAOT section " + section.getType() + " at " + address);
|
||||
}
|
||||
if (directory.getSectionByType(ReadyToRunSection.DEHYDRATED_DATA) == null)
|
||||
throw new UnsupportedOperationException("NativeAOT directory at " + address + " has no DEHYDRATED_DATA; non-dehydrated layouts are not verified.");
|
||||
for (int type : new int[] { ReadyToRunSection.DEHYDRATED_DATA, ReadyToRunSection.FROZEN_OBJECT_REGION }) {
|
||||
ReadyToRunSection section = directory.getSectionByType(type);
|
||||
if (section != null && section.getFlags() != 1)
|
||||
throw new IllegalArgumentException("NativeAOT section " + type + " requires an end pointer");
|
||||
}
|
||||
return new Located(address, directory, discovery);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.FrozenObjectAnnotator;
|
||||
import nativeaot.objectmodel.MethodTableCrawler;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
import nativeaot.rehydration.MetadataRehydratorNet80;
|
||||
import nativeaot.rtr.ReadyToRunSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/** Optional headless facade over the unchanged upstream analysis primitives. */
|
||||
public final class NativeAotExtension {
|
||||
private MethodTableManager manager;
|
||||
private NativeAotDirectory.Located located;
|
||||
private JsonObject report;
|
||||
private final List<String> diagnostics = new ArrayList<>();
|
||||
private JsonObject derivedRange;
|
||||
|
||||
public JsonObject analyze(Program program, TaskMonitor monitor) throws Exception {
|
||||
report = new JsonObject();
|
||||
report.addProperty("id", "nativeaot");
|
||||
report.addProperty("integration_api", 1);
|
||||
report.addProperty("source_revision", "effeb734fc570c32650f88b159608979dc7b423e");
|
||||
report.addProperty("source_revision_authority", "build-reported-unattested");
|
||||
report.addProperty("status", "not_applicable");
|
||||
report.addProperty("reason", "No NativeAOT directory with the supported row layout was found.");
|
||||
report.addProperty("method_tables", 0);
|
||||
if (!program.getLanguageID().getIdAsString().startsWith("x86:LE:64"))
|
||||
return failure("unsupported", "NativeAOT adapter supports x86-64 only.");
|
||||
MessageLog log = new MessageLog();
|
||||
int transaction = program.startTransaction("REA NativeAOT metadata recovery");
|
||||
boolean commit = false;
|
||||
String stage = "directory-discovery";
|
||||
try {
|
||||
located = NativeAotDirectory.locate(program, monitor, log);
|
||||
if (located == null) return finish(log);
|
||||
report.addProperty("header_address", address(located.address()));
|
||||
report.addProperty("discovery", located.discovery());
|
||||
report.addProperty("format_major", located.directory().getMajorVersion());
|
||||
report.addProperty("format_minor", located.directory().getMinorVersion());
|
||||
stage = "metadata-rehydration";
|
||||
var section = located.directory().getSectionByType(ReadyToRunSection.DEHYDRATED_DATA);
|
||||
Address start = located.address().getNewAddress(section.getStart());
|
||||
Address end = located.address().getNewAddress(section.getEnd());
|
||||
if (start.compareTo(end) >= 0) throw new IllegalArgumentException("Empty dehydrated section at " + start);
|
||||
var scan = new MetadataRehydratorNet80().rehydrate(program, new AddressSet(start, end).getFirstRange(), monitor, log);
|
||||
// Upstream reports an inclusive end at base + written length.
|
||||
long writtenLength = scan.getScanningRange().getLength() - 1;
|
||||
derivedRange = new JsonObject();
|
||||
derivedRange.addProperty("address", address(scan.getScanningRange().getMinAddress()));
|
||||
derivedRange.addProperty("size_bytes", writtenLength);
|
||||
derivedRange.addProperty("sha256", hashMemory(program, scan.getScanningRange().getMinAddress(), writtenLength, monitor));
|
||||
derivedRange.add("file_offset", JsonNull.INSTANCE);
|
||||
stage = "method-table-recovery";
|
||||
manager = MethodTableManager.createForDirectory(program, located.directory());
|
||||
var conventions = NativeAotMethodConventions.capture(program, monitor);
|
||||
new MethodTableCrawler(manager, program, scan).analyze(monitor, log);
|
||||
stage = "calling-convention-preservation";
|
||||
NativeAotMethodConventions.restore(program, manager, conventions, monitor);
|
||||
log.appendMsg("REA", "Preserved pre-recovery calling conventions; new functions use the loaded compiler specification default. Original method signatures remain unknown.");
|
||||
if (manager.getObjectMT() == null || manager.getMethodTableCount() == 0)
|
||||
throw new UnsupportedOperationException("No unambiguous System.Object method table was recovered at header " + located.address());
|
||||
stage = "frozen-object-annotation";
|
||||
if (manager.getStringMT() == null || scan.getPointerLocations().length == 0) {
|
||||
diagnostics.add("Frozen-object annotation unavailable: System.String table or pointer locations were not recovered.");
|
||||
} else {
|
||||
new FrozenObjectAnnotator(program, manager).analyze(located.directory(), scan.getPointerLocations(), monitor, log);
|
||||
}
|
||||
monitor.checkCancelled();
|
||||
report.addProperty("method_tables", manager.getMethodTableCount());
|
||||
JsonObject coverage = auditFrozenObjects(program, scan.getPointerLocations(), monitor);
|
||||
report.add("coverage", coverage);
|
||||
if (coverage.get("frozen_objects_annotated").getAsInt() != coverage.get("frozen_object_candidates").getAsInt())
|
||||
diagnostics.add("Some frozen-object candidates lack committed instance annotations; inspect coverage counts and Ghidra diagnostics.");
|
||||
report.addProperty("status", diagnostics.isEmpty() ? "complete" : "partial");
|
||||
report.add("reason", JsonNull.INSTANCE);
|
||||
report.add("derived_memory", derivedRange.deepCopy());
|
||||
JsonArray tables = new JsonArray();
|
||||
for (var mt : manager.getMethodTables()) {
|
||||
monitor.checkCancelled();
|
||||
JsonObject identity = new JsonObject();
|
||||
identity.addProperty("address", address(mt.getAddress()));
|
||||
identity.addProperty("type", mt.getMTType().getPathName());
|
||||
tables.add(identity);
|
||||
}
|
||||
report.add("types", tables);
|
||||
commit = true;
|
||||
return finish(log);
|
||||
} catch (CancelledException ex) {
|
||||
manager = null;
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
manager = null;
|
||||
diagnostics.add(stage + ": " + ex.getClass().getSimpleName() + ": " + ex.getMessage());
|
||||
report.addProperty("status", ex instanceof UnsupportedOperationException ? "unsupported" : "failed");
|
||||
report.addProperty("reason", stage + ": " + ex.getMessage());
|
||||
return finish(log);
|
||||
} finally {
|
||||
program.endTransaction(transaction, commit);
|
||||
}
|
||||
}
|
||||
|
||||
/** Inline discovery for the existing loaded-image inspection. */
|
||||
public JsonObject inspectImage() {
|
||||
JsonObject summary = new JsonObject();
|
||||
summary.addProperty("format", "dotnet-nativeaot");
|
||||
for (String key : new String[] { "status", "reason", "header_address", "discovery", "format_major", "format_minor", "method_tables", "derived_memory", "coverage", "diagnostics" })
|
||||
summary.add(key, report.has(key) ? report.get(key).deepCopy() : JsonNull.INSTANCE);
|
||||
summary.add("types", report.has("types") ? report.get("types").deepCopy() : new JsonArray());
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add("Recovery covers the reported metadata candidates; it does not reconstruct original C# source, custom field layouts, every runtime object, or execution behavior.");
|
||||
limitations.add("Method prototypes and parameter names remain Ghidra inferences; verify them against instructions and call sites. Pre-recovery conventions are retained rather than universally forcing __thiscall.");
|
||||
limitations.add("NativeAOT type identities and relationships are inferred; generated names are not original names. Derived memory has no original file offsets.");
|
||||
summary.add("limitations", limitations);
|
||||
return summary;
|
||||
}
|
||||
|
||||
/** Read recovered relationships without restoring or mutating database state. */
|
||||
public JsonObject inspectDataType(Program program, DataType type, Address selected, TaskMonitor monitor) throws Exception {
|
||||
if (manager == null) return null;
|
||||
return NativeAotTypeMetadata.describe(program, manager, located, type, report, derivedRange, monitor);
|
||||
}
|
||||
|
||||
private JsonObject auditFrozenObjects(Program program, Address[] pointerLocations, TaskMonitor monitor) throws Exception {
|
||||
JsonObject coverage = new JsonObject();
|
||||
int candidates = 0, annotated = 0;
|
||||
var section = located.directory().getSectionByType(ReadyToRunSection.FROZEN_OBJECT_REGION);
|
||||
if (section != null) for (Address location : section.getPointersInSection(pointerLocations)) {
|
||||
monitor.checkCancelled();
|
||||
var table = manager.getMethodTable(program.getMemory().getLong(location));
|
||||
if (table == null) continue;
|
||||
candidates++;
|
||||
var expected = table.getInstanceType();
|
||||
var actual = program.getListing().getDataAt(location);
|
||||
if (expected != null && actual != null && actual.getDataType().getPathName().equals(expected.getPathName())) annotated++;
|
||||
}
|
||||
coverage.addProperty("frozen_object_candidates", candidates);
|
||||
coverage.addProperty("frozen_objects_annotated", annotated);
|
||||
coverage.addProperty("basis", "rehydrated-pointer-candidates-and-committed-instance-types");
|
||||
return coverage;
|
||||
}
|
||||
|
||||
private JsonObject finish(MessageLog log) {
|
||||
if (!log.toString().isBlank()) diagnostics.add(log.toString());
|
||||
JsonArray messages = new JsonArray();
|
||||
for (String message : diagnostics) messages.add(message);
|
||||
report.add("diagnostics", messages);
|
||||
return report;
|
||||
}
|
||||
|
||||
private JsonObject failure(String status, String reason) {
|
||||
report.addProperty("status", status);
|
||||
report.addProperty("reason", reason);
|
||||
return finish(new MessageLog());
|
||||
}
|
||||
|
||||
static String address(Address value) { return "0x" + Long.toUnsignedString(value.getOffset(), 16); }
|
||||
|
||||
private static String hashMemory(Program program, Address start, long length, TaskMonitor monitor) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] buffer = new byte[65536];
|
||||
for (long offset = 0; offset < length;) {
|
||||
monitor.checkCancelled();
|
||||
int count = (int)Math.min(buffer.length, length - offset);
|
||||
if (program.getMemory().getBytes(start.add(offset), buffer, 0, count) != count)
|
||||
throw new IllegalStateException("Incomplete derived-memory read at " + start.add(offset));
|
||||
digest.update(buffer, 0, count);
|
||||
offset += count;
|
||||
}
|
||||
return java.util.HexFormat.of().formatHex(digest.digest());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/** Keep upstream's unconditional __thiscall assignment out of x64 ABI evidence. */
|
||||
final class NativeAotMethodConventions {
|
||||
static Map<Address, String> capture(Program program, TaskMonitor monitor) throws Exception {
|
||||
Map<Address, String> result = new HashMap<>();
|
||||
var functions = program.getFunctionManager().getFunctions(true);
|
||||
while (functions.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
var function = functions.next();
|
||||
result.put(function.getEntryPoint(), function.getCallingConventionName());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void restore(Program program, MethodTableManager manager, Map<Address, String> previous, TaskMonitor monitor) throws Exception {
|
||||
var visited = new HashSet<Address>();
|
||||
String defaultConvention = program.getCompilerSpec().getDefaultCallingConvention().getName();
|
||||
for (var table : manager.getMethodTables()) for (long target : table.getVTable()) {
|
||||
monitor.checkCancelled();
|
||||
Address address = table.getAddress().getNewAddress(target);
|
||||
if (!visited.add(address)) continue;
|
||||
var function = program.getFunctionManager().getFunctionAt(address);
|
||||
if (function == null) continue;
|
||||
String convention = previous.containsKey(address) ? previous.get(address) : defaultConvention;
|
||||
if (!java.util.Objects.equals(function.getCallingConventionName(), convention))
|
||||
function.setCallingConvention(convention);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package rea.extensions.nativeaot;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import nativeaot.objectmodel.MethodTable;
|
||||
import nativeaot.objectmodel.MethodTableManager;
|
||||
|
||||
/** Translate a selected recovered type into provider-neutral metadata evidence. */
|
||||
final class NativeAotTypeMetadata {
|
||||
static JsonObject describe(Program program, MethodTableManager manager, NativeAotDirectory.Located located,
|
||||
DataType type, JsonObject report, JsonObject derivedRange, TaskMonitor monitor) throws Exception {
|
||||
for (MethodTable table : manager.getMethodTables()) {
|
||||
monitor.checkCancelled();
|
||||
boolean sameType = type.getPathName().equals(table.getMTType().getPathName()) ||
|
||||
(table.getInstanceType() != null && type.getPathName().equals(table.getInstanceType().getPathName()));
|
||||
if (!sameType) continue;
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("format", "dotnet-nativeaot");
|
||||
result.addProperty("status", report.get("status").getAsString());
|
||||
result.addProperty("header_address", NativeAotExtension.address(located.address()));
|
||||
result.addProperty("format_major", located.directory().getMajorVersion());
|
||||
result.addProperty("format_minor", located.directory().getMinorVersion());
|
||||
result.addProperty("method_table_address", NativeAotExtension.address(table.getAddress()));
|
||||
result.addProperty("name_origin", table == manager.getObjectMT() || table == manager.getStringMT() ? "inferred" : "generated");
|
||||
result.add("original_name", JsonNull.INSTANCE);
|
||||
result.addProperty("base_size_bytes", table.getBaseSize());
|
||||
result.add("related_type", identity(manager, table.getRelatedTypeAddress()));
|
||||
JsonArray interfaces = new JsonArray();
|
||||
for (long value : table.getInterfaceAddresses()) if (value != 0) interfaces.add(identity(manager, value));
|
||||
result.add("interfaces", interfaces);
|
||||
JsonArray slots = new JsonArray();
|
||||
for (int slot = 0; slot < table.getVTableSlotCount(); slot++) {
|
||||
monitor.checkCancelled();
|
||||
long value = table.getVTableSlot(slot);
|
||||
JsonObject target = new JsonObject();
|
||||
target.addProperty("slot", slot);
|
||||
target.addProperty("slot_address", NativeAotExtension.address(table.getAddress().add(24 + slot * 8L)));
|
||||
target.add("target_address", value == 0 ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive("0x" + Long.toUnsignedString(value, 16)));
|
||||
var function = value == 0 ? null : program.getFunctionManager().getFunctionAt(table.getAddress().getNewAddress(value));
|
||||
target.add("procedure_name", function == null ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive(function.getName(true)));
|
||||
target.addProperty("basis", "method-table-pointer");
|
||||
slots.add(target);
|
||||
}
|
||||
result.add("virtual_slots", slots);
|
||||
result.add("derived_memory", derivedRange.deepCopy());
|
||||
result.add("diagnostics", report.get("diagnostics").deepCopy());
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add("Type identities and relationships are recovered by upstream heuristics. Original source names and custom field layouts are not recovered.");
|
||||
limitations.add("Method prototypes are inferred, not original signatures. REA preserves pre-recovery calling conventions and the loaded compiler default for new functions.");
|
||||
limitations.add("Rehydrated metadata bytes are derived analysis-memory content; their original file offsets and runtime behavior remain unknown.");
|
||||
limitations.add("Verified workflow: .NET 8.0.22 NativeAOT RTR 9.1, Linux x64 ELF / Windows x64 PE on Linux; other runtime layouts and hosts are unverified.");
|
||||
result.add("limitations", limitations);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static com.google.gson.JsonElement identity(MethodTableManager manager, long value) throws Exception {
|
||||
if (value == 0) return JsonNull.INSTANCE;
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("address", "0x" + Long.toUnsignedString(value, 16));
|
||||
MethodTable table = manager.getMethodTable(value);
|
||||
result.add("type", table == null ? JsonNull.INSTANCE : new com.google.gson.JsonPrimitive(table.getMTType().getPathName()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+164
-366
@@ -9,19 +9,15 @@ import json
|
||||
import hmac
|
||||
import os
|
||||
import re
|
||||
import sre_parse
|
||||
import socket
|
||||
from typing import Any, Optional, Protocol, Sequence
|
||||
|
||||
MAX_LINE_BYTES = 10 * 1024 * 1024
|
||||
MAX_READ_BYTES = 4096
|
||||
BAD_ADDRESSES = (-1, 0xFFFFFFFFFFFFFFFF, None)
|
||||
_selected_document = None
|
||||
_rea_session_document = None
|
||||
_search_inventory_cache = {}
|
||||
_pseudocode_cache = {}
|
||||
_hopper_api = None
|
||||
MAX_SEARCH_PATTERN_LENGTH = 256
|
||||
MAX_SEARCH_VALUE_LENGTH = 4096
|
||||
|
||||
|
||||
class HopperDocument(Protocol):
|
||||
@@ -69,8 +65,9 @@ class HopperApiFacade:
|
||||
|
||||
def _configure_hopper_api(document_provider):
|
||||
"""Inject Hopper's document provider or a standalone test facade."""
|
||||
global _hopper_api
|
||||
global _hopper_api, _rea_session_document
|
||||
_hopper_api = HopperApiFacade(document_provider)
|
||||
_rea_session_document = None
|
||||
|
||||
|
||||
def _api():
|
||||
@@ -88,25 +85,53 @@ def _api():
|
||||
|
||||
|
||||
def _session_document():
|
||||
"""Find only the document opened for this authenticated REA session."""
|
||||
"""Return the exact document bound to this authenticated REA session."""
|
||||
global _rea_session_document
|
||||
documents = _api().documents()
|
||||
if _rea_session_document is not None:
|
||||
return (
|
||||
_rea_session_document
|
||||
if any(document is _rea_session_document for document in documents)
|
||||
else None
|
||||
)
|
||||
return _bind_session_document(documents)
|
||||
|
||||
|
||||
def _bind_session_document(documents=None):
|
||||
"""Bind this bridge to Hopper's current document for its target."""
|
||||
global _rea_session_document
|
||||
api = _api()
|
||||
if documents is None:
|
||||
documents = api.documents()
|
||||
target = os.path.realpath(REA_TARGET_PATH)
|
||||
for document in _api().documents():
|
||||
|
||||
def matches_target(document):
|
||||
paths = (document.getExecutableFilePath(), document.getDatabaseFilePath())
|
||||
for path in paths:
|
||||
if path and os.path.realpath(path) == target:
|
||||
return document
|
||||
return any(path and os.path.realpath(path) == target for path in paths)
|
||||
|
||||
current = api.current_document()
|
||||
if current is not None and matches_target(current):
|
||||
_rea_session_document = current
|
||||
return current
|
||||
matching = [document for document in documents if matches_target(document)]
|
||||
if len(matching) == 1:
|
||||
_rea_session_document = matching[0]
|
||||
return matching[0]
|
||||
if len(matching) > 1:
|
||||
raise CapabilityUnavailableError(
|
||||
"The REA session document is ambiguous; Hopper did not identify the launched document"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
MAX_REGEX_BACKTRACKING_PATHS = 10000
|
||||
MAX_REGEX_CANDIDATE_LENGTH = 4096
|
||||
MAX_REGEX_SEARCH_WORK_UNITS = 1000000
|
||||
|
||||
|
||||
class CapabilityUnavailableError(Exception):
|
||||
"""The active Hopper build lacks one admitted public API operation."""
|
||||
|
||||
|
||||
class InvalidRequestError(Exception):
|
||||
"""Caller input failed bridge request or operation validation."""
|
||||
|
||||
|
||||
EXHAUSTIVE_ANALYSIS_METHODS = frozenset(
|
||||
(
|
||||
"analyze_function",
|
||||
@@ -129,6 +154,11 @@ EXHAUSTIVE_ANALYSIS_METHODS = frozenset(
|
||||
|
||||
|
||||
def _hex(value):
|
||||
if not isinstance(value, int) or isinstance(value, bool):
|
||||
try:
|
||||
value = int(str(value), 16)
|
||||
except ValueError as error:
|
||||
raise ValueError("Hopper returned a non-hexadecimal address") from error
|
||||
return "0x%x" % value
|
||||
|
||||
|
||||
@@ -149,17 +179,25 @@ def _document(name=None):
|
||||
api = _api()
|
||||
documents = api.documents()
|
||||
if name is not None:
|
||||
for candidate in documents:
|
||||
if candidate.getDocumentName() == name:
|
||||
return candidate
|
||||
raise ValueError("Unknown Hopper document")
|
||||
matches = [candidate for candidate in documents if candidate.getDocumentName() == name]
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
if len(matches) > 1:
|
||||
current = api.current_document()
|
||||
if current is not None and current.getDocumentName() == name:
|
||||
return current
|
||||
raise InvalidRequestError("Hopper document name is ambiguous")
|
||||
raise InvalidRequestError("Unknown Hopper document")
|
||||
if _selected_document is not None:
|
||||
for candidate in documents:
|
||||
if candidate.getDocumentName() == _selected_document:
|
||||
return candidate
|
||||
session_document = _session_document()
|
||||
if session_document is not None:
|
||||
return session_document
|
||||
current = api.current_document()
|
||||
if current is None:
|
||||
raise ValueError("No Hopper document is loaded")
|
||||
raise CapabilityUnavailableError("No Hopper document is loaded")
|
||||
return current
|
||||
|
||||
|
||||
@@ -168,20 +206,20 @@ def _address(document, value=None):
|
||||
if value is None:
|
||||
return document.getCurrentAddress()
|
||||
if not isinstance(value, str):
|
||||
raise ValueError("Address must be a string")
|
||||
raise InvalidRequestError("Address must be a string")
|
||||
try:
|
||||
return int(value, 16)
|
||||
except ValueError:
|
||||
result = document.getAddressForName(value)
|
||||
if result in BAD_ADDRESSES:
|
||||
raise ValueError("Unknown Hopper address or name")
|
||||
raise InvalidRequestError("Unknown Hopper address or name")
|
||||
return result
|
||||
|
||||
|
||||
def _segment(document, address):
|
||||
result = document.getSegmentAtAddress(address)
|
||||
if result is None:
|
||||
raise ValueError("Address is outside every segment")
|
||||
raise InvalidRequestError("Address is outside every segment")
|
||||
return result
|
||||
|
||||
|
||||
@@ -189,7 +227,7 @@ def _procedure(document, value=None):
|
||||
address = document.getCurrentAddress() if value is None else _address(document, value)
|
||||
result = _segment(document, address).getProcedureAtAddress(address)
|
||||
if result is None:
|
||||
raise ValueError("No procedure exists at the requested address")
|
||||
raise InvalidRequestError("No procedure exists at the requested address")
|
||||
return result
|
||||
|
||||
|
||||
@@ -203,6 +241,10 @@ def _procedure_identity(procedure):
|
||||
"address": _hex(procedure.getEntryPoint()),
|
||||
"name": _procedure_name(procedure),
|
||||
"classification": None,
|
||||
"body": {
|
||||
"available": False,
|
||||
"reason": "Hopper's public Python API does not expose complete function body ranges",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -225,18 +267,14 @@ def _containing_procedure(document, address):
|
||||
return (procedure, None) if procedure is not None else (None, "not_in_procedure")
|
||||
|
||||
|
||||
def _instruction_addresses(procedure, limit):
|
||||
def _instruction_addresses(procedure):
|
||||
result = []
|
||||
seen = set()
|
||||
segment = procedure.getSegment()
|
||||
truncated = False
|
||||
for block in procedure.basicBlockIterator():
|
||||
address = block.getStartingAddress()
|
||||
end = block.getEndingAddress()
|
||||
while address < end and address not in seen:
|
||||
if len(result) >= limit:
|
||||
truncated = True
|
||||
return result, truncated
|
||||
seen.add(address)
|
||||
instruction = segment.getInstructionAtAddress(address)
|
||||
if instruction is None:
|
||||
@@ -246,24 +284,15 @@ def _instruction_addresses(procedure, limit):
|
||||
if length <= 0:
|
||||
break
|
||||
address += length
|
||||
return result, truncated
|
||||
return result
|
||||
|
||||
|
||||
def _procedure_references(document, params):
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
direction = params.get("direction", "outgoing")
|
||||
offset = params.get("offset", 0)
|
||||
limit = params.get("limit", 100)
|
||||
max_instructions = params.get("max_instructions", 500)
|
||||
if direction not in ("incoming", "outgoing"):
|
||||
raise ValueError("direction must be incoming or outgoing")
|
||||
if not isinstance(offset, int) or isinstance(offset, bool) or offset < 0:
|
||||
raise ValueError("offset must be a non-negative integer")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 500:
|
||||
raise ValueError("limit must be an integer between 1 and 500")
|
||||
if not isinstance(max_instructions, int) or isinstance(max_instructions, bool) or max_instructions < 1 or max_instructions > 5000:
|
||||
raise ValueError("max_instructions must be an integer between 1 and 5000")
|
||||
addresses, scan_truncated = _instruction_addresses(procedure, max_instructions)
|
||||
raise InvalidRequestError("direction must be incoming or outgoing")
|
||||
addresses = _instruction_addresses(procedure)
|
||||
edges = set()
|
||||
for address in addresses:
|
||||
segment = _segment(document, address)
|
||||
@@ -272,8 +301,7 @@ def _procedure_references(document, params):
|
||||
edges.add((address, reference) if direction == "outgoing" else (reference, address))
|
||||
ordered = sorted(edges)
|
||||
items = []
|
||||
selected = ordered[offset:offset + limit]
|
||||
for source, target in selected:
|
||||
for source, target in ordered:
|
||||
source_procedure, _ = _containing_procedure(document, source)
|
||||
target_procedure, _ = _containing_procedure(document, target)
|
||||
items.append({
|
||||
@@ -283,13 +311,15 @@ def _procedure_references(document, params):
|
||||
"target_procedure": _procedure_identity(target_procedure) if target_procedure is not None else None,
|
||||
"kind": _unavailable("Hopper's public Python API does not classify reference kinds"),
|
||||
})
|
||||
next_offset = offset + len(selected)
|
||||
has_more = next_offset < len(ordered)
|
||||
truncated = scan_truncated or has_more
|
||||
return {
|
||||
"procedure": _procedure_identity(procedure), "direction": direction,
|
||||
"references": {"items": items, "total": None if scan_truncated else len(ordered), "returned": len(items), "truncated": truncated, "next_offset": next_offset if has_more and not scan_truncated else None},
|
||||
"instructions_scanned": len(addresses), "instruction_scan_truncated": scan_truncated,
|
||||
"reference_kinds_available": False,
|
||||
# The public Hopper API returns observed references but does not expose
|
||||
# enough flow metadata to enumerate calls with no resolved target.
|
||||
# Keep the required list field explicit; the provider records this as
|
||||
# unknown coverage rather than claiming the list is exhaustive.
|
||||
"unresolved_calls": [],
|
||||
"references": items,
|
||||
}
|
||||
|
||||
|
||||
@@ -352,230 +382,39 @@ def _search_inventory(document, kind):
|
||||
return inventory
|
||||
|
||||
|
||||
def _checked_regex_paths(left, right, operation):
|
||||
"""Apply one path-count operation without crossing the static work budget."""
|
||||
if operation == "add":
|
||||
exceeded = left > MAX_REGEX_BACKTRACKING_PATHS - right
|
||||
result = left + right
|
||||
else:
|
||||
exceeded = right != 0 and left > MAX_REGEX_BACKTRACKING_PATHS // right
|
||||
result = left * right
|
||||
if exceeded or result > MAX_REGEX_BACKTRACKING_PATHS:
|
||||
raise ValueError(
|
||||
"Regex exceeds the %d-path backtracking budget"
|
||||
% MAX_REGEX_BACKTRACKING_PATHS
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def _repeat_regex_paths(child_paths, minimum, maximum):
|
||||
"""Count every bounded repetition path, including alternative child paths."""
|
||||
paths = 0
|
||||
repeated_paths = 1
|
||||
for count in range(maximum + 1):
|
||||
if count >= minimum:
|
||||
paths = _checked_regex_paths(paths, repeated_paths, "add")
|
||||
if count < maximum:
|
||||
repeated_paths = _checked_regex_paths(
|
||||
repeated_paths, child_paths, "multiply"
|
||||
)
|
||||
return paths
|
||||
|
||||
|
||||
def _validate_regex_class(items):
|
||||
"""Accept only constant-time character-class operations."""
|
||||
allowed = {
|
||||
sre_parse.CATEGORY,
|
||||
sre_parse.LITERAL,
|
||||
sre_parse.NEGATE,
|
||||
sre_parse.RANGE,
|
||||
}
|
||||
if any(operation not in allowed for operation, _ in items):
|
||||
raise ValueError("Regex operation is not supported by the bounded matcher")
|
||||
|
||||
|
||||
def _validate_regex_node(node, inside_repeat=False):
|
||||
"""Return capped path and step bounds for Python regex evaluation."""
|
||||
leaf_operations = {
|
||||
sre_parse.ANY,
|
||||
sre_parse.AT,
|
||||
sre_parse.CATEGORY,
|
||||
sre_parse.LITERAL,
|
||||
sre_parse.NOT_LITERAL,
|
||||
}
|
||||
forbidden = {
|
||||
sre_parse.ASSERT,
|
||||
sre_parse.ASSERT_NOT,
|
||||
sre_parse.GROUPREF,
|
||||
sre_parse.GROUPREF_EXISTS,
|
||||
}
|
||||
for name in ("GROUPREF_IGNORE", "GROUPREF_LOC_IGNORE", "GROUPREF_UNI_IGNORE"):
|
||||
operation = getattr(sre_parse, name, None)
|
||||
if operation is not None:
|
||||
forbidden.add(operation)
|
||||
repeat_tokens = {sre_parse.MAX_REPEAT, sre_parse.MIN_REPEAT}
|
||||
possessive = getattr(sre_parse, "POSSESSIVE_REPEAT", None)
|
||||
if possessive is not None:
|
||||
repeat_tokens.add(possessive)
|
||||
atomic = getattr(sre_parse, "ATOMIC_GROUP", None)
|
||||
|
||||
paths = 1
|
||||
steps = 0
|
||||
for operation, argument in node:
|
||||
if operation in forbidden:
|
||||
raise ValueError("Regex lookarounds and backreferences are not supported")
|
||||
if operation in leaf_operations:
|
||||
operation_paths = 1
|
||||
operation_steps = 1
|
||||
elif operation == sre_parse.IN:
|
||||
_validate_regex_class(argument)
|
||||
operation_paths = 1
|
||||
operation_steps = 1
|
||||
elif operation in repeat_tokens:
|
||||
if inside_repeat:
|
||||
raise ValueError("Nested regex repetitions are not supported")
|
||||
minimum, maximum, child = argument
|
||||
if maximum == sre_parse.MAXREPEAT or maximum > 1000:
|
||||
raise ValueError(
|
||||
"Unbounded or excessive regex repetitions are not supported"
|
||||
)
|
||||
child_paths, child_steps = _validate_regex_node(child, True)
|
||||
operation_paths = _repeat_regex_paths(
|
||||
child_paths, minimum, maximum
|
||||
)
|
||||
operation_steps = maximum * child_steps
|
||||
elif operation == sre_parse.SUBPATTERN:
|
||||
operation_paths, operation_steps = _validate_regex_node(
|
||||
argument[-1], inside_repeat
|
||||
)
|
||||
elif operation == sre_parse.BRANCH:
|
||||
operation_paths = 0
|
||||
operation_steps = 0
|
||||
for branch in argument[1]:
|
||||
branch_paths, branch_steps = _validate_regex_node(
|
||||
branch, inside_repeat
|
||||
)
|
||||
operation_paths = _checked_regex_paths(
|
||||
operation_paths,
|
||||
branch_paths,
|
||||
"add",
|
||||
)
|
||||
operation_steps = max(operation_steps, branch_steps)
|
||||
elif atomic is not None and operation == atomic:
|
||||
operation_paths, operation_steps = _validate_regex_node(
|
||||
argument, inside_repeat
|
||||
)
|
||||
else:
|
||||
raise ValueError("Regex operation is not supported by the bounded matcher")
|
||||
paths = _checked_regex_paths(paths, operation_paths, "multiply")
|
||||
steps += operation_steps
|
||||
return paths, steps
|
||||
|
||||
|
||||
def _bounded_regex_matcher(expression, backtracking_paths, steps_per_path):
|
||||
"""Create a matcher with per-candidate and cumulative work bounds."""
|
||||
remaining_work = MAX_REGEX_SEARCH_WORK_UNITS
|
||||
work_per_character = backtracking_paths * max(steps_per_path, 1)
|
||||
|
||||
def matches(value):
|
||||
nonlocal remaining_work
|
||||
if not isinstance(value, str):
|
||||
raise ValueError("Regex candidates must be strings")
|
||||
if len(value) > MAX_REGEX_CANDIDATE_LENGTH:
|
||||
raise ValueError(
|
||||
"Regex candidate exceeds the %d-character safety limit"
|
||||
% MAX_REGEX_CANDIDATE_LENGTH
|
||||
)
|
||||
required_work = work_per_character * max(len(value), 1)
|
||||
if required_work > remaining_work:
|
||||
raise ValueError(
|
||||
"Regex search exceeds the %d-unit work budget"
|
||||
% MAX_REGEX_SEARCH_WORK_UNITS
|
||||
)
|
||||
remaining_work -= required_work
|
||||
return expression.search(value) is not None
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
def _search_page(document, kind, params):
|
||||
def _search_results(document, kind, params):
|
||||
pattern = params.get("pattern")
|
||||
if not isinstance(pattern, str) or not pattern or len(pattern) > MAX_SEARCH_PATTERN_LENGTH:
|
||||
raise ValueError("pattern must contain between 1 and 256 characters")
|
||||
if not isinstance(pattern, str) or not pattern:
|
||||
raise InvalidRequestError("pattern must be a non-empty string")
|
||||
mode = params.get("mode", "literal")
|
||||
if mode not in ("literal", "regex"):
|
||||
raise ValueError("mode must be literal or regex")
|
||||
raise InvalidRequestError("mode must be literal or regex")
|
||||
case_sensitive = params.get("case_sensitive", False)
|
||||
if not isinstance(case_sensitive, bool):
|
||||
raise ValueError("case_sensitive must be a boolean")
|
||||
offset = params.get("offset", 0)
|
||||
limit = params.get("limit", 100)
|
||||
if not isinstance(offset, int) or isinstance(offset, bool) or offset < 0:
|
||||
raise ValueError("offset must be a non-negative integer")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 100:
|
||||
raise ValueError("limit must be an integer between 1 and 100")
|
||||
raise InvalidRequestError("case_sensitive must be a boolean")
|
||||
|
||||
if mode == "literal":
|
||||
needle = pattern if case_sensitive else pattern.casefold()
|
||||
matches = lambda value: needle in (value if case_sensitive else value.casefold())
|
||||
else:
|
||||
try:
|
||||
parsed = sre_parse.parse(pattern)
|
||||
backtracking_paths, steps_per_path = _validate_regex_node(parsed)
|
||||
expression = re.compile(pattern, 0 if case_sensitive else re.IGNORECASE)
|
||||
except (re.error, OverflowError) as error:
|
||||
raise ValueError("Invalid regex pattern") from error
|
||||
matches = _bounded_regex_matcher(
|
||||
expression, backtracking_paths, steps_per_path
|
||||
)
|
||||
raise InvalidRequestError("Invalid regex pattern") from error
|
||||
matches = lambda value: expression.search(value) is not None
|
||||
|
||||
selected = []
|
||||
total = 0
|
||||
page_end = offset + limit
|
||||
for item in _search_inventory(document, kind):
|
||||
if not matches(item[1]):
|
||||
continue
|
||||
if offset <= total < page_end:
|
||||
selected.append(item)
|
||||
total += 1
|
||||
next_offset = offset + len(selected)
|
||||
has_more = next_offset < total
|
||||
return {
|
||||
"items": [
|
||||
selected.append(item)
|
||||
return [
|
||||
{
|
||||
"address": address,
|
||||
"value": value[:MAX_SEARCH_VALUE_LENGTH],
|
||||
"value_truncated": len(value) > MAX_SEARCH_VALUE_LENGTH,
|
||||
"value": value,
|
||||
}
|
||||
for address, value in selected
|
||||
],
|
||||
"offset": offset,
|
||||
"limit": limit,
|
||||
"total": total,
|
||||
"next_offset": next_offset if has_more else None,
|
||||
"has_more": has_more,
|
||||
}
|
||||
|
||||
|
||||
def _page(values, offset, limit):
|
||||
"""Return one deterministically address-sorted page without crossing an unbounded map."""
|
||||
if not isinstance(offset, int) or isinstance(offset, bool) or offset < 0:
|
||||
raise ValueError("offset must be a non-negative integer")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 500:
|
||||
raise ValueError("limit must be an integer between 1 and 500")
|
||||
ordered = sorted(values.items(), key=lambda item: int(item[0], 16))
|
||||
selected = ordered[offset:offset + limit]
|
||||
total = len(ordered)
|
||||
next_offset = offset + len(selected)
|
||||
has_more = next_offset < total
|
||||
return {
|
||||
"items": [{"address": address, "value": value} for address, value in selected],
|
||||
"offset": offset,
|
||||
"limit": limit,
|
||||
"total": total,
|
||||
"next_offset": next_offset if has_more else None,
|
||||
"has_more": has_more,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def _unavailable(reason):
|
||||
@@ -583,36 +422,6 @@ def _unavailable(reason):
|
||||
return {"available": False, "reason": reason}
|
||||
|
||||
|
||||
def _offset(params, name):
|
||||
value = params.get(name, 0)
|
||||
if not isinstance(value, int) or isinstance(value, bool) or value < 0:
|
||||
raise ValueError("%s must be a non-negative integer" % name)
|
||||
return value
|
||||
|
||||
|
||||
def _collection_offset(params, name):
|
||||
values = params.get("collection_offset", {})
|
||||
if not isinstance(values, dict):
|
||||
raise ValueError("collection_offset must be an object")
|
||||
value = values.get(name, 0)
|
||||
if not isinstance(value, int) or isinstance(value, bool) or value < 0:
|
||||
raise ValueError("collection_offset.%s must be a non-negative integer" % name)
|
||||
return value
|
||||
|
||||
|
||||
def _bounded(items, offset, limit, total=None, scan_truncated=False):
|
||||
selected = items[offset:offset + limit]
|
||||
known_total = len(items) if total is None and not scan_truncated else total
|
||||
has_more = offset + len(selected) < len(items)
|
||||
return {
|
||||
"items": selected,
|
||||
"total": known_total,
|
||||
"returned": len(selected),
|
||||
"truncated": scan_truncated or has_more,
|
||||
"next_offset": offset + len(selected) if has_more else None,
|
||||
}
|
||||
|
||||
|
||||
def _name_map(document):
|
||||
result = {}
|
||||
for segment in document.getSegmentsList():
|
||||
@@ -639,8 +448,8 @@ def _render_instruction(segment, address):
|
||||
)
|
||||
|
||||
|
||||
def _assembly(procedure, limit=None):
|
||||
"""Render bounded assembly while guarding against malformed instruction cycles."""
|
||||
def _assembly(procedure):
|
||||
"""Render assembly while guarding against malformed instruction cycles."""
|
||||
lines = []
|
||||
segment = procedure.getSegment()
|
||||
seen = set()
|
||||
@@ -655,8 +464,6 @@ def _assembly(procedure, limit=None):
|
||||
line = _render_instruction(segment, address)
|
||||
if line is not None:
|
||||
lines.append(line)
|
||||
if limit is not None and len(lines) >= limit:
|
||||
return "\n".join(lines)
|
||||
length = instruction.getInstructionLength()
|
||||
if length <= 0:
|
||||
break
|
||||
@@ -665,37 +472,18 @@ def _assembly(procedure, limit=None):
|
||||
|
||||
|
||||
def _read_function_instructions(document, params):
|
||||
"""Read one bounded raw-instruction window without invoking decompilation."""
|
||||
"""Read all raw instructions without invoking decompilation."""
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
offset = _offset(params, "offset")
|
||||
limit = params.get("limit", 64)
|
||||
if offset > 100000:
|
||||
raise ValueError("offset must not exceed 100000")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 500:
|
||||
raise ValueError("limit must be an integer between 1 and 500")
|
||||
addresses, scan_truncated = _instruction_addresses(
|
||||
procedure, offset + limit + 1
|
||||
)
|
||||
start = min(offset, len(addresses))
|
||||
end = min(len(addresses), start + limit)
|
||||
addresses = _instruction_addresses(procedure)
|
||||
segment = procedure.getSegment()
|
||||
items = []
|
||||
for address in addresses[start:end]:
|
||||
for address in addresses:
|
||||
line = _render_instruction(segment, address)
|
||||
if line is not None:
|
||||
items.append(line)
|
||||
has_more = scan_truncated or end < len(addresses)
|
||||
return {
|
||||
"procedure": _procedure_identity(procedure),
|
||||
"instructions": {
|
||||
"items": items,
|
||||
"total": None if scan_truncated else len(addresses),
|
||||
"returned": len(items),
|
||||
"truncated": has_more,
|
||||
"next_offset": end if has_more else None,
|
||||
},
|
||||
"instructions_scanned": len(addresses),
|
||||
"instruction_scan_truncated": scan_truncated,
|
||||
"instructions": items,
|
||||
"limitations": [
|
||||
"Instruction text and ordering are Hopper-specific representations.",
|
||||
"The fast path does not decompile the procedure or scan whole-program names and strings.",
|
||||
@@ -704,20 +492,9 @@ def _read_function_instructions(document, params):
|
||||
|
||||
|
||||
def _analyze_function(document, params):
|
||||
"""Collect a bounded, single-pass function dossier for agent callers."""
|
||||
"""Collect the complete function dossier for agent callers."""
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
limit = params.get("limit", 100)
|
||||
max_chars = params.get("max_pseudocode_chars", 20000)
|
||||
max_instructions = params.get("max_instructions", 500)
|
||||
pseudocode_offset = _offset(params, "pseudocode_offset")
|
||||
assembly_offset = _offset(params, "assembly_offset")
|
||||
if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1 or limit > 500:
|
||||
raise ValueError("limit must be an integer between 1 and 500")
|
||||
if not isinstance(max_chars, int) or isinstance(max_chars, bool) or max_chars < 1 or max_chars > 100000:
|
||||
raise ValueError("max_pseudocode_chars must be between 1 and 100000")
|
||||
if not isinstance(max_instructions, int) or isinstance(max_instructions, bool) or max_instructions < 1 or max_instructions > 5000:
|
||||
raise ValueError("max_instructions must be between 1 and 5000")
|
||||
addresses, instruction_scan_truncated = _instruction_addresses(procedure, max_instructions)
|
||||
addresses = _instruction_addresses(procedure)
|
||||
blocks = []
|
||||
all_blocks = list(procedure.basicBlockIterator())
|
||||
for block in all_blocks:
|
||||
@@ -732,7 +509,7 @@ def _analyze_function(document, params):
|
||||
"successors": sorted(set(successors), key=lambda value: int(value, 16)),
|
||||
})
|
||||
pseudo = _pseudocode(document, procedure) or ""
|
||||
assembly_lines = _assembly(procedure, max_instructions).splitlines() if params.get("include_assembly", False) else []
|
||||
assembly_lines = _assembly(procedure).splitlines()
|
||||
callers = sorted((_procedure_identity(item) for item in procedure.getAllCallerProcedures()), key=lambda item: int(item["address"], 16))
|
||||
callees = sorted((_procedure_identity(item) for item in procedure.getAllCalleeProcedures()), key=lambda item: int(item["address"], 16))
|
||||
comments = []
|
||||
@@ -785,28 +562,21 @@ def _analyze_function(document, params):
|
||||
comments.sort(key=lambda item: (int(item["address"], 16), item["kind"]))
|
||||
referenced_strings.sort(key=lambda item: (int(item["address"], 16), int(item["source_address"], 16)))
|
||||
referenced_names.sort(key=lambda item: (int(item["address"], 16), int(item["source_address"], 16)))
|
||||
pseudo_text = pseudo[pseudocode_offset:pseudocode_offset + max_chars]
|
||||
pseudo_next = pseudocode_offset + len(pseudo_text)
|
||||
def collection(name, items, scan_limited=False):
|
||||
return _bounded(items, _collection_offset(params, name), limit, None, scan_limited)
|
||||
return {
|
||||
"procedure": {"address": _hex(procedure.getEntryPoint()), "name": _procedure_name(procedure), "classification": None, "signature": procedure.signatureString(), "locals": _procedure_locals(procedure)},
|
||||
"pseudocode": {"text": pseudo_text, "total_chars": len(pseudo), "returned_chars": len(pseudo_text), "truncated": pseudo_next < len(pseudo), "next_offset": pseudo_next if pseudo_next < len(pseudo) else None},
|
||||
"assembly": _bounded(
|
||||
assembly_lines,
|
||||
assembly_offset,
|
||||
max_instructions,
|
||||
None,
|
||||
instruction_scan_truncated if params.get("include_assembly", False) else False,
|
||||
),
|
||||
"comments": collection("comments", comments, instruction_scan_truncated),
|
||||
"callers": collection("callers", callers), "callees": collection("callees", callees),
|
||||
"incoming_references": collection("incoming_references", incoming, instruction_scan_truncated),
|
||||
"outgoing_references": collection("outgoing_references", outgoing, instruction_scan_truncated),
|
||||
"referenced_strings": collection("referenced_strings", referenced_strings, instruction_scan_truncated),
|
||||
"referenced_names": collection("referenced_names", referenced_names, instruction_scan_truncated),
|
||||
"basic_blocks": collection("basic_blocks", blocks),
|
||||
"instruction_scan": {"scanned": len(addresses), "truncated": instruction_scan_truncated},
|
||||
"procedure": {
|
||||
**_procedure_identity(procedure),
|
||||
"signature": procedure.signatureString(),
|
||||
"locals": _procedure_locals(procedure),
|
||||
},
|
||||
"pseudocode": pseudo,
|
||||
"assembly": assembly_lines,
|
||||
"comments": comments,
|
||||
"callers": callers, "callees": callees,
|
||||
"incoming_references": incoming,
|
||||
"outgoing_references": outgoing,
|
||||
"referenced_strings": referenced_strings,
|
||||
"referenced_names": referenced_names,
|
||||
"basic_blocks": blocks,
|
||||
"limitations": [
|
||||
"Hopper's public Python API does not classify reference kinds.",
|
||||
"Hopper's public Python API does not expose equivalent external or thunk classification in this dossier.",
|
||||
@@ -825,6 +595,13 @@ def _dispatch(method, params):
|
||||
document = _session_document()
|
||||
if document is None:
|
||||
return {"shutdown": True, "analysis_stopped": True, "document_closed": True}
|
||||
if method == "shutdown" and not REA_OWNS_PROCESS_LIFETIME:
|
||||
return {
|
||||
"shutdown": True,
|
||||
"analysis_stopped": not document.backgroundProcessActive(),
|
||||
"document_closed": False,
|
||||
"document_retained": True,
|
||||
}
|
||||
if document.backgroundProcessActive():
|
||||
document.requestBackgroundProcessStop()
|
||||
if method == "shutdown" and REA_OWNS_PROCESS_LIFETIME:
|
||||
@@ -864,9 +641,9 @@ def _dispatch(method, params):
|
||||
if method == "read_bytes":
|
||||
length = params.get("length", 256)
|
||||
if isinstance(length, bool) or not isinstance(length, int):
|
||||
raise ValueError("Byte-read length must be an integer")
|
||||
if length < 1 or length > MAX_READ_BYTES:
|
||||
raise ValueError("Byte-read length must be between 1 and 4096")
|
||||
raise InvalidRequestError("Byte-read length must be an integer")
|
||||
if length < 1:
|
||||
raise InvalidRequestError("Byte-read length must be at least 1")
|
||||
reader = getattr(document, "readBytes", None)
|
||||
if not callable(reader):
|
||||
raise CapabilityUnavailableError(
|
||||
@@ -900,7 +677,7 @@ def _dispatch(method, params):
|
||||
or offset in BAD_ADDRESSES
|
||||
or offset < 0
|
||||
):
|
||||
raise ValueError("Address has no authoritative file-offset mapping")
|
||||
raise InvalidRequestError("Address has no authoritative file-offset mapping")
|
||||
return {"address": _hex(address), "file_offset": offset}
|
||||
if method == "resolve_containing_procedure":
|
||||
address = _address(document, params.get("address"))
|
||||
@@ -936,7 +713,7 @@ def _dispatch(method, params):
|
||||
else:
|
||||
result = document.getInstructionStart(max(0, target - 1))
|
||||
if result in BAD_ADDRESSES:
|
||||
raise ValueError("No adjacent address")
|
||||
raise InvalidRequestError("No adjacent address")
|
||||
return _hex(result)
|
||||
if method == "list_segments":
|
||||
result = []
|
||||
@@ -968,24 +745,24 @@ def _dispatch(method, params):
|
||||
})
|
||||
return result
|
||||
if method == "list_procedures":
|
||||
return _page(dict(_search_inventory(document, "procedure")), params.get("offset", 0), params.get("limit", 100))
|
||||
return [{"address": address, "value": value} for address, value in _search_inventory(document, "procedure")]
|
||||
if method == "list_strings":
|
||||
values = dict(_search_inventory(document, "string"))
|
||||
requested = params.get("address")
|
||||
if requested is not None:
|
||||
key = _hex(_address(document, requested))
|
||||
values = {key: values[key]} if key in values else {}
|
||||
return _page(values, params.get("offset", 0), params.get("limit", 100))
|
||||
return [{"address": address, "value": value} for address, value in values.items()]
|
||||
if method == "list_names":
|
||||
result = dict(_search_inventory(document, "name"))
|
||||
requested = params.get("address")
|
||||
if requested is not None:
|
||||
key = _hex(_address(document, requested))
|
||||
result = {key: result[key]} if key in result else {}
|
||||
return _page(result, params.get("offset", 0), params.get("limit", 100))
|
||||
return [{"address": address, "value": value} for address, value in result.items()]
|
||||
if method in ("search_procedures", "search_strings"):
|
||||
kind = "procedure" if method == "search_procedures" else "string"
|
||||
return _search_page(document, kind, params)
|
||||
return _search_results(document, kind, params)
|
||||
if method.startswith("procedure_"):
|
||||
procedure = _procedure(document, params.get("procedure"))
|
||||
if method == "procedure_address":
|
||||
@@ -1001,7 +778,16 @@ def _dispatch(method, params):
|
||||
if method == "procedure_info":
|
||||
blocks = list(procedure.basicBlockIterator())
|
||||
length = sum(max(0, block.getEndingAddress() - block.getStartingAddress()) for block in blocks)
|
||||
return {"name": _procedure_name(procedure), "entrypoint": _hex(procedure.getEntryPoint()), "basicblock_count": procedure.getBasicBlockCount(), "length": length, "signature": procedure.signatureString(), "locals": _procedure_locals(procedure), "classification": None}
|
||||
return {
|
||||
"name": _procedure_name(procedure),
|
||||
"entrypoint": _hex(procedure.getEntryPoint()),
|
||||
"basicblock_count": procedure.getBasicBlockCount(),
|
||||
"length": length,
|
||||
"signature": procedure.signatureString(),
|
||||
"locals": _procedure_locals(procedure),
|
||||
"classification": None,
|
||||
"body": _procedure_identity(procedure)["body"],
|
||||
}
|
||||
if method == "set_address_name":
|
||||
address = _address(document, params.get("address"))
|
||||
try:
|
||||
@@ -1035,27 +821,38 @@ def _dispatch(method, params):
|
||||
address = _address(document, params.get("address"))
|
||||
document.removeBookmarkAtAddress(address)
|
||||
return not document.hasBookmarkAtAddress(address)
|
||||
raise ValueError("Unknown bridge method")
|
||||
raise InvalidRequestError("Unknown bridge method")
|
||||
|
||||
|
||||
def _serve_connection(connection):
|
||||
"""Serve one size-bounded, capability-authenticated NDJSON connection."""
|
||||
"""Serve one capability-authenticated NDJSON connection."""
|
||||
file = connection.makefile("rwb")
|
||||
while True:
|
||||
line = file.readline(MAX_LINE_BYTES + 1)
|
||||
if not line or len(line) > MAX_LINE_BYTES:
|
||||
line = file.readline()
|
||||
if not line:
|
||||
break
|
||||
request_id = None
|
||||
authenticated = False
|
||||
should_stop = False
|
||||
try:
|
||||
request = json.loads(line.decode("utf-8"))
|
||||
if set(request) != {"id", "token", "method", "params"}:
|
||||
raise ValueError("Invalid bridge request shape")
|
||||
try:
|
||||
request = json.loads(line.decode("utf-8"))
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||
raise InvalidRequestError("Invalid bridge request JSON") from error
|
||||
if not isinstance(request, dict) or set(request) != {"id", "token", "method", "params"}:
|
||||
raise InvalidRequestError("Invalid bridge request shape")
|
||||
request_id = request["id"]
|
||||
if isinstance(request_id, bool) or not isinstance(request_id, int) or request_id < 0:
|
||||
raise ValueError("Invalid bridge request id")
|
||||
if not isinstance(request["token"], str) or not hmac.compare_digest(request["token"], REA_TOKEN):
|
||||
raise InvalidRequestError("Invalid bridge request id")
|
||||
if (
|
||||
not isinstance(request["method"], str) or not request["method"]
|
||||
or not isinstance(request["params"], dict)
|
||||
):
|
||||
raise InvalidRequestError("Invalid bridge method or parameters")
|
||||
if (
|
||||
not isinstance(request["token"], str) or not request["token"].isascii()
|
||||
or not hmac.compare_digest(request["token"], REA_TOKEN)
|
||||
):
|
||||
raise PermissionError("Invalid bridge capability")
|
||||
authenticated = True
|
||||
_write_message(file, {"id": request_id, "event": {
|
||||
@@ -1110,7 +907,7 @@ def _diagnostic_type(error):
|
||||
return "capability_unavailable"
|
||||
if isinstance(error, PermissionError):
|
||||
return "authorization"
|
||||
if isinstance(error, (ValueError, TypeError, KeyError)):
|
||||
if isinstance(error, InvalidRequestError):
|
||||
return "invalid_request"
|
||||
return "bridge_exception"
|
||||
|
||||
@@ -1119,13 +916,13 @@ def _safe_diagnostic(error):
|
||||
"""Project one exception without retaining provider or credential text."""
|
||||
diagnostic_type = _diagnostic_type(error)
|
||||
if diagnostic_type == "capability_unavailable":
|
||||
message = str(error)[:512]
|
||||
message = str(error)
|
||||
elif diagnostic_type == "authorization":
|
||||
message = "Invalid bridge capability"
|
||||
elif diagnostic_type == "invalid_request":
|
||||
message = "Invalid Hopper bridge request"
|
||||
else:
|
||||
message = "%s: Hopper bridge operation failed" % type(error).__name__[:128]
|
||||
message = "%s: Hopper bridge operation failed" % type(error).__name__
|
||||
return {"code": -32000, "message": message, "type": diagnostic_type}
|
||||
|
||||
|
||||
@@ -1147,6 +944,7 @@ def _run():
|
||||
|
||||
|
||||
# Hopper's public objects are bound to its dedicated Python execution thread.
|
||||
# Keep dispatch on that thread; moving calls to an arbitrary worker can deadlock.
|
||||
# Keep dispatch on that thread; moving calls to a worker can deadlock.
|
||||
if __name__ == "__main__":
|
||||
_bind_session_document()
|
||||
_run()
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import Foundation
|
||||
|
||||
struct ChildBatch<Element> {
|
||||
let values: [Element]
|
||||
let complete: Bool
|
||||
}
|
||||
|
||||
struct ChildCount {
|
||||
let value: Int?
|
||||
let error: Int32?
|
||||
}
|
||||
|
||||
func serializeHelperOutput(_ value: Any) throws -> Data {
|
||||
try JSONSerialization.data(withJSONObject: value, options: [.sortedKeys, .withoutEscapingSlashes])
|
||||
}
|
||||
|
||||
func captureChildCount(status: Int32, success: Int32, value: Int) -> ChildCount {
|
||||
guard status == success else { return ChildCount(value: nil, error: status) }
|
||||
return ChildCount(value: value, error: nil)
|
||||
}
|
||||
|
||||
func captureChildValues<Element>(requestedCount: Int, copy: () -> [Element]?) -> ChildBatch<Element> {
|
||||
guard requestedCount > 0 else { return ChildBatch(values: [], complete: true) }
|
||||
let returned = copy() ?? []
|
||||
let values = Array(returned.prefix(requestedCount))
|
||||
return ChildBatch(values: values, complete: values.count == requestedCount)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import AppKit
|
||||
import ApplicationServices
|
||||
import CryptoKit
|
||||
import ScreenCaptureKit
|
||||
import Darwin
|
||||
|
||||
struct Request: Decodable {
|
||||
var pid: Int32
|
||||
var window_id: UInt32
|
||||
var executable: String
|
||||
var sha256: String
|
||||
var launch_time: Double?
|
||||
var screenshot: Bool
|
||||
var accessibility: Bool
|
||||
var max_nodes: Int
|
||||
var action: Action?
|
||||
}
|
||||
struct Action: Decodable { var kind: String; var path: [Int]?; var direction: String?; var text: String? }
|
||||
struct BoundaryFailure: Error { var code: String; var message: String }
|
||||
func fail(_ code: String, _ message: String) throws -> Never { throw BoundaryFailure(code: code, message: message) }
|
||||
func attribute(_ element: AXUIElement, _ key: String) -> CFTypeRef? {
|
||||
var value: CFTypeRef?
|
||||
return AXUIElementCopyAttributeValue(element, key as CFString, &value) == .success ? value : nil
|
||||
}
|
||||
func childCount(_ element: AXUIElement) -> ChildCount {
|
||||
var count = 0
|
||||
let status = AXUIElementGetAttributeValueCount(element, kAXChildrenAttribute as CFString, &count)
|
||||
return captureChildCount(status: status.rawValue, success: AXError.success.rawValue, value: count)
|
||||
}
|
||||
func children(_ element: AXUIElement, count: Int) -> ChildBatch<AXUIElement> {
|
||||
captureChildValues(requestedCount: count) {
|
||||
var value: CFArray?
|
||||
guard AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, 0, count, &value) == .success else { return nil }
|
||||
return value as? [AXUIElement]
|
||||
}
|
||||
}
|
||||
func text(_ element: AXUIElement, _ key: String) -> Any { (attribute(element, key) as? String) ?? NSNull() as Any }
|
||||
func windowBounds(_ element: AXUIElement) -> CGRect? {
|
||||
guard let p = attribute(element, kAXPositionAttribute), let s = attribute(element, kAXSizeAttribute),
|
||||
CFGetTypeID(p) == AXValueGetTypeID(), CFGetTypeID(s) == AXValueGetTypeID() else { return nil }
|
||||
var point = CGPoint.zero; var size = CGSize.zero
|
||||
guard AXValueGetValue(p as! AXValue, .cgPoint, &point), AXValueGetValue(s as! AXValue, .cgSize, &size) else { return nil }
|
||||
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 canonicalPointer = realpath(url.path, nil) else { try fail("target-mismatch", "Running executable path cannot be resolved") }
|
||||
defer { free(canonicalPointer) }
|
||||
let canonicalExecutable = String(cString: canonicalPointer)
|
||||
guard canonicalExecutable == request.executable else {
|
||||
try fail("target-mismatch", "Selected PID executable \(canonicalExecutable) differs from active target \(request.executable)")
|
||||
}
|
||||
guard let launch = app.launchDate?.timeIntervalSince1970 else {
|
||||
try fail("process-identity-unavailable", "Selected application does not expose a launch time; stable PID reuse checks are unavailable")
|
||||
}
|
||||
if let expected = request.launch_time, expected != launch { try fail("process-replaced", "Selected process was replaced") }
|
||||
let executableSize = (try FileManager.default.attributesOfItem(atPath: url.path)[.size] as? NSNumber)?.int64Value ?? -1
|
||||
guard executableSize >= 0 && executableSize <= 256 * 1024 * 1024 else { try fail("target-limit", "Running executable exceeds the 256 MiB identity-check budget") }
|
||||
let executableBytes = try Data(contentsOf: url, options: .mappedIfSafe)
|
||||
guard SHA256.hash(data: executableBytes).map({ String(format: "%02x", $0) }).joined() == request.sha256 else {
|
||||
try fail("target-mismatch", "Running executable bytes differ from the active target digest")
|
||||
}
|
||||
guard let windows = CGWindowListCopyWindowInfo([.optionIncludingWindow], request.window_id) as? [[String: Any]],
|
||||
windows.count == 1, let window = windows.first,
|
||||
(window[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == request.pid,
|
||||
(window[kCGWindowLayer as String] as? NSNumber)?.intValue == 0,
|
||||
let boundsDictionary = window[kCGWindowBounds as String] as? [String: Any],
|
||||
let bounds = CGRect(dictionaryRepresentation: boundsDictionary as CFDictionary) else {
|
||||
try fail("window-mismatch", "Selected window is absent or belongs to another process")
|
||||
}
|
||||
if request.screenshot && !CGPreflightScreenCaptureAccess() {
|
||||
try fail("screen-recording-denied", "Grant Screen Recording to the REA host in System Settings > Privacy & Security, then retry; no broad capture fallback is used")
|
||||
}
|
||||
var selected: AXUIElement?
|
||||
if request.accessibility || request.action != nil {
|
||||
guard AXIsProcessTrusted() else { try fail("accessibility-denied", "Grant Accessibility to the REA host in System Settings > Privacy & Security, then retry") }
|
||||
let application = AXUIElementCreateApplication(request.pid)
|
||||
AXUIElementSetMessagingTimeout(application, 2)
|
||||
guard let axWindows = attribute(application, kAXWindowsAttribute) as? [AXUIElement] else {
|
||||
try fail("accessibility-unavailable", "Selected application does not expose accessibility windows")
|
||||
}
|
||||
let matches = axWindows.filter { windowBounds($0) == bounds }
|
||||
guard matches.count == 1 else { try fail("ambiguous-window", "Accessibility window geometry does not identify exactly one selected window; selected CG bounds \(bounds), AX candidates \(axWindows.map { String(describing: windowBounds($0)) })") }
|
||||
selected = matches[0]
|
||||
}
|
||||
if let action = request.action, let root = selected {
|
||||
var element = root
|
||||
for index in action.path ?? [] {
|
||||
var selectedChild: CFArray?
|
||||
let count = childCount(element)
|
||||
guard let available = count.value else {
|
||||
try fail("element-count-unavailable", "Cannot validate selected accessibility path because child count failed with AXError \(count.error ?? -1)")
|
||||
}
|
||||
guard index >= 0 && index < available,
|
||||
AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, index, 1, &selectedChild) == .success,
|
||||
let item = (selectedChild as? [AXUIElement])?.first else { try fail("element-missing", "Selected accessibility path no longer exists") }
|
||||
element = item
|
||||
}
|
||||
var owner: pid_t = 0
|
||||
guard AXUIElementGetPid(element, &owner) == .success && owner == request.pid else { try fail("element-owner-mismatch", "Selected element belongs to another process") }
|
||||
let outcome: AXError
|
||||
switch action.kind {
|
||||
case "click": outcome = AXUIElementPerformAction(element, kAXPressAction as CFString)
|
||||
case "scroll": outcome = AXUIElementPerformAction(element, (action.direction == "increment" ? kAXIncrementAction : kAXDecrementAction) as CFString)
|
||||
case "key-entry": outcome = AXUIElementSetAttributeValue(element, kAXValueAttribute as CFString, (action.text ?? "") as CFString)
|
||||
default: try fail("unsupported-action", "Only selected-element press, increment/decrement and text-value entry are admitted")
|
||||
}
|
||||
guard outcome == .success else { try fail("action-failed", "Accessibility action failed with AXError \(outcome.rawValue)") }
|
||||
}
|
||||
var nodes: [[String: Any]] = []; var truncated = false; var gaps: [String] = []
|
||||
if request.accessibility, let root = selected {
|
||||
var pending: [(AXUIElement, [Int])] = [(root, [])]
|
||||
while let (element, path) = pending.popLast() {
|
||||
if nodes.count >= request.max_nodes { truncated = true; break }
|
||||
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()])
|
||||
guard let totalChildren = childCountResult.value else {
|
||||
truncated = true
|
||||
gaps.append("AX child count unavailable at path \(path): AXError \(childCountResult.error ?? -1)")
|
||||
continue
|
||||
}
|
||||
if path.count >= 32 { if totalChildren > 0 { truncated = true }; continue }
|
||||
let available = max(0, request.max_nodes - nodes.count - pending.count)
|
||||
let count = min(available, totalChildren)
|
||||
let batch = children(element, count: count)
|
||||
if !batch.complete { truncated = true }
|
||||
if totalChildren > count { truncated = true }
|
||||
for (index, item) in batch.values.enumerated().reversed() {
|
||||
pending.append((item, path + [index]))
|
||||
}
|
||||
}
|
||||
}
|
||||
var screenshot: Any = NSNull()
|
||||
if request.screenshot {
|
||||
guard #available(macOS 14, *) else { try fail("unsupported-host", "Selected-window screenshots require macOS 14 or later") }
|
||||
let content = try await SCShareableContent.excludingDesktopWindows(true, onScreenWindowsOnly: false)
|
||||
guard let scWindow = content.windows.first(where: { $0.windowID == request.window_id && $0.owningApplication?.processID == request.pid }) else {
|
||||
try fail("capture-window-missing", "Selected window disappeared before capture")
|
||||
}
|
||||
guard bounds.width > 0 && bounds.height > 0 && bounds.width <= 100000 && bounds.height <= 100000 else { try fail("window-bounds", "Selected window has invalid or oversized geometry") }
|
||||
let filter = SCContentFilter(desktopIndependentWindow: scWindow)
|
||||
let configuration = SCStreamConfiguration()
|
||||
let scale = min(1, 2048 / max(bounds.width, bounds.height))
|
||||
configuration.width = max(1, Int(bounds.width * scale)); configuration.height = max(1, Int(bounds.height * scale))
|
||||
configuration.showsCursor = false
|
||||
let image = try await SCScreenshotManager.captureImage(contentFilter: filter, configuration: configuration)
|
||||
guard let png = NSBitmapImageRep(cgImage: image).representation(using: .png, properties: [:]) else {
|
||||
try fail("capture-failed", "Selected-window PNG encoding failed")
|
||||
}
|
||||
screenshot = ["mime_type": "image/png", "base64": png.base64EncodedString(), "sha256": SHA256.hash(data: png).map({ String(format: "%02x", $0) }).joined(), "width": image.width, "height": image.height]
|
||||
}
|
||||
return ["window": ["pid": request.pid, "window_id": request.window_id, "executable": request.executable, "launch_time": launch, "title": window[kCGWindowName as String] as? String ?? ""], "nodes": nodes, "truncated": truncated, "screenshot": screenshot, "gaps": request.accessibility ? gaps : ["Accessibility capture was disabled"]]
|
||||
}
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let request = try JSONDecoder().decode(Request.self, from: Data(CommandLine.arguments[1].utf8))
|
||||
let result = try await observe(request)
|
||||
let output = try serializeHelperOutput(["ok": true, "result": result])
|
||||
FileHandle.standardOutput.write(output)
|
||||
} catch {
|
||||
let failure = error as? BoundaryFailure
|
||||
let output: [String: Any] = ["ok": false, "code": failure?.code ?? "capture-failed", "message": failure?.message ?? error.localizedDescription]
|
||||
if let data = try? serializeHelperOutput(output) { FileHandle.standardOutput.write(data) }
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
RunLoop.main.run()
|
||||
@@ -0,0 +1,84 @@
|
||||
import { defineConfig } from "vitepress";
|
||||
|
||||
export default defineConfig({
|
||||
title: "REA",
|
||||
description:
|
||||
"Reverse engineer binaries, applications, and runtime behavior with one CLI and MCP server.",
|
||||
lang: "en-US",
|
||||
base: "/rea/",
|
||||
themeConfig: {
|
||||
nav: [
|
||||
{ text: "Get started", link: "/installation" },
|
||||
{ text: "Reference", link: "/mcp-contracts" },
|
||||
{ text: "Roadmap", link: "/roadmap" },
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
text: "Getting started",
|
||||
items: [
|
||||
{ text: "Installation and setup", link: "/installation" },
|
||||
{ text: "Agent prompts", link: "/mcp-prompts" },
|
||||
{ text: "Roadmap", link: "/roadmap" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Investigation guides",
|
||||
items: [
|
||||
{ text: "Native binaries", link: "/native-investigation" },
|
||||
{
|
||||
text: "JavaScript and Electron",
|
||||
link: "/javascript-artifact-reconstruction",
|
||||
},
|
||||
{
|
||||
text: "JavaScript workflows",
|
||||
link: "/javascript-application-workflows",
|
||||
},
|
||||
{ text: "Browser observation", link: "/browser-observation" },
|
||||
{ text: "Electron observation", link: "/electron-observation" },
|
||||
{ text: "Process capture", link: "/process-capture" },
|
||||
{ text: ".NET assemblies", link: "/managed-code-analysis" },
|
||||
{ text: "Android applications", link: "/android-analysis" },
|
||||
{ text: "Apple applications", link: "/apple-application-analysis" },
|
||||
{ text: "Firmware", link: "/firmware-analysis" },
|
||||
{
|
||||
text: "Reconstruction readiness",
|
||||
link: "/reconstruction-readiness",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Provider guides",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "IDA", link: "/ida-provider" },
|
||||
{ text: "Ghidra on Windows", link: "/windows-ghidra-p0" },
|
||||
{ text: "Ghidra DOS analysis", link: "/ghidra-dos" },
|
||||
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
|
||||
{ text: "Provider evaluation", link: "/provider-evaluation" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Reference and development",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "MCP contracts", link: "/mcp-contracts" },
|
||||
{ text: "Tool design", link: "/tool-design" },
|
||||
{ text: "Testing", link: "/testing" },
|
||||
{ text: "Releasing", link: "/releasing" },
|
||||
{ text: "Architecture decisions", link: "/adr/README" },
|
||||
],
|
||||
},
|
||||
],
|
||||
search: { provider: "local" },
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/morluto/rea" },
|
||||
{ icon: "discord", link: "https://discord.gg/GkcryMnJDM" },
|
||||
],
|
||||
editLink: {
|
||||
pattern: "https://github.com/morluto/rea/edit/main/docs/:path",
|
||||
},
|
||||
footer: {
|
||||
message: "Released under the MIT License.",
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -5,9 +5,12 @@
|
||||
- Implementation status: The provider registry, deterministic selection,
|
||||
target binding, analysis-profile commitment, and snapshot/Evidence migration
|
||||
are implemented. Ghidra discovery, target/profile resolution, doctor checks,
|
||||
the private headless-session foundation, ten read-only inventory capabilities,
|
||||
and nine function-analysis capabilities are implemented with real
|
||||
cross-format conformance.
|
||||
the private headless-session foundation, thirteen read-only inventory/load-image
|
||||
capabilities, and twelve function-analysis capabilities are implemented with
|
||||
real cross-format conformance. Linux/macOS also supports atomic session
|
||||
function annotations; experimental Windows x64 P0 uses bundled native controls
|
||||
and admits only the 25 read-only operations. See the
|
||||
[release boundary](../installation.md#released-package-and-main).
|
||||
|
||||
## Context
|
||||
|
||||
@@ -25,13 +28,13 @@ engine, engine version, or complete analysis configuration. Two analyses of the
|
||||
same bytes could therefore disagree because of language, compiler, loader,
|
||||
analyzer, or provider-version differences while appearing target-compatible.
|
||||
|
||||
These constraints are embodied in the current
|
||||
[`CompositeProvider`](../../src/application/CompositeProvider.ts),
|
||||
[runtime composition](../../src/application/runtime.ts),
|
||||
[`BinarySession`](../../src/application/BinarySession.ts),
|
||||
[`BinaryTarget`](../../src/domain/binaryTarget.ts),
|
||||
[analysis snapshot](../../src/domain/analysisSnapshot.ts), and
|
||||
[session lifecycle inputs](../../src/contracts/sessionLifecycleInputs.ts).
|
||||
The decision addressed the earlier representations of
|
||||
[`CompositeProvider`](https://github.com/morluto/rea/blob/main/src/application/binary/CompositeProvider.ts),
|
||||
[runtime composition](https://github.com/morluto/rea/blob/main/src/composition/binary.ts),
|
||||
[`BinarySession`](https://github.com/morluto/rea/blob/main/src/application/binary/BinarySession.ts),
|
||||
[`BinaryTarget`](https://github.com/morluto/rea/blob/main/src/domain/binaryTarget.ts),
|
||||
[analysis snapshot](https://github.com/morluto/rea/blob/main/src/domain/analysisSnapshot.ts), and
|
||||
[session lifecycle inputs](https://github.com/morluto/rea/blob/main/src/contracts/sessionLifecycleInputs.ts).
|
||||
|
||||
The next deep provider must not weaken the existing product invariants:
|
||||
|
||||
@@ -278,9 +281,7 @@ The adapter returns this provider-neutral envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"provider": { "id": "ghidra", "version": "<concrete version>" },
|
||||
"provider_profile_schema_version": 1,
|
||||
"parameters": {},
|
||||
"digest": "<sha256 of RFC 8785 canonical JSON excluding digest>"
|
||||
}
|
||||
@@ -295,8 +296,8 @@ parameters as opaque data.
|
||||
A selected deep binding requires a concrete provider version. If the adapter
|
||||
cannot determine it, the candidate is unavailable with `version_unresolved`;
|
||||
REA does not persist a cache that merely commits `null` or "whatever is
|
||||
installed". Provider identity, provider-profile schema version, normalized
|
||||
parameters, or provider version changes produce a different digest.
|
||||
installed". Provider identity, normalized parameters, or provider version
|
||||
changes produce a different digest.
|
||||
|
||||
The same target, configuration, and provider must resolve the same profile over
|
||||
CLI and MCP. Profile resolution must be side-effect-free and complete before
|
||||
@@ -304,11 +305,10 @@ the provider imports the target.
|
||||
|
||||
### 7. Make snapshots provider- and profile-exact
|
||||
|
||||
Analysis snapshot v2 replaces Hopper loader arguments with an explicit binding
|
||||
Analysis snapshots replace Hopper loader arguments with an explicit binding
|
||||
commitment:
|
||||
|
||||
```text
|
||||
snapshot_version: 2
|
||||
target: digest + generic format/kind + selected architecture
|
||||
binding: provider identity + analysis profile commitment
|
||||
entries: exact immutable queries
|
||||
@@ -321,7 +321,7 @@ A cached entry is reusable only when all of the following match exactly:
|
||||
|
||||
- target digest, generic format and kind, and selected architecture;
|
||||
- provider ID and provider version;
|
||||
- profile schema versions and profile digest;
|
||||
- profile digest;
|
||||
- operation and canonical parameters; and
|
||||
- the existing cacheability and immutability rules.
|
||||
|
||||
@@ -330,20 +330,20 @@ entries from the previous binding. Auxiliary operations are cacheable only if
|
||||
their provider defines an equivalent explicit profile commitment; otherwise
|
||||
they remain Evidence but not snapshot entries.
|
||||
|
||||
Snapshot v1 is not silently upgraded. Its `loader_args` cannot prove which
|
||||
engine version, defaults, or analyzer settings produced the entries. Importing
|
||||
v1 returns a typed incompatibility with a recapture instruction. Its embedded
|
||||
Evidence bundle may still be imported separately through the existing explicit
|
||||
Evidence import flow; it is never promoted into a v2 analysis cache.
|
||||
Snapshot v1 cannot be parsed as the current snapshot format. Its `loader_args`
|
||||
cannot prove which engine version, defaults, or analyzer settings produced the
|
||||
entries, so snapshots must be recaptured before they can be used as a cache.
|
||||
An embedded Evidence bundle can still be imported separately through the
|
||||
Evidence import flow; it is never promoted into an analysis cache.
|
||||
|
||||
### 8. Add profile commitments to new deep Evidence without rejecting legacy Evidence v2
|
||||
### 8. Add profile commitments to new deep Evidence while preserving legacy records
|
||||
|
||||
Evidence remains schema version 2. A backward-compatible optional
|
||||
`analysis_profile` field uses the canonical envelope above. Every newly created
|
||||
deep-analysis observation must include it, and its provider ID and version must
|
||||
match the record's provider. The field participates in the Evidence semantic ID.
|
||||
The optional `analysis_profile` field uses the canonical envelope above. Every
|
||||
newly created deep-analysis observation must include it, and its provider ID
|
||||
and version must match the record's provider. The field participates in the
|
||||
Evidence semantic ID.
|
||||
|
||||
Existing Evidence v2 records without `analysis_profile` remain valid and keep
|
||||
Existing Evidence records without `analysis_profile` remain valid and keep
|
||||
their existing IDs. Absence on a legacy deep record means the analysis profile
|
||||
is unknown; REA does not infer or enrich it during import. Such evidence can be
|
||||
displayed and cited but cannot establish profile compatibility, seed snapshot
|
||||
@@ -459,9 +459,9 @@ snapshot's provider.
|
||||
| `open_binary` and CLI structured `loaderArgs` output | Retained as a deprecated Hopper-only compatibility projection for 1.x; the analysis profile is authoritative |
|
||||
| `BinaryTarget.loaderArgs` | Removed; Hopper derives them inside its adapter and commits normalized semantics in the profile |
|
||||
| Legacy `.hop` input | Classified generically as an analysis database and accepted only by Hopper |
|
||||
| Snapshot v1 | Rejected for cache replay with explicit recapture guidance; embedded Evidence may be imported separately |
|
||||
| Existing Evidence v2 without a profile | Accepted unchanged; profile compatibility is unknown |
|
||||
| New deep-analysis Evidence v2 | Must include `analysis_profile`; its semantic ID commits the field |
|
||||
| Snapshot v1 | Not accepted as the current snapshot format; recapture before cache replay |
|
||||
| Existing Evidence without a profile | Accepted unchanged; profile compatibility is unknown |
|
||||
| New deep-analysis Evidence | Must include `analysis_profile`; its semantic ID commits the field |
|
||||
| Existing status fields | Retained for 1.x with additive binding and candidate fields |
|
||||
| Provider failure after selection | Returned from the selected provider; no transparent retry through another engine |
|
||||
|
||||
@@ -485,7 +485,6 @@ with a separately documented major contract change.
|
||||
| Same target is reopened without a selector | Existing binding remains unchanged |
|
||||
| Same bytes are opened with a different profile | Real provider switch and a separate snapshot partition |
|
||||
| Snapshot provider or profile differs from the binding | Typed `EvidenceIntegrityError` with `profile_mismatch`; nothing is imported |
|
||||
| Snapshot v1 is supplied | Typed incompatibility with recapture or explicit Evidence-import guidance |
|
||||
| Artifact-only target has no deep candidate | Target opens unbound; disjoint artifact capabilities remain usable |
|
||||
| Environment prefers Ghidra for an artifact-only target | Deep selection is `not_applicable`; auxiliary operations remain usable |
|
||||
| Concrete deep provider is requested for artifact-only data | Typed `target_unsupported`; the request is not silently ignored |
|
||||
@@ -504,7 +503,7 @@ Positive consequences:
|
||||
Costs and tradeoffs:
|
||||
|
||||
- Multi-provider hosts must choose when more than one candidate is usable.
|
||||
- Snapshot v1 cannot safely serve as a cache after this migration.
|
||||
- Snapshots must be recaptured when their provider or profile binding changes.
|
||||
- Providers must expose deterministic availability, support, version, and
|
||||
profile resolution before they can be admitted.
|
||||
- Status and error contracts gain additive structured metadata.
|
||||
@@ -561,16 +560,19 @@ The first six implementation stages are shipped:
|
||||
4. Ghidra availability, target/profile resolution, doctor checks, and a private
|
||||
read-only headless session were admitted without declaring operations.
|
||||
5. Program identity, procedures, strings, symbols, memory blocks,
|
||||
address/name resolution, containing-procedure resolution, and bounded search
|
||||
address/name resolution, containing-procedure resolution, and search
|
||||
were admitted with exact wire schemas and real debug/stripped ELF
|
||||
conformance.
|
||||
6. Function metadata, persistent bounded decompilation, assembly, resolved
|
||||
6. Function metadata, persistent decompilation, assembly, resolved
|
||||
callers/callees, typed references, xrefs, CFG, and complete function dossiers
|
||||
were admitted. A bounded per-Program queue serializes API access, and real
|
||||
conformance covers x86-64 and AArch64 ELF, PE, Mach-O, stripped symbols,
|
||||
targetless indirect calls, cancellation, deadlines, and cleanup.
|
||||
were admitted. A serial per-Program queue keeps API access ordered without a
|
||||
fixed queue length or per-operation deadline. Real conformance covers
|
||||
x86-64 and AArch64 ELF, PE, Mach-O, stripped symbols, targetless indirect
|
||||
calls, cancellation, and cleanup.
|
||||
|
||||
Future stages may deepen format and indirect-flow coverage, but must continue
|
||||
to compare normalized semantics rather than provider-specific pseudocode or
|
||||
assembly text. Ghidra continues to omit GUI and mutation operations, so
|
||||
unsupported requests cannot route to an unverified implementation.
|
||||
assembly text. Linux and macOS now admit atomic function-name and entry-comment
|
||||
edits in the ephemeral database, with readback, rollback, and cache invalidation.
|
||||
Executable bytes remain unchanged. Windows P0 retains its read-only boundary;
|
||||
GUI and other unimplemented mutations remain unavailable.
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# ADR-0002: Controlled JavaScript replay authority and sandbox policy
|
||||
|
||||
- Status: Accepted
|
||||
- Status: Superseded; the controlled JavaScript replay tool was removed
|
||||
- Date: 2026-07-16
|
||||
- Implementation status: Implemented by `run_controlled_replay` with a
|
||||
Linux-x86_64 Bubblewrap, seccomp, and delegated systemd user-cgroup backend.
|
||||
Hosts that cannot establish every required boundary fail closed.
|
||||
- Implementation status: The implementation was removed. The design below is
|
||||
retained as historical context and does not describe a supported REA tool.
|
||||
|
||||
> Historical design note: The tool, sandbox backend, and plan/execute workflow
|
||||
> described here are no longer part of REA. For runtime behavior, run probes
|
||||
> directly against the application and use the available browser, Electron, or
|
||||
> process capture workflows.
|
||||
|
||||
## Context
|
||||
|
||||
@@ -168,9 +172,10 @@ requires an active permission grant and literal per-call approval bound to the
|
||||
exact replay plan. Project, session, and administrator grants may satisfy the
|
||||
permission-policy layer, but none replace that per-run approval.
|
||||
|
||||
Reproducer export is a separate host write after the sandbox has stopped. It
|
||||
uses the existing bounded `evidence_write` authority and a separate export
|
||||
approval; `javascript_replay` alone never writes a host file.
|
||||
Reproducer export is a separate host write after the sandbox has stopped. This
|
||||
ADR's root-scoped `evidence_write` authority was removed; current exports use
|
||||
the path supplied by the caller, while replay still waits for complete sandbox
|
||||
cleanup before writing.
|
||||
|
||||
### 2. Admit extracted modules only
|
||||
|
||||
@@ -226,7 +231,7 @@ The plan must disclose at least:
|
||||
projection; and
|
||||
- expected Evidence and cleanup behavior.
|
||||
|
||||
Execution requires both `approved: true` and the exact `plan_digest`. REA
|
||||
Execution requires the exact `plan_digest`. REA
|
||||
rebuilds the plan immediately before admission. Any changed file, runtime,
|
||||
sandbox executable, policy, stub, input, limit, or environment commitment
|
||||
returns `plan_stale` before application code starts. Approval is not transferable
|
||||
@@ -321,7 +326,8 @@ Every limit has a conservative product default. The v1 plan and result commit:
|
||||
- cgroup memory, swap, task, and CPU ceilings;
|
||||
- Node/V8 heap ceiling as defense in depth;
|
||||
- private tmpfs bytes;
|
||||
- module count and aggregate bytes, case count, and input bytes;
|
||||
- serialized worker-protocol bytes, aggregate module bytes, and case-input
|
||||
bytes; preset-generated cases also have a fixed preflight count ceiling;
|
||||
- stdout, stderr, exception, stack, and aggregate retained-output bytes; and
|
||||
- structured-result depth and node count.
|
||||
|
||||
@@ -332,7 +338,7 @@ partial successful return.
|
||||
|
||||
### 7. Preserve exact provenance and the right Evidence authority
|
||||
|
||||
A run that reaches worker admission produces Evidence v2 with:
|
||||
A run that reaches worker admission produces Evidence with:
|
||||
|
||||
- provider `rea-javascript-replay` and a concrete implementation version;
|
||||
- operation `run_controlled_replay`;
|
||||
@@ -406,7 +412,7 @@ Failure at one cleanup step does not skip the remaining steps.
|
||||
The canonical replay manifest commits:
|
||||
|
||||
```text
|
||||
schema and policy versions
|
||||
plan structure and policy identity
|
||||
REA/provider/runtime/sandbox executable identities and digests
|
||||
module aliases, dependency graph, bytes digests, and source references
|
||||
stub IDs, versions, source digests, and caller-provided stub digests
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
# ADR-0003: Managed-code evidence and provider boundary
|
||||
|
||||
- Status: Accepted
|
||||
> Historical design record: The non-executing runtime-correlation planning
|
||||
> tool described in earlier revisions was removed. This ADR's current
|
||||
> implementation status below covers only the static managed tools that ship.
|
||||
|
||||
- Status: Superseded for runtime planning; static evidence boundary remains in force
|
||||
- Date: 2026-07-16
|
||||
- Implementation status: Read-only PE/CLI triage and exact identity are shipped
|
||||
through `inspect_managed_artifact` / `rea inspect-managed-artifact`. Bounded
|
||||
metadata, signatures, method bodies, raw CIL hashes, limited
|
||||
decoded-instruction-tuple v1 hashes, exception regions, call edges, and
|
||||
field-access anchors are shipped through `inspect_managed_members`; `rea
|
||||
inspect-managed-members` provides CLI parity. Static member comparison and build-local
|
||||
inspect-managed-members` provides CLI parity. Static member comparison and build-local
|
||||
token remapping are shipped through `compare_managed_members` /
|
||||
`rea compare-managed-members`. Declaration-only managed/native boundary
|
||||
inventory for ModuleRef, ImplMap/PInvoke, ReadyToRun indicators, and non-IL
|
||||
@@ -19,11 +23,9 @@ inspect-managed-members` provides CLI parity. Static member comparison and build
|
||||
addresses. Decompiler reconstruction import is
|
||||
shipped through `import_managed_reconstruction` /
|
||||
`rea import-managed-reconstruction`; it records user-supplied C#/IL/pseudocode
|
||||
as analyst inference only after exact static member locks match. Default-disabled
|
||||
runtime-correlation admission planning is shipped through
|
||||
`plan_managed_runtime_correlation` / `rea plan-managed-runtime-correlation`;
|
||||
it does not attach, load, debug, reflect, instrument, invoke, or execute
|
||||
target code. Managed static graph projection is shipped through
|
||||
as analyst inference only after exact static member locks match. Managed
|
||||
inspection does not execute assemblies or observe CLR internals. Managed static graph
|
||||
projection is shipped through
|
||||
`project_managed_application_graph` /
|
||||
`rea project-managed-application-graph`; it projects authenticated managed
|
||||
Evidence into the application graph without changing managed tokens into
|
||||
@@ -50,7 +52,7 @@ makes names especially weak while tokens remain coordinates in only one build.
|
||||
REA already separates one selected deep native provider from disjoint auxiliary
|
||||
providers under [ADR-0001](0001-provider-selection-and-analysis-profiles.md).
|
||||
It also separates static evidence, passive observation, process capture, and
|
||||
controlled replay authorities. Managed analysis must fit those boundaries
|
||||
interactive observation authorities. Managed analysis must fit those boundaries
|
||||
without requiring a CLR to inspect an artifact, loading target code into the
|
||||
REA process, or treating a managed token as a native address.
|
||||
|
||||
@@ -189,7 +191,7 @@ or a CLR load. Complete-coverage claims require every applicable bounded region
|
||||
to have been admitted; otherwise coverage is partial, unknown, or unavailable
|
||||
with exact offsets and reasons.
|
||||
|
||||
The input path remains under REA's approved investigation roots. Local artifact
|
||||
The caller selects the local input path directly. Local artifact
|
||||
paths, digests, MVIDs, tokens, malformed offsets, and analysis metadata are
|
||||
actionable diagnostics and are not secrets. Credentials, authorization data,
|
||||
and genuine secrets remain redacted.
|
||||
@@ -203,7 +205,7 @@ artifact: canonical path + byte length + SHA-256
|
||||
component: container identity + component path/name + component SHA-256, when applicable
|
||||
classification: container + runtime family + implementation form + architecture
|
||||
assembly/module: simple name + version + culture + public-key identity + module name + MVID
|
||||
producer: provider ID + exact version + parser/profile schema + profile digest
|
||||
producer: provider ID + exact version + analysis-affecting parameters + profile digest
|
||||
```
|
||||
|
||||
An assembly-qualified name does not replace the artifact digest or MVID.
|
||||
@@ -218,7 +220,7 @@ declaring type + normalized CLI signature
|
||||
build-local metadata token
|
||||
RVA/file extent when present
|
||||
raw CIL byte length + SHA-256 when the body is admitted
|
||||
member-result schema version + decoded-instruction-tuple SHA-256 when CIL is valid
|
||||
decoded-instruction-tuple SHA-256 when CIL is valid
|
||||
separate max-stack, locals-token, and exception-region observations
|
||||
```
|
||||
|
||||
@@ -232,9 +234,9 @@ The caller-visible field remains named `normalized_il_sha256`, but its shipped
|
||||
v1 meaning is deliberately narrower than a canonical semantic CIL identity.
|
||||
For a completely decoded method, REA serializes the instruction-order array of
|
||||
`[opcode, operand_kind, operand]` tuples with JavaScript `JSON.stringify`,
|
||||
encodes that text as UTF-8, and reports its lowercase SHA-256. The member
|
||||
result's `schema_version: 1` scopes this convention; there is no independent
|
||||
normalized-CIL algorithm-version field in v1.
|
||||
encodes that text as UTF-8, and reports its lowercase SHA-256. The tuple
|
||||
projection is defined here; the member result carries only the described
|
||||
normalized digest.
|
||||
|
||||
Operands retain the current decoder projection: metadata and user-string
|
||||
operands are build-local lowercase hexadecimal tokens, scalar branch operands
|
||||
@@ -257,10 +259,13 @@ exception/local commitments, and an explicit algorithm version require a new
|
||||
normalized-CIL schema.
|
||||
|
||||
Metadata tokens are reported because they are precise coordinates inside one
|
||||
module. Cross-version identity never consists of a token alone. A structural
|
||||
match cites both build commitments, both local tokens, the v1 tuple inputs, the
|
||||
member-result schema version, and all competing candidates. V1 cannot cite a
|
||||
separate matching-algorithm version because none is exposed.
|
||||
module. Cross-version identity never consists of a token alone. Matching tries
|
||||
exact CIL/signature identity, then an exact declared-type/name/signature tuple,
|
||||
then structural shape; a name by itself never selects a pair. Partial or
|
||||
malformed bodies leave body facets unknown after a signature tuple pairs the
|
||||
methods. Matches cite both build commitments, both local tokens, and all
|
||||
competing candidates. V1 cannot cite a separate matching-algorithm version
|
||||
because none is exposed.
|
||||
|
||||
### 5. Separate five evidence layers
|
||||
|
||||
@@ -356,24 +361,13 @@ emit full IL, decompiled source, runtime logs, user data, credentials, or
|
||||
service/account material. No proprietary binary, hash-locked application
|
||||
manifest, or derived dump is committed to REA.
|
||||
|
||||
### 8. Keep runtime correlation a different, default-disabled authority
|
||||
### 8. Keep runtime behavior separate from static managed analysis
|
||||
|
||||
Static managed support grants no permission to attach, load, debug, reflect,
|
||||
instrument, invoke, or execute. The shipped runtime-correlation planning
|
||||
capability distinguishes those effects, remains disabled by default, requires
|
||||
explicit administrator and per-call approval, and binds to the exact artifact
|
||||
SHA-256, MVID, method signature, and CIL/body shape observed by the static
|
||||
path. It records an admission plan only; a future executor must pass a separate
|
||||
threat model before any effect can occur.
|
||||
|
||||
Runtime admission must additionally validate host OS, CLR family, architecture,
|
||||
and supported build/tool versions; bound time, threads, outputs, UI and network
|
||||
effects; avoid real services and accounts; and own all created processes and
|
||||
artifacts through complete cleanup. Reflection-only loading is still a CLR load
|
||||
and is not a substitute for the static byte parser.
|
||||
|
||||
Runtime execution is intentionally outside the current managed-code track and
|
||||
requires its own threat model and admission decision.
|
||||
No managed runtime planning or execution tool ships. Managed inspection does
|
||||
not execute assemblies or establish which methods ran. Where supported, direct
|
||||
process capture can record target-level inputs and outputs, but it does not
|
||||
prove CLR method execution or resolution. Internal managed behavior remains
|
||||
unknown until independently observed with suitable runtime evidence.
|
||||
|
||||
## Public contract consequences
|
||||
|
||||
@@ -386,7 +380,7 @@ requires its own threat model and admission decision.
|
||||
- Managed addresses distinguish file offsets, RVAs, native virtual addresses,
|
||||
metadata tokens, and CIL offsets. They are not interchangeable.
|
||||
- Cache compatibility is exact on artifact/component commitment, member-result
|
||||
schema, parser profile, and producer version. For shipped v1,
|
||||
fields, parser profile, and producer version. For shipped v1,
|
||||
`normalized_il_sha256` has no independent algorithm-version field.
|
||||
- Provider-neutral workflows may compose canonical managed findings with the
|
||||
one native binding, but every source operation retains its actual producer.
|
||||
@@ -440,9 +434,7 @@ or development-only oracles.
|
||||
5. Add source-built, pinned real-tool, package, CLI, and MCP conformance.
|
||||
Source-owned conformance is shipped; pinned external real-tool checks remain
|
||||
planned.
|
||||
6. Add separately authorized runtime-correlation admission planning. Shipped;
|
||||
runtime execution remains planned.
|
||||
7. Project authenticated managed static Evidence into the application graph.
|
||||
6. Project authenticated managed static Evidence into the application graph.
|
||||
Shipped.
|
||||
|
||||
Every gate must keep unsupported ReadyToRun, NativeAOT, IL2CPP, mixed-mode, or
|
||||
|
||||
+5
-5
@@ -4,8 +4,8 @@ Accepted decisions describe the architecture that implementation PRs must
|
||||
follow. Acceptance does not by itself mean the behavior is shipped; each record
|
||||
states its implementation status separately.
|
||||
|
||||
| ADR | Status | Implementation |
|
||||
| ------------------------------------------------------------------------------------------------------------------ | -------- | ----------------------------------------------- |
|
||||
| [0001: Provider selection and analysis profiles](0001-provider-selection-and-analysis-profiles.md) | Accepted | Foundation shipped; capability work continues |
|
||||
| [0002: Controlled JavaScript replay authority and sandbox policy](0002-controlled-replay-authority-and-sandbox.md) | Accepted | Linux x86_64 extracted-module replay shipped |
|
||||
| [0003: Managed-code evidence and provider boundary](0003-managed-code-evidence-and-provider-boundary.md) | Accepted | PE/CLI triage shipped; deeper contracts planned |
|
||||
| ADR | Status | Implementation |
|
||||
| ------------------------------------------------------------------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [0001: Provider selection and analysis profiles](0001-provider-selection-and-analysis-profiles.md) | Accepted | Provider selection and 25 read-only Ghidra operations on Linux/macOS and experimental Windows x64 P0; Linux/macOS session annotations |
|
||||
| [0002: Controlled JavaScript replay authority and sandbox policy](0002-controlled-replay-authority-and-sandbox.md) | Superseded | Historical design; the controlled JavaScript replay tool was removed |
|
||||
| [0003: Managed-code evidence and provider boundary](0003-managed-code-evidence-and-provider-boundary.md) | Partially superseded | Seven static managed tools remain; runtime planning removed |
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Static Android APK analysis
|
||||
|
||||
REA wraps an existing headless JADX engine through `src/android/`. Its source is
|
||||
kept as an unmodified, commit-pinned Git submodule; see
|
||||
[upstream provenance](https://github.com/morluto/rea/blob/main/third_party/README.md). CLI and MCP use the same
|
||||
application workflow and return artifact-bound Evidence inline.
|
||||
|
||||
This family is present on repository main and in npm 4.1.0. Check the
|
||||
[released package boundary](installation.md#released-package-and-main) and the
|
||||
connected server's tool list before selecting it.
|
||||
|
||||
## Supply tools
|
||||
|
||||
Use a **full JDK 17 or newer** (including its compiler module) and
|
||||
**jadx-headless-mcp 0.7.1**. REA runs its packaged Java metadata bridge in
|
||||
source-file mode against the unmodified engine JAR. REA does not install Java,
|
||||
an Android SDK, JADX, a device service or an emulator. Set the path in the shell
|
||||
or MCP server environment:
|
||||
|
||||
```sh
|
||||
export REA_JADX_MCP_JAR=/absolute/path/jadx-headless-mcp-0.7.1-all.jar
|
||||
# Optional: select an existing JDK rather than java on PATH.
|
||||
export JAVA_HOME=/absolute/path/existing-jdk
|
||||
```
|
||||
|
||||
The current metadata bridge is verified on macOS arm64 with OpenJDK 21 and the
|
||||
public Appium ApiDemos fixture. The POSIX adapter also supports Linux; the new
|
||||
bridge has not yet undergone real verification on Linux/JDK 25.
|
||||
Windows is unsupported for this provider's owned stdio process boundary.
|
||||
|
||||
## Answer an analyst question
|
||||
|
||||
```sh
|
||||
rea inspect-android-package /path/Example.apk
|
||||
rea search-android-classes /path/Example.apk MainActivity
|
||||
rea inspect-android-class /path/Example.apk example.MainActivity
|
||||
rea inspect-android-method /path/Example.apk example.MainActivity onCreate
|
||||
rea inspect-android-method /path/Example.apk example.MainActivity select --overload-index 1
|
||||
rea trace-android-references /path/Example.apk example.MainActivity
|
||||
rea trace-android-references /path/Example.apk example.MainActivity --method-name onCreate
|
||||
```
|
||||
|
||||
Equivalent MCP methods are `inspect_android_package`, `search_android_classes`,
|
||||
`inspect_android_class`, `inspect_android_method` and `trace_android_references`.
|
||||
Every request includes `path`. Class/method operations use `class_name`,
|
||||
`method_name` and optional `overload_index` as applicable. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_android_method",
|
||||
"arguments": {
|
||||
"path": "/path/Example.apk",
|
||||
"class_name": "example.MainActivity",
|
||||
"method_name": "select",
|
||||
"overload_index": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `inspect_android_class` to choose an overload. REA rejects an ambiguous
|
||||
method request rather than silently selecting the first match. The engine's
|
||||
method reference operation cannot select overloads, so REA reports that boundary
|
||||
as unsupported when multiple same-name methods exist.
|
||||
|
||||
The engine's smali fallback also joins same-name overloads; REA rejects that
|
||||
fallback for an overloaded method instead of attributing all bodies to one
|
||||
selected overload. Native/abstract methods with no decompiled body report
|
||||
`body_status: "not_available"` without inferring which implementation flag caused it.
|
||||
|
||||
## 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.
|
||||
Signature verification is explicitly `not_performed`.
|
||||
- Class search consumes all pages and checks their counts. An empty `query`
|
||||
inventories all class names; class names are decompiler representations.
|
||||
- Class inventories read parsed metadata without generating source and return
|
||||
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.
|
||||
Exact DEX descriptors are unknown. Overload indices are tied to this artifact
|
||||
and engine, not stable identities across different builds.
|
||||
- Method inspection reports Java or smali, fallback markers and complete/partial
|
||||
returned text. Decompiled source is a derived representation. A complete text
|
||||
result does not establish complete semantics or a byte-identical reconstruction.
|
||||
- Reference results are incoming static relationships between provider nodes.
|
||||
Source lines and DEX instruction offsets remain unknown. They are not observed
|
||||
runtime calls.
|
||||
- Original requests, APK SHA-256, actual engine JAR SHA-256 and raw producer
|
||||
responses remain in Evidence. Source revision is populated only for bytes
|
||||
matching the audited release JAR.
|
||||
|
||||
These operations do not execute the APK or provide split APK/AAB handling,
|
||||
signature validation, full resource-table semantics, native-library analysis or
|
||||
Android runtime capture. Existing artifact inventory/extraction tools can supply
|
||||
archive evidence. Project that Evidence with the execution-free inventory tool:
|
||||
|
||||
```sh
|
||||
rea project-android-application-graph '{"inventory_evidence":[<inventory_artifact Evidence>]}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "project_android_application_graph",
|
||||
"arguments": {
|
||||
"inventory_evidence": ["<inventory_artifact Evidence>"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The projection reports exact component paths and hashes, runtime-family hints,
|
||||
and path-based bridge hypotheses. It does not decode DEX or claim observed
|
||||
runtime calls.
|
||||
|
||||
## Resource and lifecycle limits
|
||||
|
||||
REA retains one serialized session with private, immutable APK, engine JAR and
|
||||
metadata-bridge copies. Matching requests reuse the loaded engine. APK/engine
|
||||
digests and JVM configuration are checked before reuse; changed inputs retire
|
||||
the previous session. Sessions are cleaned after 60 seconds of inactivity, on
|
||||
target/configuration changes, failure, timeout, cancellation, MCP disconnect or
|
||||
server shutdown. One-shot CLI commands always join cleanup before returning.
|
||||
REA never writes to the original APK or launches target code.
|
||||
|
||||
If process cleanup cannot be confirmed, REA retains the workspace and reports its
|
||||
location. That provider instance blocks subsequent engine launches until the
|
||||
caller resolves the reported resources and starts a fresh instance.
|
||||
|
||||
The provider uses one JADX analysis worker independently of JVM processor and
|
||||
garbage-collector settings. REA supplies no default heap or visible-CPU override;
|
||||
Java uses its normal ergonomics and inherits caller JVM settings. Optional
|
||||
provider settings add explicit JVM arguments:
|
||||
|
||||
```sh
|
||||
export REA_JADX_HEAP_MIB=8192
|
||||
# Optional, independent of the single JADX worker:
|
||||
export REA_JADX_ACTIVE_PROCESSOR_COUNT=4
|
||||
# Or select settings through Java's standard environment options:
|
||||
export JAVA_TOOL_OPTIONS=-Xmx8g
|
||||
```
|
||||
|
||||
Both REA settings must be positive safe integers; processor counts must also fit
|
||||
the JVM's signed 32-bit range. REA preserves
|
||||
`JAVA_TOOL_OPTIONS`, `JDK_JAVA_OPTIONS` and `_JAVA_OPTIONS` rather than replacing
|
||||
them. Java's standard precedence applies: explicit command-line heap settings
|
||||
override `JAVA_TOOL_OPTIONS`, while `_JAVA_OPTIONS` can override command-line
|
||||
settings. Evidence reports the worker's observed `Runtime.maxMemory()` rounded
|
||||
down to MiB and retains exact bytes, Java version and visible processors in its
|
||||
raw runtime response. The observed allocatable heap can differ from configured
|
||||
`-Xmx`, especially with Serial GC. Heap is not a total RSS cap: the JVM also
|
||||
uses native memory. Independent REA instances have independent queues. Whole
|
||||
operations have a **120-second** execution deadline after reaching the queue's
|
||||
front; upstream decompilation is limited to **90 seconds**.
|
||||
|
||||
Decoded manifest and method text have a **1 MiB** upstream byte budget and report
|
||||
truncation. Class-name pages use indexed slices of up to 4,096 names without changing the
|
||||
complete, case-sensitive search contract. A protocol frame above **8 MiB**, or combined stdout/stderr above
|
||||
**32 MiB per operation**, fails with a diagnostic instead of returning an incomplete inventory
|
||||
as complete. These are memory-safety boundaries, not undocumented result caps.
|
||||
|
||||
## Real verification
|
||||
|
||||
Download the explicit public fixtures once; this installs no tools:
|
||||
|
||||
```sh
|
||||
npm run fixtures:android
|
||||
export REA_JADX_MCP_JAR="$PWD/_reference/apk-integration/jadx-headless-mcp-0.7.1-all.jar"
|
||||
export REA_ANDROID_TEST_APK="$PWD/_reference/apk-integration/ApiDemos-debug.apk"
|
||||
npm run verify:android
|
||||
```
|
||||
|
||||
The download script verifies fixed SHA-256 values, reuses matching files and
|
||||
refuses to overwrite a different existing file. APK/JAR files live under ignored
|
||||
`_reference/` and are excluded from package contents. The lane exercises actual
|
||||
REA CLI and MCP results with the real engine, separately from synthetic protocol
|
||||
regressions. It needs no Gradle build, emulator, Android SDK or Ghidra.
|
||||
@@ -0,0 +1,112 @@
|
||||
# Apple Application Inventory Projection
|
||||
|
||||
REA can project one or more authenticated `inventory_artifact` Evidence records
|
||||
for the same IPA, macOS `.app` directory, ZIP, or DMG into an Apple application
|
||||
inventory. The projection is execution-free: it reports exact paths, content
|
||||
hashes, detected formats, bundle anatomy, runtime-family hints, and path-based
|
||||
bridge hypotheses. It does not parse plist or CMS semantics or claim observed
|
||||
runtime calls.
|
||||
|
||||
```sh
|
||||
rea project-apple-application-graph '{"inventory_evidence":[<inventory_artifact Evidence>]}'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "project_apple_application_graph",
|
||||
"arguments": {
|
||||
"inventory_evidence": ["<inventory_artifact Evidence>"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The result includes every component in each inventory category and every
|
||||
JavaScript-to-native bridge candidate pair. It has no caller-selected component
|
||||
budget, prefix truncation, or omitted-count fields. Duplicate inventory pages
|
||||
are merged by artifact identity and occurrence path; their Evidence IDs remain
|
||||
attached as source Evidence.
|
||||
|
||||
Coverage is `complete-within-inventory` when the supplied pages reconstruct the
|
||||
complete authenticated inventory, and `partial` otherwise. A partial result
|
||||
states that absence is unknown. Component and bridge-candidate arrays are still
|
||||
the complete projection of the inventory that was supplied.
|
||||
|
||||
## Application roots
|
||||
|
||||
| Inventory root | Application root |
|
||||
| -------------------------------------- | --------------------------------------------------- |
|
||||
| IPA | Each `Payload/<Name>.app` |
|
||||
| `.app` directory | `.`, the inventoried bundle itself |
|
||||
| Directory, ZIP, or DMG containing apps | Each outermost `<Name>.app` followed by `Contents/` |
|
||||
|
||||
ZIP archives made with `ditto -c -k --keepParent` report `<Name>.app`. A DMG
|
||||
reports `<image>.dmg/<Volume>/<Name>.app`; DMG child inventory needs the native
|
||||
macOS mounting adapter. When a DMG has only its root identity, the projection is
|
||||
`partial` and says that bundle absence is unknown. Other inventory formats, such
|
||||
as APK or ASAR, are rejected as input errors.
|
||||
|
||||
An inventoried directory whose name ends in `.app` is its own root even when its
|
||||
inventory is partial or lacks `Contents/`. For directory, ZIP, and DMG
|
||||
inventories, components outside every application root are not attributed to
|
||||
the application. This covers installers, disk-image extras, and AppleDouble
|
||||
sidecars, which therefore stay out of runtime families and bridge candidates. A
|
||||
limitation reports how many inventoried entries were left out. IPA projection
|
||||
still lists every archive component, including `SwiftSupport/`.
|
||||
|
||||
`platforms` lists `macos` for application roots with `Contents/`. It lists `ios`
|
||||
for shallow application roots that have an `Info.plist`. An application root
|
||||
with neither, such as an empty inventory, has no platform.
|
||||
|
||||
## macOS bundle anatomy
|
||||
|
||||
`bundles` lists each application root and every nested bundle under it. Each
|
||||
bundle reports its `parent_path`, `layout`, `info_plist_path`,
|
||||
`executable_candidates`, and `signing_paths`.
|
||||
|
||||
| Layout | Meaning | Content directory |
|
||||
| --------------------- | --------------- | --------------------------------------------- |
|
||||
| `macos-deep` | Has `Contents/` | `Contents/`, executables in `Contents/MacOS/` |
|
||||
| `versioned-framework` | Has `Versions/` | Each real `Versions/<version>/` |
|
||||
| `shallow` | Neither | The bundle directory itself |
|
||||
|
||||
Roles are path conventions (`role_basis: "path-convention"`):
|
||||
|
||||
| Role | Path |
|
||||
| -------------------- | --------------------------------------------------------------- |
|
||||
| `app-extension` | `*.appex` |
|
||||
| `xpc-service` | `*.xpc`, including XPC services inside frameworks |
|
||||
| `framework` | `*.framework` |
|
||||
| `system-extension` | `*.systemextension` |
|
||||
| `driver-extension` | `*.dext` |
|
||||
| `plug-in` | `PlugIns/*.bundle`, `*.plugin`, `*.qlgenerator`, `*.mdimporter` |
|
||||
| `resource-bundle` | `Resources/*.bundle`, such as SwiftPM resource bundles |
|
||||
| `bundle` | Any other `*.bundle` |
|
||||
| `login-item` | `Contents/Library/LoginItems/*.app` |
|
||||
| `helper-application` | `Contents/Helpers/*.app` |
|
||||
| `nested-application` | Any other nested `*.app` |
|
||||
|
||||
The components also list:
|
||||
|
||||
- `privileged_helpers`: files directly in `Contents/Library/LaunchServices/`,
|
||||
where SMJobBless helpers live;
|
||||
- `launchd_plists`: `.plist` files directly in `Contents/Library/LaunchAgents/`
|
||||
(`domain: "agent"`) or `Contents/Library/LaunchDaemons/` (`domain: "daemon"`),
|
||||
as used by SMAppService;
|
||||
- `helpers`: files directly in `Contents/Helpers/`.
|
||||
|
||||
Symlinks are reported by path in `symlinks`. Their targets are not inventoried,
|
||||
so a versioned framework is described through its real `Versions/<version>/`
|
||||
directories rather than `Versions/Current`. A framework's `info_plist_path` is
|
||||
`null`, with a limitation explaining why, when it has more than one real
|
||||
`Versions/<version>/` directory (even if only one of them holds a plist,
|
||||
because `Versions/Current` decides which one applies) or when its single
|
||||
version directory has no `Resources/Info.plist`.
|
||||
|
||||
AppleDouble sidecar files (`._*` and `__MACOSX/`) are inventory facts, but they
|
||||
describe neighbouring files. They are excluded from roots, bundle roles, and the
|
||||
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.
|
||||
Use `inspect_signature` on an executable candidate for code-signing claims.
|
||||
+69
-16
@@ -1,89 +1,142 @@
|
||||
graph TD
|
||||
subgraph "Entry Points"
|
||||
CLI["src/cli.ts<br/>One-shot CLI adapter<br/>setup, diagnostics, analysis, decompilation"]
|
||||
MAIN["src/main.ts<br/>MCP stdio server adapter<br/>config, session, process lifetime"]
|
||||
CLI["src/cli.ts + src/cli/<br/>One-shot CLI adapter<br/>setup, diagnostics, analysis, decompilation"]
|
||||
MAIN["src/main.ts + src/main/<br/>MCP stdio server adapter<br/>config, session, process lifetime"]
|
||||
DISPATCH["scripts/rea.mjs<br/>Executable dispatcher<br/>routes mcp / one-shot"]
|
||||
end
|
||||
|
||||
subgraph "Production Composition"
|
||||
COMPOSITION["src/composition/<br/>fresh typed provider and session factories<br/>one-shot binding and independent optional loading"]
|
||||
end
|
||||
|
||||
subgraph "Adapters"
|
||||
SERVER["src/server/<br/>MCP request translation<br/>createServer, tool registration, toolResult"]
|
||||
HOPPER_BRIDGE["bridge/hopper_bridge.py<br/>Runs inside Hopper<br/>adapts operations to Hopper Python API"]
|
||||
GHIDRA_BRIDGE["bridge/ghidra/ReaGhidraBridge.java<br/>Packaged HeadlessScript<br/>inventory, decompiler, references, CFG"]
|
||||
INSTALL["Host installation adapters<br/>macOS: verified vendor DMG to ~/Applications<br/>Linux: verified vendor package + apt/dnf/pacman"]
|
||||
INSTALL["src/cli/ + src/application/Setup*<br/>Host installation adapters<br/>macOS: verified vendor DMG to ~/Applications<br/>Linux: verified vendor package + apt/dnf/pacman"]
|
||||
end
|
||||
|
||||
subgraph "Application Layer"
|
||||
APP["src/application/<br/>Shared CLI/MCP workflows<br/>enhanced analysis and investigations"]
|
||||
SESSION["SessionProviderRouter + BinarySession<br/>one immutable deep binding per target<br/>disjoint auxiliary routing"]
|
||||
REGISTRY["AnalysisProviderRegistry<br/>sorted deep candidates<br/>deterministic selection, no fallback"]
|
||||
WORKSPACE["Investigation workspace store<br/>approved roots, canonical JSON<br/>atomic CAS revisions and locks"]
|
||||
JS_RECON["JavaScript artifact reconstruction<br/>bounded directory/ASAR reads<br/>AST-only bundle projection"]
|
||||
RECORDS["src/application/investigation/<br/>composed InvestigationRecords + narrow ports<br/>EvidenceLedger and Unknown ownership"]
|
||||
SNAPSHOT["BinarySessionRecords + AnalysisSnapshotCache<br/>binary target/profile snapshots<br/>compatibility facade and post-commit notifications"]
|
||||
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"
|
||||
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"]
|
||||
IDA_MCP["External ida-pro-mcp<br/>GUI proxy or database supervisor<br/>upstream installation and engine lifecycle"]
|
||||
AUX["src/artifacts/ + src/native/<br/>disjoint artifact and native capabilities"]
|
||||
BROWSER["src/browser/<br/>Loopback CDP transport<br/>exact-origin passive observation"]
|
||||
REPLAY["src/replay/<br/>Disposable JavaScript worker<br/>Bubblewrap, seccomp, cgroup limits"]
|
||||
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"]
|
||||
DOTNET["src/dotnet/<br/>Managed static metadata provider<br/>PE/CLI metadata inventory, members, IL"]
|
||||
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"]
|
||||
end
|
||||
|
||||
subgraph "Provider Process Foundation"
|
||||
PROCESS["src/process/<br/>owned process groups, private roots<br/>deadlines, bounded diagnostics, cleanup"]
|
||||
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"]
|
||||
end
|
||||
|
||||
subgraph "Contracts Layer"
|
||||
CONTRACTS["src/contracts/<br/>Caller-visible schemas for analysis, managed, browser, Electron, application, and session tools"]
|
||||
ERRORS["src/contracts/errorSchemas.ts<br/>Shared analysis error schemas<br/>CLI diagnostics, generated error catalog"]
|
||||
end
|
||||
|
||||
subgraph "Domain Layer"
|
||||
DOMAIN["src/domain/<br/>Pure side-effect-free modules<br/>evidence, comparisons, application graphs, workspace commitments"]
|
||||
DOMAIN["src/domain/<br/>Pure side-effect-free modules<br/>evidence, comparisons, application graphs"]
|
||||
end
|
||||
|
||||
subgraph "Configuration"
|
||||
CONFIG["src/config.ts<br/>Zod-validated env parsing<br/>provider settings, origins, and approved roots"]
|
||||
CONFIG["src/config.ts + src/config/<br/>Zod-validated env parsing<br/>provider settings and origins"]
|
||||
end
|
||||
|
||||
subgraph "Tests"
|
||||
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"]
|
||||
end
|
||||
|
||||
DISPATCH --> CLI
|
||||
DISPATCH --> MAIN
|
||||
MAIN --> CONFIG
|
||||
MAIN --> SERVER
|
||||
MAIN --> APP
|
||||
CLI --> APP
|
||||
MAIN --> COMPOSITION
|
||||
CLI --> COMPOSITION
|
||||
SERVER --> COMPOSITION
|
||||
COMPOSITION --> SESSION
|
||||
APP --> FIRMWARE
|
||||
FIRMWARE --> PROCESS
|
||||
FIRMWARE --> CONTRACTS
|
||||
APP --> ANDROID
|
||||
ANDROID --> PROCESS
|
||||
ANDROID --> CONTRACTS
|
||||
ANDROID --> DOMAIN
|
||||
CLI --> ERRORS
|
||||
APP --> SESSION
|
||||
SESSION --> REGISTRY
|
||||
REGISTRY --> HOPPER
|
||||
REGISTRY --> GHIDRA
|
||||
SESSION --> SNAPSHOT
|
||||
SNAPSHOT --> RECORDS
|
||||
APP -->|record callbacks| RECORDS
|
||||
SERVER -->|narrow record ports| RECORDS
|
||||
RECORDS --> DOMAIN
|
||||
COMPOSITION --> HOPPER
|
||||
COMPOSITION --> GHIDRA
|
||||
COMPOSITION --> IDA
|
||||
COMPOSITION --> AUX
|
||||
IDA --> IDA_MCP
|
||||
IDA --> CONTRACTS
|
||||
IDA --> DOMAIN
|
||||
SESSION --> AUX
|
||||
APP --> WORKSPACE
|
||||
APP --> JS_RECON
|
||||
APP --> INSTALL
|
||||
SERVER --> CONTRACTS
|
||||
SERVER --> APP
|
||||
APP --> BROWSER
|
||||
APP --> REPLAY
|
||||
APP -->|source-map codec port| JS_CODEC
|
||||
JS_CODEC --> PROCESS
|
||||
JS_CODEC --> DOMAIN
|
||||
APP -->|runtime observation port| INSPECTOR
|
||||
INSPECTOR -->|shared CDP and file-location helpers| BROWSER
|
||||
INSPECTOR --> DOMAIN
|
||||
APP --> DOTNET
|
||||
DOTNET --> DOMAIN
|
||||
HOPPER --> HOPPER_BRIDGE
|
||||
GHIDRA --> GHIDRA_BRIDGE
|
||||
HOPPER --> PROCESS
|
||||
GHIDRA --> PROCESS
|
||||
GHIDRA --> WINDOWS
|
||||
PROCESS --> WINDOWS
|
||||
WINDOWS --> WINDOWS_NATIVE
|
||||
APP --> PROCESS
|
||||
APP --> DOMAIN
|
||||
JS_RECON --> AUX
|
||||
JS_RECON --> DOMAIN
|
||||
APP --> REFERENCE
|
||||
REFERENCE --> DOMAIN
|
||||
CONTRACTS --> DOMAIN
|
||||
HOPPER --> DOMAIN
|
||||
GHIDRA --> DOMAIN
|
||||
AUX --> DOMAIN
|
||||
BROWSER --> DOMAIN
|
||||
REPLAY --> DOMAIN
|
||||
PROCESS --> DOMAIN
|
||||
TESTS -.-> MAIN
|
||||
TESTS -.-> HOPPER
|
||||
TESTS -.-> GHIDRA
|
||||
TESTS -.-> IDA
|
||||
TESTS -.-> PROCESS
|
||||
TESTS -.-> BROWSER
|
||||
TESTS -.-> REPLAY
|
||||
TESTS -.-> EVALUATION
|
||||
TESTS -.-> ERRORS
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Discord</title>
|
||||
<path fill="#5865F2" d="M20.317 4.3698a19.7913 19.7913 0 0 0-4.8851-1.5152.0741.0741 0 0 0-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 0 0-.0785-.037 19.7363 19.7363 0 0 0-4.8852 1.515.0699.0699 0 0 0-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 0 0 .0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 0 0 .0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 0 0-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 0 1-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 0 1 .0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 0 1 .0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 0 1-.0066.1276 12.2986 12.2986 0 0 1-1.873.8914.0766.0766 0 0 0-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 0 0 .0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 0 0 .0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 0 0-.0312-.0286ZM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189Zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
+85
-82
@@ -1,21 +1,40 @@
|
||||
# Website observation with CDP
|
||||
|
||||
REA can attach to a user-owned Chrome-family browser through the Chrome DevTools Protocol (CDP) and produce bounded Evidence v2 about an existing page. This is a passive reverse-engineering capability, not a general browser automation or remote-control surface.
|
||||
Captured Debugger sources can be exported for REA's existing static JavaScript
|
||||
analysis with `export_web_scripts`. See [captured website scripts](website-script-export.md)
|
||||
for the capture options, source mappings, and browser/local resolution limits.
|
||||
Use [captured module relationships](website-module-trace.md) to resolve one
|
||||
exported script's native imports under an explicit URL/import-map context.
|
||||
|
||||
REA can attach to a user-owned Chrome-family browser through the Chrome DevTools Protocol (CDP) and produce bounded Evidence about an existing page. This is a passive reverse-engineering capability, not a general browser automation or remote-control surface.
|
||||
|
||||
## Shipped surfaces
|
||||
|
||||
- `list_browser_targets` / `rea list-browser-targets` discovers page targets whose current URL matches an approved exact origin.
|
||||
- `inspect_web_page` / `rea inspect-web-page` captures bounded DOM structure, accessibility nodes, scripts, resources, safe response/DOM metadata, attach-window network and console metadata, WebSocket frame sizes, workers, quota, and optionally storage key names, redacted storage-content fingerprints, or script sources.
|
||||
- `analyze_web_bundle` / `rea analyze-web-bundle` parses approved script artifacts without execution and derives chunk edges, route and endpoint candidates, vendor fingerprints, static WebMCP declarations, and optionally approved source-map/original-source evidence.
|
||||
- `observe_web_session` / `rea observe-web-session` arms a bounded window 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 inventory page registrations. REA never exposes `WebMCP.invokeTool`.
|
||||
- `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 structural input-schema summary inline. 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. REA never exposes `WebMCP.invokeTool`; malformed protocol payloads fail explicitly instead of returning incomplete tool metadata.
|
||||
- `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.
|
||||
- `capture_web_screenshot` / `rea capture-web-screenshot` returns an explicitly approved, bounded, content-addressed visible-viewport PNG.
|
||||
- `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.
|
||||
- Every surface has equivalent CLI and MCP contracts and returns Evidence v2 provenance.
|
||||
- MCP session results are retained as `rea://evidence/{evidenceId}` resources. One-shot CLI output is not retained by a long-lived REA session.
|
||||
- Every surface has equivalent CLI and MCP contracts and returns Evidence provenance.
|
||||
- MCP tools return complete results inline and include the Evidence ID. Session evidence can be exported with `export_evidence_bundle`.
|
||||
|
||||
Electron `file://` pages use a separate provider, permission capability, and root model; see [electron-observation.md](electron-observation.md).
|
||||
Target discovery returns the complete in-scope target array in one result.
|
||||
|
||||
Electron `file://` pages use a separate provider and target boundary; see [electron-observation.md](electron-observation.md).
|
||||
|
||||
## Provider authorities
|
||||
|
||||
CDP page capture, V8 Inspector observation, and Playwright scenarios stay
|
||||
separate providers because their authorities differ: CDP and V8 attach to an
|
||||
already-running target and never launch, drive, evaluate, or mutate it, while
|
||||
Playwright providers own and drive the runtime (launch browsers, run actions,
|
||||
capture step snapshots). Merging them would mix attach-only and owned-process
|
||||
lifecycles in one contract. What they share instead is one exclusion
|
||||
vocabulary (`BrowserExclusionReason`): every denial reason in every stack maps
|
||||
onto it, even where a wire schema keeps a narrower historical bucket.
|
||||
Existing static application Evidence and passive web/Electron captures can be
|
||||
combined later through
|
||||
[JavaScript static/runtime reconciliation](javascript-runtime-reconciliation.md).
|
||||
@@ -33,40 +52,24 @@ google-chrome \
|
||||
|
||||
REA does not launch, own, or terminate this browser. Use a dedicated profile and stop it yourself when the investigation is complete. Do not expose the debugging port on a non-loopback interface.
|
||||
|
||||
## Configure authority
|
||||
## Request boundary
|
||||
|
||||
Browser observation is fail-closed by default. Configure the administrator ceiling before starting REA:
|
||||
|
||||
```bash
|
||||
export REA_BROWSER_OBSERVE_ENABLED=true
|
||||
export REA_BROWSER_CDP_ENDPOINTS_JSON='["http://127.0.0.1:9222"]'
|
||||
export REA_BROWSER_ALLOWED_ORIGINS_JSON='["http://127.0.0.1:3000"]'
|
||||
```
|
||||
|
||||
`REA_BROWSER_CDP_ENDPOINTS_JSON` accepts at most 16 literal `http://127.0.0.1:PORT` or `http://[::1]:PORT` endpoints. `localhost`, private-LAN addresses, HTTPS endpoints, credentials, paths, queries, fragments, and implicit ports are rejected.
|
||||
|
||||
`REA_BROWSER_ALLOWED_ORIGINS_JSON` accepts at most 32 exact HTTP(S) origins. Paths, credentials, queries, fragments, and wildcards are rejected. An input must stay within both this administrator ceiling and any active project/session/once grant.
|
||||
|
||||
Configuration is reloadable through the existing `SIGHUP` permission-policy path. A request still requires `approved: true`; configuration alone does not make a tool call implicit.
|
||||
|
||||
Inspect the exact effective scope without contacting the browser:
|
||||
|
||||
```bash
|
||||
rea policy explain browser_observe \
|
||||
--origins http://127.0.0.1:9222 \
|
||||
--origins http://127.0.0.1:3000 \
|
||||
--network loopback \
|
||||
--json
|
||||
```
|
||||
Discovery supplies a literal loopback CDP endpoint and returns all eligible
|
||||
page targets unless an exact HTTP(S) origin filter narrows the result. Follow-up
|
||||
inspection requests select one target; their default scope is its current origin. `localhost`, private-LAN
|
||||
addresses, HTTPS CDP endpoints, credentials, paths, queries, fragments, and
|
||||
implicit ports are not valid CDP endpoints. Origin filters contain only scheme,
|
||||
host, and port; paths, credentials, queries, fragments, and wildcards are not
|
||||
valid origins.
|
||||
|
||||
## CLI workflow
|
||||
|
||||
Configured origins are used by default. `--allowed-origins` can request a narrower set that still fits the configured ceiling.
|
||||
Pass the endpoint and selected target for each call. An origin filter is
|
||||
optional and narrows the target set.
|
||||
|
||||
```bash
|
||||
rea list-browser-targets http://127.0.0.1:9222 --approved --json
|
||||
rea list-browser-targets http://127.0.0.1:9222 --json
|
||||
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
|
||||
--approved \
|
||||
--observation-ms 1000 \
|
||||
--json
|
||||
```
|
||||
@@ -75,34 +78,33 @@ Static bundle analysis and a user-driven observation window are separate operati
|
||||
|
||||
```bash
|
||||
rea analyze-web-bundle http://127.0.0.1:9222 TARGET_ID \
|
||||
--approved --source-capture-approved --json
|
||||
--json
|
||||
rea observe-web-session http://127.0.0.1:9222 TARGET_ID \
|
||||
--approved --observation-ms 10000 --json
|
||||
--observation-ms 10000 --json
|
||||
rea discover-webmcp-tools http://127.0.0.1:9222 TARGET_ID \
|
||||
--approved --json
|
||||
--json
|
||||
```
|
||||
|
||||
Accessibility names/descriptions, console primitive text, JSON body shapes, WebSocket shapes, script content, and storage key names require separate opt-ins. Console/body/WebSocket capture additionally requires its matching approval flag:
|
||||
Accessibility names/descriptions, console primitive text, JSON body shapes, WebSocket shapes, script content, and storage key names are selected independently:
|
||||
|
||||
```bash
|
||||
rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
|
||||
--approved \
|
||||
--include-accessibility-text \
|
||||
--include-console-text --console-text-approved \
|
||||
--include-json-body-shapes --json-body-schema-approved \
|
||||
--include-websocket-shapes --websocket-shape-approved \
|
||||
--include-console-text \
|
||||
--include-json-body-shapes \
|
||||
--include-websocket-shapes \
|
||||
--include-script-sources \
|
||||
--include-storage-keys --include-storage-fingerprints \
|
||||
--json
|
||||
```
|
||||
|
||||
Accessibility text and script content may contain application secrets and become part of the returned Evidence. Accessibility and console text have independent per-field and aggregate UTF-8 byte limits; credential-shaped console substrings are redacted. JSON and WebSocket captures retain paths, types, counts, and truncation only, never values or examples. Storage values remain redacted even when key-name capture is approved. `--include-storage-fingerprints` separately approves SHA-256 identity/value fingerprints, which support comparison only when cookies, DOM storage, IndexedDB, and Cache Storage were all captured completely.
|
||||
Accessibility text, script content, query values, and explicitly requested primitive console text may contain application secrets and become part of the returned Evidence. URL username/password credentials are removed; ordinary query values, fragments, and parameter order remain intact. JSON and WebSocket captures retain complete paths, types, and counts, never values or examples. Storage values remain redacted even when key-name capture is requested. `--include-storage-fingerprints` requests SHA-256 identity/value fingerprints, which support comparison only when cookies, DOM storage, IndexedDB, and Cache Storage were all captured completely.
|
||||
|
||||
Screenshot pixels require both `--approved` and `--screenshot-approved`:
|
||||
Screenshot capture returns the complete visible viewport as an inline image artifact:
|
||||
|
||||
```bash
|
||||
rea capture-web-screenshot http://127.0.0.1:9222 TARGET_ID \
|
||||
--approved --screenshot-approved --json
|
||||
--json
|
||||
```
|
||||
|
||||
## MCP input
|
||||
@@ -110,58 +112,59 @@ rea capture-web-screenshot http://127.0.0.1:9222 TARGET_ID \
|
||||
```json
|
||||
{
|
||||
"cdp_endpoint": "http://127.0.0.1:9222",
|
||||
"allowed_origins": ["http://127.0.0.1:3000"],
|
||||
"target_id": "TARGET_ID_FROM_LIST_BROWSER_TARGETS",
|
||||
"approved": true,
|
||||
"observation_ms": 500,
|
||||
"include_accessibility_text": false,
|
||||
"include_console_text": false,
|
||||
"console_text_approved": false,
|
||||
"include_json_body_shapes": false,
|
||||
"json_body_schema_approved": false,
|
||||
"include_websocket_shapes": false,
|
||||
"websocket_shape_approved": false,
|
||||
"include_script_sources": false,
|
||||
"include_storage_keys": false,
|
||||
"include_storage_fingerprints": false
|
||||
"target_id": "TARGET_ID_FROM_LIST_BROWSER_TARGETS"
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
The omitted observation window and capture options use conservative defaults.
|
||||
Set an option only when the investigation needs that additional content.
|
||||
Collection counts are not capped. Per-value validation, the selected observation
|
||||
window, and CDP protocol limits still apply; any incomplete or truncated values
|
||||
are reported in the result.
|
||||
|
||||
REA establishes an authorized main-frame 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`.
|
||||
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`.
|
||||
|
||||
## Data minimization
|
||||
|
||||
REA removes sensitive values before normalized event data is retained:
|
||||
REA retains local observation data and removes only URL userinfo credentials, explicitly declared scenario secrets, and values from structured credential/storage fields:
|
||||
|
||||
- URLs retain a bounded path and at most 256 bounded query parameter names, replace every retained query value with `[REDACTED]`, and remove credentials and fragments.
|
||||
- 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, but names and descriptions require `include_accessibility_text: true` and remain byte-bounded.
|
||||
- Network observations retain method, status, MIME type, size, type, initiator stack location, and redacted URLs. 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. With independent approval, only allowed-origin JSON media types are read within per-body and aggregate budgets, converted immediately to value-free property paths/types, and discarded. `Network.getResponseBody` is never sent without that approval.
|
||||
- Console observations with an approved stack source retain call type, argument types, timestamp, and redacted source location. With independent approval, only already-delivered primitive values are retained after credential redaction and byte bounds; objects, getters, and remote properties are never expanded.
|
||||
- WebSocket observations retain direction, opcode, and payload byte length. With independent approval, bounded text frames are classified as text or value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
|
||||
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` approval. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
|
||||
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Source content is omitted unless explicitly requested and becomes a self-verifying `rea://web-content/sha256/...` artifact subject to per-script and aggregate byte limits.
|
||||
- Accessibility structure and roles are retained by default, while names and descriptions require `include_accessibility_text: true`.
|
||||
- Network observations retain method, status, MIME type, size, type, initiator stack location, and complete URLs with userinfo credentials removed. 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 property paths/types, and discarded. `Network.getResponseBody` is never sent unless JSON body shape capture is selected.
|
||||
- Console observations with a stack source retain call type, argument types, timestamp, and source location. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
|
||||
- WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
|
||||
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
|
||||
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.
|
||||
|
||||
Cross-origin frames, resources, scripts, events, and workers are excluded unless their exact origins are also approved. Excluded target details are counted without being exposed.
|
||||
Retained workers include bounded opener-target and parent-frame IDs when CDP
|
||||
Frames, resources, scripts, events, and workers outside the selected origin scope are excluded. An explicit `allowed_origins` list can include additional origins; excluded target details are counted without being exposed.
|
||||
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.
|
||||
|
||||
## Completeness and limits
|
||||
|
||||
The default observation window is 500 ms and the maximum is 10 seconds. Frames (200), DOM nodes (2,000), accessibility nodes (2,000), scripts (200), resources (2,000), workers (500), and each storage key/name inventory (1,000) have conservative defaults and caller-visible hard bounds. Script source bytes, network events, console events, and WebSocket events are bounded separately. The CLI exposes the same limits as kebab-case options such as `--max-frames` and `--max-storage-keys`.
|
||||
The default inspection window is 500 ms; callers choose its duration. The
|
||||
capture returns all in-scope frames, DOM and accessibility nodes, scripts,
|
||||
resources, workers, storage names, and observed network, console, and WebSocket
|
||||
events. Full selected text, script sources, and JSON shapes are returned inline.
|
||||
The result distinguishes
|
||||
`complete_within_window`, `policy_filtered`, `attach_limited`, and `truncated`
|
||||
coverage. It reports excluded items and any values omitted or truncated by
|
||||
validation; it does not drop records to meet an internal collection quota.
|
||||
Disallowed-origin entries are filtered before retention.
|
||||
|
||||
Every result distinguishes `complete_within_window`, `policy_filtered`, `attach_limited`, and `truncated` coverage. It names affected sections and reports sparse excluded-item and per-event-type dropped counts rather than implying exhaustive capture. Disallowed-origin entries are filtered before collection-limit accounting, so they cannot consume an approved origin's output budget.
|
||||
CDP discovery, WebSocket connection, and commands continue until the peer responds or the caller cancels the operation; REA has no internal byte, message-count, or pending-command quota for these exchanges. Without caller cancellation, a stalled peer can leave an operation waiting. On cancellation, REA closes only its own WebSocket, waiting up to one second before terminating it. Malformed unsolicited protocol events poison the connection so later commands fail closed.
|
||||
|
||||
CDP discovery, WebSocket connection, and each command have a 5-second timeout. Version discovery is capped at 64 KiB, target discovery at 2 MiB and 1,000 targets, each WebSocket message at 16 MiB, and correlated pending commands at 128. Malformed unsolicited protocol events poison the connection so later commands fail closed.
|
||||
|
||||
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. Source maps are not fetched by inspection. Bundle analysis can fetch them only after separate approval, with exact-origin redirect checks, no credentials/cookies/referrer, per-map and aggregate byte limits, a total map-count limit, v3 validation, and bounded generated/original mappings. Source-map results report requested, processed, and dropped counts rather than hiding budget truncation.
|
||||
Network and console coverage starts only after REA attaches and enables the relevant CDP domains. `prior_activity_available` is always `false`; absence from these arrays is not evidence that an event never occurred. 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. Every requested map, mapping, original source, AST node, and finding from parsed content is returned inline; failed maps remain explicitly unavailable and make bundle completeness partial. Original source artifacts with `.ts`, `.tsx`, `.mts`, or `.cts` suffixes carry `text/typescript`; other source names retain `text/javascript`.
|
||||
|
||||
## 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 requires separate literal approval. REA disables the domains it enabled, detaches the target session, and closes only its own WebSocket.
|
||||
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.
|
||||
|
||||
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, approve only origins you intend to inspect, and treat other same-user processes as outside this boundary.
|
||||
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.
|
||||
|
||||
Response metadata normalizes a `Referrer-Policy` fallback list to its last recognized nonempty policy, including mixed-case tokens. Unknown policy names are skipped; malformed token syntax and lists without a recognized policy remain `null`. This reports the response's declared policy, not the effective policy of a document or an inferred browser default.
|
||||
|
||||
@@ -1,59 +1,275 @@
|
||||
# Browser scenario contract
|
||||
|
||||
`capture_browser_scenario` accepts the versioned provider-neutral
|
||||
`capture_browser_scenario` accepts the provider-neutral
|
||||
`browserScenarioSchema` through both MCP and the
|
||||
`capture-browser-scenario INPUT_JSON` CLI command. Version 1 is declarative and
|
||||
bounded: it admits a fixed action vocabulary, exact HTTP(S) origins,
|
||||
deterministic browser settings, explicit storage and request replay, and finite
|
||||
capture limits. `INPUT_JSON` may be inline JSON or a JSON file path.
|
||||
`capture-browser-scenario INPUT_JSON` CLI command. The request is declarative:
|
||||
it admits a fixed action vocabulary, explicit HTTP(S) targets, deterministic
|
||||
browser settings, explicit storage seeds, and provider-owned
|
||||
liveness deadlines. `INPUT_JSON` may be inline JSON or a JSON file path.
|
||||
|
||||
The minimal request contains only browser launch/connect selection, `start_url`,
|
||||
and at least one `actions` entry. Launch `headless` defaults to `true` and can
|
||||
be set to `false` where the host supports a visible browser. Navigations follow
|
||||
the destinations named by the scenario and the page's ordinary browser requests;
|
||||
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.
|
||||
Add secret declarations, storage seeds, and additional artifact or event
|
||||
capture only when needed. Action, secret, and storage counts are not capped.
|
||||
Duration, action, and navigation
|
||||
timeouts remain fixed provider-owned liveness limits; the request does not
|
||||
accept a caller-controlled `limits` object.
|
||||
|
||||
## Minimal launch example
|
||||
|
||||
Save this as `browser-scenario.json`, replacing the executable path with an
|
||||
already-installed Chrome-family browser and the URL with your running fixture:
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": {
|
||||
"mode": "launch",
|
||||
"executable_path": "/absolute/path/to/chromium"
|
||||
},
|
||||
"start_url": { "url": "http://127.0.0.1:3000" },
|
||||
"actions": [
|
||||
{ "step_id": "settle", "action": "wait_for_timeout", "duration_ms": 100 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
rea capture-browser-scenario ./browser-scenario.json --json > browser-capture.json
|
||||
```
|
||||
|
||||
The default capture includes the final URL. Select DOM, screenshots, or events
|
||||
only when they help answer the question. REA launches and cleans up the browser
|
||||
profile for this request; it does not start your fixture server or install a
|
||||
browser. A schema-valid scenario does not prove that the executable or target
|
||||
is available on the host.
|
||||
|
||||
## Interaction example: capture the visible result
|
||||
|
||||
The default final URL answers "where did the page end up?", not "what did the
|
||||
page show?". Many interactions, such as a search button that renders results in
|
||||
place, change the page without changing the URL. A capture with only the
|
||||
default `url` artifact then reports that the click succeeded and the URL is
|
||||
unchanged, which cannot tell you whether results appeared.
|
||||
|
||||
Request the artifacts that answer your question and wait for the state you want
|
||||
to inspect. This scenario clicks a Search button, waits for the current search
|
||||
results, and captures the final DOM, accessibility tree, and URL. It assumes the
|
||||
local page clears `#results`'s `data-state` while loading and sets it to `ready`
|
||||
after rendering the current response. Adapt the selectors and readiness marker
|
||||
to your page; an already-visible container or previous results are insufficient.
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": {
|
||||
"mode": "launch",
|
||||
"executable_path": "/absolute/path/to/chromium"
|
||||
},
|
||||
"start_url": { "url": "http://127.0.0.1:3000" },
|
||||
"actions": [
|
||||
{
|
||||
"step_id": "search",
|
||||
"action": "click",
|
||||
"locator": { "kind": "css", "selector": "#search" },
|
||||
"timeout_ms": 10000
|
||||
},
|
||||
{
|
||||
"step_id": "results-ready",
|
||||
"action": "wait_for",
|
||||
"locator": { "kind": "css", "selector": "#results[data-state='ready']" },
|
||||
"state": "visible",
|
||||
"timeout_ms": 10000
|
||||
}
|
||||
],
|
||||
"capture": { "at_end": ["dom", "accessibility", "url"] }
|
||||
}
|
||||
```
|
||||
|
||||
Save it as `browser-search.json` and run it the same way as the minimal
|
||||
example:
|
||||
|
||||
```bash
|
||||
rea capture-browser-scenario ./browser-search.json --json > browser-search-capture.json
|
||||
```
|
||||
|
||||
Through MCP, pass the same object as the `capture_browser_scenario` arguments.
|
||||
The final step's DOM and accessibility artifacts retain the page state at that
|
||||
capture time inline. REA still launches and cleans up the browser profile.
|
||||
|
||||
The click's `timeout_ms` covers the click action. A non-navigating `fetch` or
|
||||
delayed render can finish later, so use `wait_for` to observe a page-specific
|
||||
ready condition before final capture. A page that updates synchronously can
|
||||
omit that wait. REA does not infer application readiness from capture selection.
|
||||
|
||||
Choose captures by the question being asked:
|
||||
|
||||
- `dom` and `accessibility`: text or structure the page rendered, such as
|
||||
search results, validation messages, or updated lists. The accessibility tree
|
||||
is usually the more compact way to read visible text.
|
||||
- `screenshot`: visual layout or styling, or content not exposed as text, such
|
||||
as a canvas.
|
||||
- `history` or `storage`: navigation history, or cookies and web storage the
|
||||
interaction wrote. Request them only when that state is the question; they
|
||||
are never captured implicitly.
|
||||
- `after_each_step` instead of `at_end`: intermediate state between actions in
|
||||
a multi-step scenario.
|
||||
- `events` (`console`, `page-errors`, `network`, and so on): how the page
|
||||
reached its result, such as the request a search issued or an error it
|
||||
logged. Select only the event families you need.
|
||||
|
||||
Capture states are reported per artifact. An artifact you did not request is
|
||||
`not_requested`, which means REA did not look, not that the page had nothing
|
||||
to show. A step's `completeness` of `complete` covers only the sections that
|
||||
were requested. A URL-only capture can be `complete` and still be unable to
|
||||
answer a question about rendered content, so add the relevant artifact and run
|
||||
the scenario again rather than inferring from its absence. Capture completeness
|
||||
also does not assert that the page's background work has finished.
|
||||
|
||||
The browser boundary is part of the contract. Launch mode requires a
|
||||
caller-selected absolute executable, a provider-owned temporary profile,
|
||||
headless operation, and close-plus-delete cleanup. Connect mode accepts only an
|
||||
explicit-port loopback CDP endpoint, declares the browser externally owned, and
|
||||
permits disconnect-only cleanup. The Playwright driver preserves this
|
||||
caller-selected executable and always uses a provider-owned temporary profile
|
||||
that is closed and deleted during cleanup. Connect mode accepts only an
|
||||
explicit-port loopback CDP endpoint. The Playwright driver preserves this
|
||||
distinction: it closes and deletes provider-owned profiles, but only disconnects
|
||||
from an external CDP browser. Real-browser verification checks both outcomes and
|
||||
confirms that an attached external browser remains alive.
|
||||
|
||||
URLs contain a query-free HTTP(S) base plus ordered query entries. Every query,
|
||||
form, storage, cookie, and replay value is either an explicitly public literal
|
||||
or a reference to a declared environment-backed secret. Raw credentials in
|
||||
URLs and literal credential headers are rejected. Secret query names must also
|
||||
appear in the redaction policy, and the required credential headers cannot be
|
||||
removed from that policy. Durable results must replace resolved secret values
|
||||
HTTP(S) URLs may include ordinary query values and fragments directly. Use
|
||||
ordered structured query entries when a value needs to reference a declared
|
||||
environment-backed secret. Form, storage, and cookie values are either literal
|
||||
strings or declared secret references. Raw URL userinfo credentials are rejected.
|
||||
Captured credential-header
|
||||
values are not retained, and declared secret values are redacted automatically.
|
||||
A secret may be declared solely to
|
||||
redact matching observed content; every secret reference in an action, URL,
|
||||
storage value still needs a declaration. Ordinary query values
|
||||
and fragments remain intact. Durable results replace resolved secret values
|
||||
with their secret references.
|
||||
|
||||
Every start, navigation, storage, replay, and redirect origin must be listed in
|
||||
`allowed_origins`. Unsupported action tags, unknown fields, duplicate step or
|
||||
route IDs, undeclared or unused secrets, and provider-owned replay headers fail
|
||||
validation. HTTP and WebSocket routing enforces the same declared scope. Exact
|
||||
replay may abort unmatched requests or pass through only approved origins.
|
||||
The exact `start_url`, navigation destinations, and storage-seed origins define
|
||||
the inputs to a scenario; ordinary page requests are handled by the browser.
|
||||
Storage cookies are scoped to the page URL at each capture. Unsupported action
|
||||
tags, unknown fields, duplicate step IDs, and undeclared secret references fail
|
||||
validation.
|
||||
|
||||
The result is Evidence v2 with an initial state followed by one record per
|
||||
The result is Evidence with an initial state followed by one record per
|
||||
declared action. Each step reports action status, elapsed time, sanitized URLs,
|
||||
event bounds, and independently typed capture states for screenshot, DOM,
|
||||
accessibility, URL, history, and storage. Console, page-error, network,
|
||||
WebSocket, frame, worker, popup, and cancelled-download events are attributed to
|
||||
steps and bounded by the scenario limits. Missing and truncated sections remain
|
||||
explicit and make the capture ineligible for equality claims. Attach-mode
|
||||
captures also declare the unavoidable pre-attach event gap.
|
||||
steps and all observed events and requested artifacts are returned inline.
|
||||
There are no event, frame, worker, popup, WebSocket, DOM-node,
|
||||
accessibility-node, storage-entry, screenshot-count, or cumulative
|
||||
metadata-byte caps. Requested artifacts and observed events are returned
|
||||
inline without application-defined size ceilings. Other missing sections
|
||||
remain explicit and make the capture ineligible for equality claims.
|
||||
Attach-mode captures also declare the unavoidable pre-attach event gap.
|
||||
|
||||
Browser automation is disabled by default. The administrator ceiling is
|
||||
configured with:
|
||||
Selected event families are observed on the root page and discovered descendant
|
||||
popups, including nested popups. Popup lifecycle records are emitted only when
|
||||
`popups` is selected. Launch-mode network observation starts at the owned
|
||||
context before navigation, so popup document requests, redirects, and responses
|
||||
are included once. Connected contexts can contain unrelated tabs: only network
|
||||
events with a frame belonging to the root or a discovered popup are retained.
|
||||
Initial popup requests without a frame cannot be attributed in shared contexts
|
||||
and are omitted; attach coverage remains incomplete.
|
||||
|
||||
- `REA_BROWSER_SCENARIO_ENABLED`
|
||||
- `REA_BROWSER_SCENARIO_EXECUTABLE_ROOTS_JSON`
|
||||
- `REA_BROWSER_SCENARIO_CDP_ENDPOINTS_JSON`
|
||||
- `REA_BROWSER_SCENARIO_ALLOWED_ORIGINS_JSON`
|
||||
- `REA_BROWSER_SCENARIO_ALLOWED_ENV_JSON`
|
||||
Playwright reports new popup Pages after their first navigation has committed.
|
||||
It buffers early console messages and page errors, but the pinned API cannot
|
||||
recover every earlier frame, worker, WebSocket, or download event. If one of
|
||||
those families is selected and a popup is discovered, REA reports the family gap
|
||||
in `limitations`, marks `events` missing, and disables equality eligibility.
|
||||
Later events remain available; no initial navigation is synthesized from the
|
||||
popup's current URL.
|
||||
|
||||
Unlike passive observation, browser automation is not granted automatically.
|
||||
Use an explicit project/session grant, or opt into the administrator grant with
|
||||
`REA_BROWSER_SCENARIO_AUTO_GRANT=true` for a trusted unattended environment.
|
||||
Authorization commits the exact executable or CDP endpoint, origins, environment
|
||||
variable names, network class, and scenario digest before any browser side
|
||||
effect.
|
||||
Each request names the exact launch executable or loopback CDP endpoint, target,
|
||||
actions, and capture behavior. Origin filters and environment-variable names
|
||||
are included when the scenario needs them.
|
||||
Launch owns a temporary profile; connect mode disconnects from the selected
|
||||
external browser without closing it. The request itself defines the operation;
|
||||
there is no additional REA grant step. The browser and host still enforce their
|
||||
own access and process rules.
|
||||
|
||||
## Network evidence
|
||||
|
||||
`capture.events: ["network"]` retains request, response, completion, failure,
|
||||
and unfinished-request metadata. Select content independently:
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": {
|
||||
"mode": "launch",
|
||||
"executable_path": "/absolute/path/to/chromium"
|
||||
},
|
||||
"start_url": { "url": "http://127.0.0.1:3000" },
|
||||
"actions": [
|
||||
{
|
||||
"step_id": "run",
|
||||
"action": "click",
|
||||
"locator": { "kind": "css", "selector": "#start" },
|
||||
"timeout_ms": 10000
|
||||
},
|
||||
{
|
||||
"step_id": "ready",
|
||||
"action": "wait_for",
|
||||
"locator": { "kind": "css", "selector": "#done" },
|
||||
"state": "visible",
|
||||
"timeout_ms": 10000
|
||||
}
|
||||
],
|
||||
"capture": {
|
||||
"network": {
|
||||
"request_body": true,
|
||||
"response_body": true,
|
||||
"header_values": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All three options default to `false`. Any selected network content also enables
|
||||
network metadata observation; listing `network` in `events` is then optional.
|
||||
Each observed request object receives a capture-scoped `transaction_id`.
|
||||
Requests sharing a URL remain distinct. Redirects link to the preceding
|
||||
transaction only when it was observed; a null predecessor does not establish
|
||||
that no earlier hop existed. Older captures without IDs remain readable and
|
||||
their transaction association is unknown.
|
||||
|
||||
`scenario.network_content` records the resolved content selection. Comparing
|
||||
captures with different selection, or legacy captures without that coverage
|
||||
information, reports network evidence as unknown with a recovery instruction.
|
||||
Capturing more data alone does not establish a website change.
|
||||
|
||||
`network-content` events reference the exact request or response event through
|
||||
`source_event_sequence`. They include independently typed headers and body
|
||||
states. Captured bodies retain base64 bytes, byte count, media type, and SHA-256
|
||||
of the retained bytes. Request bytes are those exposed by Playwright; a
|
||||
`not_exposed` request body does not prove absence or complete multipart file
|
||||
coverage. Response bytes are decoded by the browser, not the compressed wire
|
||||
representation. No later fetch is substituted for the original response.
|
||||
Headers preserve producer order and duplicates. Authorization,
|
||||
Proxy-Authorization, Cookie, and Set-Cookie values are null with
|
||||
`redacted: true`; ordinary values remain intact. Declared UTF-8 secret values
|
||||
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.
|
||||
|
||||
Only completed responses are read. At the end of the final step, observation
|
||||
stops; requests still in flight emit `request-unfinished`, and their observed
|
||||
responses have explicitly unavailable bodies. This cutoff does not wait for
|
||||
long-lived streaming connections. Already-started content reads settle before
|
||||
browser cleanup, with a provider-owned five-second timeout per read and
|
||||
cancellation support. Failed or unexposed reads retain per-content states and make selected
|
||||
event coverage incomplete. Body events can arrive during later steps or final
|
||||
drain; event bounds describe receipt during the step, while the source event
|
||||
reference establishes the transaction phase. Comparison attributes content to
|
||||
its source event's step and excludes the sequence reference from normalization.
|
||||
Playwright scenario capture does not supply initiator stacks; passive CDP
|
||||
observation retains its own initiator evidence.
|
||||
|
||||
## Scenario comparison
|
||||
|
||||
@@ -75,6 +291,6 @@ capture fields and are preserved in the result.
|
||||
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.
|
||||
`max_changes` bounds retained artifact records while total and omitted counts
|
||||
remain visible. Missing, truncated, or mismatched capture coverage remains
|
||||
unknown instead of being treated as equality.
|
||||
Every changed or unknown artifact record is returned inline for each aligned
|
||||
step. Missing, truncated, or mismatched capture coverage remains unknown
|
||||
instead of being treated as equality.
|
||||
|
||||
@@ -0,0 +1,427 @@
|
||||
# Incremental capability migration
|
||||
|
||||
This working guide tracks the migration in #740. Update it as concrete domains
|
||||
migrate. Existing public contracts and domain-specific ports remain authoritative;
|
||||
there is no new universal provider interface or fixed module manifest.
|
||||
|
||||
## Ownership to preserve
|
||||
|
||||
| Responsibility | Current owner | Migration direction |
|
||||
| --------------------------------------------- | ----------------------------------- | ----------------------------------------------------- |
|
||||
| Analyst semantics and Evidence representation | domain | Keep pure and provider-neutral |
|
||||
| Named public input/output contracts | contracts | Keep exact CLI/MCP meaning and complete discovery |
|
||||
| Producer parsing and external tool protocols | provider adapters | Keep behind the relevant typed port |
|
||||
| Shared analyst workflows | application | Share between CLI and MCP |
|
||||
| Concrete provider construction | composition, MCP startup | Keep fresh typed factories at the production boundary |
|
||||
| Investigation Evidence and Unknowns | InvestigationRecords/EvidenceLedger | Consume narrow read/write/atomic record ports |
|
||||
| Target/profile snapshots and invalidation | BinarySessionRecords/BinarySession | Keep binary-owned |
|
||||
| Subprocess ownership and host primitives | process/windows | Reuse; preserve cancellation and cleanup |
|
||||
| Public translation | CLI/MCP adapters | Delegate to shared workflows and named contracts |
|
||||
|
||||
Retain exact Evidence IDs, provenance, detached reads, atomic Evidence/Unknown
|
||||
mutations and optimistic revisions. Preserve current close and failed-cleanup
|
||||
record clearing while extracting ownership; #721 owns any retention-policy change.
|
||||
Each CLI invocation and MCP connection keeps its own state. Provider registration
|
||||
must not launch engines or install prerequisites.
|
||||
|
||||
## Migrate before generalizing
|
||||
|
||||
1. Record the actual callers, state, environment, protocol and verification owner
|
||||
of the capability being migrated.
|
||||
2. Move concrete construction behind its existing typed port. Preserve injected
|
||||
ports, selected environment, queues, failure state and cleanup behavior.
|
||||
3. Test both caller paths and the applicable package/real-provider boundary.
|
||||
4. Use independently migrated capabilities to identify repeated metadata and
|
||||
composition needs. Derive a small source declaration only when those needs
|
||||
are established; keep unlike target/lifecycle semantics separate.
|
||||
5. Move a coherent capability's files, fixtures and verification lanes in a
|
||||
mechanical PR. Update all path-sensitive consumers in that same PR.
|
||||
|
||||
Pure helpers do not need an interface each. A capture-format adapter or typed
|
||||
factory may be sufficient. Avoid generic JSON execution, external MCP catalog
|
||||
passthrough, eager provider acquisition, new permission fields or a multi-package
|
||||
rewrite. Optional loading should preserve successful peers and report the failed
|
||||
capability without hiding tools from the catalog.
|
||||
|
||||
## Checks before and after a move
|
||||
|
||||
`npm run verify:test-discovery` compares actual Vitest file discovery with every
|
||||
repository-owned `*.test.ts` under src and tests, including newly added files. The
|
||||
check requires Git, includes untracked non-ignored tests, and excludes removed
|
||||
paths during a move. It
|
||||
rejects missing files and overlapping project ownership. It only lists files;
|
||||
it does not replace behavior tests or real-provider verification.
|
||||
|
||||
Check imports, project isolation and coverage, development-test selection,
|
||||
lint exceptions, catalog/generator imports, npm and CI verification entrypoints,
|
||||
fixture ownership, and installed-package bridge/native path resolution.
|
||||
Domain and contracts must not import application workflows, caller adapters or
|
||||
provider implementations. Production composition is a deliberate outer boundary.
|
||||
Other existing adapter imports will migrate incrementally; do not widen a lint
|
||||
exception to conceal new protocol code inside application workflows.
|
||||
|
||||
`npm run verify:module-boundaries` parses repository-owned source imports,
|
||||
reexports, type imports and literal dynamic imports, then checks their resolved
|
||||
source layer. Nested names such as `domain/android` are domain code; `src/android`
|
||||
is the provider adapter. The check runs with lint, including cached static gates.
|
||||
It covers tracked and untracked non-ignored TypeScript source and skips cached
|
||||
paths removed during a move. Pass exact source paths for a focused check.
|
||||
|
||||
Pure source and its tests keep inward dependencies. Application/server provider
|
||||
construction guards cover the migrated deep, Android, firmware, JavaScript recovery and observation
|
||||
implementations. Application workflows cannot import composition, including the
|
||||
former binary runtime entrypoint; shared browser capture/export helpers remain admitted while their
|
||||
ownership is reviewed. Test lane restrictions still use Oxlint. Producer-dependent
|
||||
browser and managed comparison tests live in boundary lanes with their original
|
||||
fixtures and assertions. This is a development guard, not runtime authorization.
|
||||
|
||||
Select verification by the claim changed: schema/composition, package, real
|
||||
engine or host-native workflow. Record host/target prerequisites and source-owned
|
||||
fixtures. Run heavy lanes serially with bounded workers/heaps and reuse existing
|
||||
engines. Recording ports do not prove real provider or platform support.
|
||||
|
||||
## Extension examples to validate during migration
|
||||
|
||||
An implementation of an existing port should require a provider adapter, typed
|
||||
production factory selection, exact producer parsing/provenance and matching
|
||||
verification. Existing workflow meaning and public contracts should stay stable.
|
||||
|
||||
A new analyst capability deliberately adds semantics, named contracts, a typed
|
||||
port/workflow when needed, both caller adapters, availability, documentation and
|
||||
verification. Its integration is broader than swapping a provider. The migration
|
||||
should eliminate incidental duplicate wiring, not erase those design decisions.
|
||||
|
||||
Update these examples with real migrated entrypoints before closing #740.
|
||||
|
||||
## First production factory migration
|
||||
|
||||
`src/composition/android.ts` and `src/composition/firmware.ts` construct providers
|
||||
behind the existing AndroidAnalysisPort and FirmwareAnalysisPort. CLI registrars
|
||||
pass their selected environment; MCP uses the process environment unless a caller
|
||||
injects a port. Each factory call returns a fresh instance and acquires no process,
|
||||
workspace or toolchain. Queues, cleanup-failure state and per-request cleanup stay
|
||||
inside the provider. The existing launcher seams remain available to boundary
|
||||
fixtures, which now exercise the production factories.
|
||||
|
||||
Application workflows and MCP registration must not import these concrete
|
||||
providers. Composition may import their implementations. Binary composition also
|
||||
uses this outer boundary after its direct-analysis callers were migrated.
|
||||
This concrete pilot does not introduce a shared provider interface or module
|
||||
manifest.
|
||||
|
||||
## Independent optional observation adapters
|
||||
|
||||
Browser observation, browser scenarios, Electron observation, Electron scenarios
|
||||
and V8 Inspector observation now have separate factories in `src/composition/`.
|
||||
CLI commands reuse them; MCP startup dynamically imports each factory independently.
|
||||
`OptionalObservationProviders` describes these five existing typed ports only. It
|
||||
is provisional startup wiring, not an extensible plugin manifest or a universal
|
||||
provider interface.
|
||||
|
||||
A failed import or constructor retains the successful peers. The complete tool
|
||||
catalog remains advertised. Session availability reports the failed adapter and
|
||||
its actual reason, and affected handlers return the existing capability-unavailable
|
||||
error. Explicit loading failures take precedence over configured availability.
|
||||
Passive Electron and active Electron failures are independent; static JavaScript
|
||||
analysis and runtime reconciliation remain usable without either adapter. CLI
|
||||
Electron commands also load only their selected runtime factory at execution.
|
||||
|
||||
Source identities are available without importing the optional implementations.
|
||||
Registration acquires no engines, probes no endpoints, changes no permissions and
|
||||
installs nothing. Core transport failures and shutdown still use their existing
|
||||
lifecycle. Fault-injection coverage uses the production loading/startup path and
|
||||
actual MCP SDK calls; recording ports establish composition behavior, while the
|
||||
real Inspector lane separately establishes runtime behavior.
|
||||
|
||||
## Investigation record ownership pilot
|
||||
|
||||
`src/application/investigation/InvestigationRecords.ts` owns one existing
|
||||
EvidenceLedger per runtime. The production session composition constructs that
|
||||
owner explicitly; the direct BinarySession constructor retains a fresh default
|
||||
for compatibility. There is no second ledger, global store, persistence or new
|
||||
retention policy.
|
||||
|
||||
The ledger, Unknown Evidence helper and the ledger's two original behavior tests
|
||||
now live beside the owner in `src/application/investigation/`. Bundle validation
|
||||
and record-owner consumers follow the new paths; retained-reference access still
|
||||
uses the narrow record ports. The move preserves implementation bodies and test
|
||||
assertions. Binary snapshot/cache files remain with their existing owner.
|
||||
|
||||
EvidenceReader, EvidenceWriter and EvidenceUnknownWriter describe existing
|
||||
read/write/atomic callback needs. UnknownRegistryPort preserves optimistic
|
||||
revisions and consistency verification. Non-binary MCP registrars use these
|
||||
record types without depending on the binary execution/lifecycle surface.
|
||||
BinarySessionPort retains its compatible record methods through composition.
|
||||
|
||||
BinarySessionRecords remains the snapshot owner and compatibility facade. It
|
||||
passes the actual active target explicitly for recordUnknown/updateUnknown;
|
||||
recordEvidenceWithUnknown keeps its target-free mutation subject. Snapshot import
|
||||
merges records without notifying midway, then emits after the cache commits.
|
||||
Bundle import retains the full change delta so Unknown-only changes notify even
|
||||
when recordsAdded is zero. Clearing resets records, cache and invalidation before
|
||||
notification; observer failures remain best effort after a committed mutation.
|
||||
|
||||
Close and failed-cleanup clearing, ordinary target-switch retention, detached
|
||||
reads, Evidence identities and deterministic bundle transfer keep their existing
|
||||
behavior. Snapshots still require an active target and concrete analysis profile;
|
||||
extracting investigation ownership does not make them a target-free record store.
|
||||
#721 remains responsible for any future lifetime-policy change.
|
||||
|
||||
## Inspector adapter ownership
|
||||
|
||||
`src/inspector/` owns V8 Inspector discovery, passive capture, script/target
|
||||
location interpretation and its lightweight provider identity. The composition
|
||||
factory and optional loader use this owner. Browser CDP connection, endpoint/value
|
||||
and Electron file-location helpers remain deliberate shared adapter utilities at
|
||||
their existing paths; no provider wire protocol moves into domain or process.
|
||||
|
||||
Loopback fixtures and producer boundaries now live under
|
||||
`tests/fixtures/inspector/` and `tests/boundary/inspector/`. The socket-backed
|
||||
unresolved-target reconciliation test moves from composition into the forked
|
||||
boundary lane with its original assertions. Adapter tests retain fork isolation.
|
||||
`npm run verify:inspector` keeps its public development entrypoint and delegates
|
||||
to `scripts/verify/inspector/runtime-observation.mjs`, using the real Node fixture
|
||||
under `tests/conformance/inspector/`. Catalog imports, source guards and test
|
||||
discovery follow the new paths. Observation authority, lifecycle, producer
|
||||
interpretation and caller-visible contracts stay unchanged.
|
||||
|
||||
## Auxiliary source declaration pilot
|
||||
|
||||
Artifact, macOS native and managed static metadata now belongs to each adapter's
|
||||
`*ProviderMetadata.ts` module. The implementation and
|
||||
`src/composition/auxiliaryAnalysisProviders.ts` reuse the same identity and exact
|
||||
capability descriptors. The declarations reference existing AnalysisProvider
|
||||
loaders; they cover these three disjoint auxiliary implementations only. Deep
|
||||
candidate selection and the five typed observation ports remain separate.
|
||||
|
||||
The binary runtime reads these source declarations while auxiliary construction
|
||||
stays lazy. The separate managed-only entrypoint retains direct construction for
|
||||
its execution-free workflow. The MCP catalog generator reads declarations without
|
||||
constructing implementations, using its canonical macOS platform; actual runtime
|
||||
host restrictions remain explicit.
|
||||
Discovery and binary operation-kind routing now consume canonical source contracts
|
||||
rather than their generated JSON artifact. No declaration imports its generated
|
||||
output. Generated catalog facts and public schemas stay unchanged.
|
||||
|
||||
All observation registrars resolve contracts by name. An isolated compiled-process
|
||||
test reverses presentation arrays and performs actual MCP SDK calls to verify
|
||||
advertised schemas and handler meaning. Reordering metadata must not select a
|
||||
different operation, schema, provider or deep-provider priority.
|
||||
|
||||
## Android layer and verification ownership
|
||||
|
||||
Android workflows and their existing typed port live in `src/application/android/`,
|
||||
pure APK/inventory semantics in `src/domain/android/`, and the named inspection
|
||||
contracts in `src/contracts/android/`. The headless JADX producer remains in
|
||||
`src/android/`; `src/composition/android.ts` supplies its fresh factory to both
|
||||
caller adapters. Public names, result meaning and JVM/queue/cleanup limits are
|
||||
unchanged. Inventory-only application projection remains execution-free.
|
||||
|
||||
The real lane and signal-cleanup helper belong to `scripts/verify/android/`; its
|
||||
explicit downloader and fixed manifest belong to `scripts/fixtures/android/`.
|
||||
`verify:android` and `fixtures:android` keep their command names and repository-root
|
||||
`_reference/apk-integration/` fixture default. Synthetic producer fixtures stay in
|
||||
`tests/fixtures/android/`, with matching boundary and composition ownership. Real
|
||||
JADX CLI/MCP parity requires the fixed APK and existing audited JAR; synthetic
|
||||
protocol/cancellation success does not establish that engine or an unverified host.
|
||||
|
||||
## Deep-provider source declarations
|
||||
|
||||
Hopper, Ghidra and IDA declare their implemented analyst operations in their
|
||||
own `*ProviderCapabilities.ts` modules. Hopper's inventory is typed against the
|
||||
existing direct-operation contracts; Ghidra reuses its inventory/function adapter
|
||||
operations; IDA retains its read-only operation family. Frozen operation arrays
|
||||
feed the existing capability builders without importing generated catalog data.
|
||||
|
||||
The catalog generator reads these source declarations and joins operation names
|
||||
to canonical `TOOL_CONTRACTS`. A missing contract identifies the declaring provider
|
||||
and operation. Public schemas and descriptions remain owned by contracts; the
|
||||
declarations describe implementation coverage. Provider adapters must not consume
|
||||
the generated MCP catalog that is derived from them. The resolved module guard
|
||||
checks this edge while admitting canonical source contracts, generated package
|
||||
metadata and the existing application snapshot-cache consumer.
|
||||
|
||||
Exact provider effects and lifecycle differences remain in their adapters:
|
||||
Hopper's GUI and mutation effects, Ghidra's experimental Windows authority and
|
||||
mutation restrictions, and IDA's attached/headless effects and live observations.
|
||||
This source increment introduces no common provider lifecycle or new interface.
|
||||
|
||||
## JavaScript application ownership
|
||||
|
||||
`src/application/javascript/` owns JavaScript/Electron artifact analysis and
|
||||
graph projection, feature/version/export/source comparisons, runtime Evidence
|
||||
and reconciliation, observation ports, and source recovery workflows. Pure
|
||||
analysis and public schemas keep their existing domain/contract ownership;
|
||||
shared artifact inventory, investigation records and execution types remain
|
||||
shared. CLI, MCP and production factories import the owning workflow directly.
|
||||
|
||||
`src/domain/javascript/` owns static parsing and semantic IR, application and
|
||||
semantic graphs, Electron facts, runtime reconciliation, recovery result
|
||||
semantics, and source-to-bundle comparisons. Shared Evidence, digests, ordering,
|
||||
comparison semantics and historical-source graphs stay at their existing owners.
|
||||
Native handoffs link exact-subject Evidence without selecting or executing a
|
||||
provider.
|
||||
|
||||
`src/contracts/javascript/` owns the JavaScript/Electron observation and recovery
|
||||
contracts, JavaScript-specific workflow requests, and their examples. The shared
|
||||
`applicationToolContracts.ts` aggregate stays at the contract root because it also
|
||||
owns reconstruction and Android/Apple projection operations. Canonical tool
|
||||
aggregation, named handler binding and SDK schema presentation remain unchanged.
|
||||
Catalog source paths, compiled verifier/SDK fixtures and recovery CI filters
|
||||
follow the domain/contract owners.
|
||||
|
||||
`scripts/verify/javascript/` groups application-workflow, runtime-reconciliation,
|
||||
large-digest and real-recovery entrypoints. Their existing npm commands retain
|
||||
their invocation semantics. Recovery fixture compilation lives beside its
|
||||
unchanged sources under `scripts/fixtures/javascript-recovery/`. Its default
|
||||
REA entrypoint still resolves to `scripts/rea.mjs`. Managed conformance imports
|
||||
the relocated compiled workflow; owning CI source filters follow the new paths.
|
||||
|
||||
`src/artifacts/javascript/` owns directory/ASAR reader construction and relevant
|
||||
file acquisition, including root/nested byte verification, UTF-8 diagnostics and
|
||||
nested reader cleanup. Its narrow factory returns the existing `ArtifactReader`;
|
||||
the application retains input admission, inventory, graph construction and its
|
||||
original root-reader `finally` close. Pure file facts live in
|
||||
`src/domain/javascript/javascriptArtifactFiles.ts`, and the shared immutable
|
||||
inventory snapshot lives in `src/domain/artifactInventorySnapshot.ts`. Inventory
|
||||
options and integrity policy remain application-owned. Shared stream hashing
|
||||
lives in `src/artifacts/ArtifactHash.ts`, preserving complete byte counts and the
|
||||
bounded classification prefix. Acquisition/hash guards reject imports from
|
||||
application, composition or caller adapters; JavaScript workflows use the factory
|
||||
instead of importing concrete ASAR/directory reader classes.
|
||||
|
||||
ZIP, native-DMG and Mach-O inventory reader selection keep their existing
|
||||
lifecycles. Runtime ports retain their distinct Inspector, passive Electron and
|
||||
active Electron effects; shipped Electron hooks remain at their existing
|
||||
provider boundary.
|
||||
|
||||
## Managed capability ownership
|
||||
|
||||
`src/domain/managed/` owns managed artifact and member semantics, reconstruction
|
||||
import, native-boundary verification and application graph projection.
|
||||
`src/application/managed/` owns their shared Evidence workflows, and
|
||||
`src/contracts/managed/` owns their named contracts and examples. Producer
|
||||
metadata/IL parsing stays in `src/dotnet/`; target resolution, Evidence, ordering
|
||||
and investigation records retain their shared owners. Path-based member
|
||||
comparison preserves its actual target/byte-digest admission and existing parser
|
||||
helper.
|
||||
|
||||
`scripts/verify/managed/` groups the portable conformance entrypoint and its
|
||||
support, manifest, oracle and completion-report helpers. Its PE byte fixture
|
||||
builder and declaration live in `scripts/fixtures/managed/`, shared with the
|
||||
installed-package lane and existing managed conformance tests. `verify:managed`
|
||||
keeps its command name and optional caller-cwd manifest resolution. Compiled
|
||||
catalog and generator imports follow the owners; document-check cache inputs
|
||||
include the relocated runtime verifier and fixture files.
|
||||
|
||||
Portable byte fixtures, optional operator-local manifests, actual ILSpy oracles
|
||||
and real Ghidra NativeAOT checks retain their distinct proof levels and
|
||||
prerequisites. The existing NativeAOT fixture/engine lane keeps its owner. This
|
||||
move preserves implementations, Evidence identities and generated commitments.
|
||||
|
||||
## Binary application ownership
|
||||
|
||||
`src/application/binary/` owns the active session, deep-provider registry and
|
||||
evaluation, operation routing, disjoint provider composition, lazy auxiliary
|
||||
clients, cancellation and client cleanup. Its records facade and snapshot
|
||||
cache/files remain bound to the exact target and analysis profile. Investigation
|
||||
records still have their separate owner in `src/application/investigation/`.
|
||||
|
||||
`AnalysisProvider.ts` stays shared because its identity/execution types also serve
|
||||
nonbinary ports. `BinaryTargetResolver.ts` stays shared because Android, firmware,
|
||||
managed and artifact workflows use its target parsing. Production wiring lives
|
||||
in `src/composition/binary.ts`; application workflows gain no outward composition
|
||||
dependency.
|
||||
|
||||
The three colocated application tests move with their owner. Composition cases
|
||||
remain in `tests/composition/analysis-sessions/`; filesystem and SDK cases retain
|
||||
their boundary lanes. The shared `tests/fixtures/binarySession.ts` factory supports
|
||||
several capability families. Existing recursive application globs discover the
|
||||
moved tests; bridge assets and real-verifier entrypoints remain at their owners.
|
||||
|
||||
## Firmware layer and verification ownership
|
||||
|
||||
Firmware workflows and their existing typed port live in
|
||||
`src/application/firmware/`, pure request/result semantics in
|
||||
`src/domain/firmware/`, and named contracts in `src/contracts/firmware/`.
|
||||
`src/firmware/` keeps producer commands, report parsing, source copies, publication
|
||||
and cleanup; `src/composition/firmware.ts` supplies a fresh typed factory.
|
||||
|
||||
The real lane belongs to `scripts/verify/firmware/analysis.mjs`; the fixture runner
|
||||
and its unchanged Python producer belong together in `scripts/fixtures/firmware/`.
|
||||
The npm command names and selected `REA_FIRMWARE_FIXTURE_ROOT` are unchanged. The
|
||||
runner retains its working-directory-relative default; the verifier retains its
|
||||
repository-root default. Both point to `_reference/firmware-integration/generated`
|
||||
when invoked through the documented npm commands. Optional ext4/Ghidra lanes keep
|
||||
their own prerequisites. Source-fixture generation and synthetic producer tests
|
||||
do not establish real Binwalk/Unblob analysis or another host's execution support.
|
||||
|
||||
## Apple artifact capability ownership
|
||||
|
||||
`src/domain/apple/` owns Apple application anatomy/projection, asset catalog
|
||||
facts, Interface Builder graphs, keyed archive semantics and plist values.
|
||||
`src/application/apple/AppleApplicationService.ts` retains the shared workflow
|
||||
that projects authenticated IPA/macOS inventory Evidence.
|
||||
|
||||
`src/artifacts/apple/` owns the actual producer boundary: directory acquisition,
|
||||
assetutil invocation, plist/NIB decoding, encoded view-parent interpretation and
|
||||
keyed archive inspection. These helpers are used by ArtifactProvider, retain
|
||||
its existing typed analysis boundary, injected assetutil seam and original reader
|
||||
cleanup, and do not require a new port for each helper. Producer code no longer
|
||||
lives in the application layer. Guards reject application/composition/caller
|
||||
imports from this owner while admitting pure facts and shared artifact readers.
|
||||
Adapter-source tests retain their assertions in the existing forked adapter lane.
|
||||
|
||||
`scripts/verify/apple/` groups archive, asset catalog, Interface Builder,
|
||||
Objective-C/Swift dispatch and macOS bundle verification. The macOS bundle fixture
|
||||
builder lives in `scripts/fixtures/apple/`; NIB byte fixtures stay beside their
|
||||
artifact decoder. Existing format-specific conformance sources and goldens keep
|
||||
their locations, and relocated verifiers resolve them against their own file
|
||||
URLs. Public npm entrypoints and CLI/MCP contracts stay stable; generated-document
|
||||
cache inputs include the relocated runtime scripts and fixture builder.
|
||||
|
||||
Portable decoder and injected producer tests establish their format/boundary
|
||||
claims. Actual Swift/Xcode, assetutil, native NIB, bundle signing and DMG lifecycle
|
||||
claims require the real Apple lane. Native binary API/value-flow semantics and
|
||||
shared cross-domain contract aggregates keep their separate owners.
|
||||
|
||||
## Binary production composition closure
|
||||
|
||||
`src/composition/binary.ts` is the shared production owner for deep candidate
|
||||
construction, lazy auxiliary composition and the managed-only session factory.
|
||||
It preserves the existing constructor bodies, selection policy and fresh session
|
||||
per invocation. Factory registration acquires no engine or target.
|
||||
|
||||
`DirectAnalysis` and `DirectAnalysisStatus` receive these two existing factories
|
||||
through `DirectAnalysisDependencies`. They retain configuration parsing, selected
|
||||
status environment, cancellation, snapshot replay/binding, Evidence projection
|
||||
and finally-close. The dependency uses the existing BinarySession type, including
|
||||
its replay policy; it adds no universal backend or lifecycle contract.
|
||||
|
||||
`src/composition/directAnalysis.ts` binds the production factories for CLI and
|
||||
one-shot managed MCP callers. Binding creates no session and stores no retained
|
||||
analysis state. MCP startup constructs its connection session through the same
|
||||
binary factory. Source and compiled fixtures use these entrypoints. The former
|
||||
application runtime path is removed, and the source guard no longer admits its
|
||||
outward provider/composition imports.
|
||||
|
||||
## Native analyst capability ownership
|
||||
|
||||
`src/domain/native/` owns portable native inspection, instruction/data types,
|
||||
load-image facts, API boundaries, annotations, metadata recovery, UI observation
|
||||
semantics and value/investigation graphs. `src/application/native/` owns shared
|
||||
API projection, call routes, dispatch inspection and UI/value tracing. Its tests
|
||||
keep the existing service lane. `src/contracts/native/` owns the named native
|
||||
adapter contracts; shared official/enhanced aggregates remain at their owners.
|
||||
|
||||
Producer interpretation, external commands and host UI lifecycle remain in
|
||||
`src/native/`, Hopper, Ghidra and IDA. The shared function dossier still lives in
|
||||
`domain/hopperValues.ts`: its historical name does not make its meaning exclusive
|
||||
to Hopper. This move retains existing typed ports, exact result schemas,
|
||||
observed/derived/unknown distinctions, profiles and Evidence links.
|
||||
|
||||
Source and compiled verifier imports follow the new owners. Host UI and real
|
||||
native-value verifier locations are retained until their own proof lanes migrate;
|
||||
interactive macOS UI success and real dependency tracing require their respective
|
||||
host/engine workflows. Portable source fixtures and injected call-route tests
|
||||
establish their narrower boundaries, without expanding provider/platform support.
|
||||
@@ -1,101 +0,0 @@
|
||||
# Controlled JavaScript replay
|
||||
|
||||
`run_controlled_replay` executes only operator-selected extracted modules. It
|
||||
does not launch or drive the original application. Static inference, passive
|
||||
browser/Electron observation, isolated replay, and real application behavior
|
||||
remain different authorities.
|
||||
|
||||
## Enable the ceiling
|
||||
|
||||
Replay is disabled by default. Configure exact source roots before REA starts:
|
||||
|
||||
```sh
|
||||
export REA_JAVASCRIPT_REPLAY_ENABLED=true
|
||||
export REA_JAVASCRIPT_REPLAY_ROOTS_JSON='["/absolute/extracted/modules"]'
|
||||
```
|
||||
|
||||
The default trusted executables are the current Node.js runtime,
|
||||
`/usr/bin/bwrap`, `/usr/bin/systemd-run`, `/usr/bin/systemctl`, and
|
||||
`/usr/bin/bash`. Override them only with the corresponding
|
||||
`REA_JAVASCRIPT_REPLAY_*_PATH` variable. Configuration creates the
|
||||
administrator ceiling; it does not replace per-call approval. `rea doctor`
|
||||
reports disabled, available, and exact sandbox-probe failures without running
|
||||
module code. REA setup never installs these host components.
|
||||
|
||||
## Plan, review, execute
|
||||
|
||||
Call the same CLI command or MCP tool twice. The first request uses
|
||||
`mode: "plan"`. Its response commits module paths and digests, closed dependency
|
||||
aliases, cases, deterministic providers, runtime/backend identities, resource
|
||||
limits, no-network policy, private filesystems, and a `plan_digest`.
|
||||
Runtime commitments include the exact worker, seccomp filter, Node executable,
|
||||
ELF loader, and shared-library source/destination paths and SHA-256 digests.
|
||||
|
||||
The execute request must repeat the same manifest and include:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "execute",
|
||||
"approved": true,
|
||||
"plan_digest": "<exact digest returned by plan>"
|
||||
}
|
||||
```
|
||||
|
||||
REA rebuilds the plan. Any changed module, stub, case, runtime, backend, limit,
|
||||
or export commitment returns `plan_stale` before worker admission.
|
||||
|
||||
Modules use `esm` or `commonjs-factory`. The latter accepts extracted Rspack
|
||||
factory syntax such as `123(module, exports, require) { ... }` and implements
|
||||
the bounded `require.d`, `require.r`, `require.n`, and `require.nmd` helpers.
|
||||
Every import or require must map to a declared alias. There is no ambient
|
||||
package resolution.
|
||||
|
||||
Explicit cases can be combined with deterministic `parser-boundaries`,
|
||||
`sanitizer-boundaries`, or `clipboard-boundaries` generation. Supplying a
|
||||
`right` manifest runs both sides with fresh realms per case and reports
|
||||
`equal`, `changed`, or `unknown` comparisons.
|
||||
|
||||
## Boundary and Evidence
|
||||
|
||||
The worker receives module bytes over bounded stdin and runs under a fresh
|
||||
Bubblewrap user/PID/network/IPC/UTS namespace, an architecture-checked seccomp
|
||||
filter, an empty mount root with a descriptor-backed read-only Node closure,
|
||||
private tmpfs, and a transient systemd user cgroup. Host and external network,
|
||||
host writes, process spawning, workers, native addons, inspector access, and
|
||||
ambient environment are unavailable. Wall time, memory, swap, tasks, CPU,
|
||||
case input, worker-protocol input, output, stderr, depth, and node counts are
|
||||
independently bounded. The parent also requires an exact worker response shape
|
||||
and authenticates every returned case ID, order, and input digest. Values are
|
||||
projected through own data descriptors only; Proxy values and accessors are
|
||||
rejected without invoking their traps or getters.
|
||||
|
||||
Returns, exceptions, serialization failures, denials, timeouts, OOMs, crashes,
|
||||
protocol failures, and cleanup state are observations with provider
|
||||
`rea-javascript-replay` and authority `controlled-replay`. An observation does
|
||||
not prove that the original renderer, preload, main process, browser, or remote
|
||||
service behaved identically.
|
||||
|
||||
Each left/right run is retained as its own `observed` source Evidence. A
|
||||
differential envelope is `derived` and links those source Evidence IDs, so the
|
||||
comparison never erases the underlying observations.
|
||||
|
||||
Optional `reproducer_export` is committed by the plan and requires both
|
||||
`approved: true` inside that object and separate `evidence_write` authority.
|
||||
The owner-only manifest is written only after complete sandbox cleanup. Source
|
||||
bytes are excluded unless `include_sources: true` was explicitly approved.
|
||||
An export failure is retained in the result and does not erase a completed
|
||||
replay observation.
|
||||
|
||||
## Local real-artifact verification
|
||||
|
||||
`npm run verify:replay` uses source-owned parser and hostile fixtures. An
|
||||
operator can additionally supply a local ESM or Rspack/CommonJS-factory
|
||||
manifest without copying module source into the repository:
|
||||
|
||||
```sh
|
||||
REA_REPLAY_INPUT_PATH=/absolute/replay-manifest.json npm run verify:replay
|
||||
```
|
||||
|
||||
The verifier canonicalizes the manifest's module paths and prints only plan,
|
||||
module, and Evidence digests, case/comparison summaries, and cleanup state.
|
||||
This is the intended seam for local Notion-like parser/sanitizer benchmarks.
|
||||
@@ -1,59 +1,105 @@
|
||||
# Electron file-page observation
|
||||
|
||||
REA can attach to a user-owned Electron/Chromium CDP endpoint and inspect existing `file://` renderer pages without evaluating JavaScript or invoking Electron APIs. Electron observation is separate from website observation because filesystem roots, not HTTP origins, define its authority.
|
||||
REA can attach to an Electron/Chromium CDP endpoint and inspect existing `file://` renderer pages without evaluating JavaScript or invoking Electron APIs. The request supplies the endpoint directly; REA accepts only literal-port loopback HTTP endpoints. Selecting an endpoint exposes every Electron target it serves, including local file paths and page metadata.
|
||||
|
||||
This passive runtime surface is distinct from the target-free static
|
||||
[`analyze_javascript_application`](javascript-artifact-reconstruction.md)
|
||||
workflow. Static analysis reads an approved ASAR or extracted directory under
|
||||
`REA_INVESTIGATION_INPUT_ROOTS_JSON`; it does not attach to CDP. Runtime
|
||||
observations and static inferences are never silently treated as the same fact.
|
||||
workflow. Static analysis reads a local ASAR or extracted directory without
|
||||
executing application code. Passive observation inspects an existing Electron
|
||||
page. The results keep static inferences and runtime observations distinct.
|
||||
Use the separate
|
||||
[`reconcile_javascript_runtime`](javascript-runtime-reconciliation.md) workflow
|
||||
when both Evidence sets already exist.
|
||||
|
||||
## Configure authority
|
||||
## Target boundary
|
||||
|
||||
The capability is disabled by default:
|
||||
|
||||
```bash
|
||||
export REA_ELECTRON_OBSERVE_ENABLED=true
|
||||
export REA_ELECTRON_CDP_ENDPOINTS_JSON='["http://127.0.0.1:9223"]'
|
||||
export REA_ELECTRON_FILE_ROOTS_JSON='["/Applications/Example.app/Contents/Resources"]'
|
||||
```
|
||||
|
||||
Endpoints accept only explicit-port loopback HTTP URLs. Roots are canonicalized by the shared permission authority. Each target, frame, script, and resource is independently converted from a hostless `file://` URL to a real path and checked after symlink resolution. UNC hosts, percent-encoded path separators, nonexistent paths, and root escapes are rejected.
|
||||
The request supplies a literal-port loopback endpoint and, for inspection, a
|
||||
target ID. REA accepts local hostless `file://` URLs that resolve to regular
|
||||
files; remote hosts, encoded path separators, and nonexistent paths are
|
||||
rejected. The endpoint exposes every eligible target and its local path
|
||||
metadata.
|
||||
|
||||
## Workflow
|
||||
|
||||
`list_electron_targets` returns every eligible target served by the supplied endpoint in one inline array.
|
||||
|
||||
For the MCP follow-up call, pass the same literal-loopback endpoint and the selected target ID to `inspect_electron_page`. REA rediscovers targets and validates the ID against the live endpoint before inspection.
|
||||
|
||||
```bash
|
||||
rea list-electron-targets http://127.0.0.1:9223 --approved --json
|
||||
rea list-electron-targets http://127.0.0.1:9223 --json
|
||||
rea inspect-electron-page http://127.0.0.1:9223 TARGET_ID \
|
||||
--approved --observation-ms 100 --json
|
||||
--observation-ms 100 --json
|
||||
```
|
||||
|
||||
Script content is excluded by default. Capturing it requires both flags and remains subject to per-script and aggregate byte budgets:
|
||||
Script content is excluded by default. Setting `include_script_sources` in the
|
||||
request returns the selected target's script sources:
|
||||
|
||||
```bash
|
||||
rea inspect-electron-page http://127.0.0.1:9223 TARGET_ID \
|
||||
--approved \
|
||||
--include-script-sources \
|
||||
--source-capture-approved \
|
||||
--json
|
||||
```
|
||||
|
||||
The normalized result contains canonical local paths, bounded frame and DOM
|
||||
The normalized result contains canonical local paths, frame and DOM
|
||||
structure, resource metadata, stable script/resource identities, explicit
|
||||
completeness, and content-addressed approved source artifacts. Script metadata
|
||||
completeness, and content-addressed script artifacts. Script metadata
|
||||
retains its execution-context frame ID when CDP supplies one. The capture also
|
||||
inventories authorized worker, service-worker, and shared-worker targets with
|
||||
bounded opener-target and parent-frame IDs. Worker discovery uses passive
|
||||
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.
|
||||
|
||||
The default worker limit is 500 and the caller-visible maximum is 5,000. Like
|
||||
every target, frame, script, and resource, a worker URL must resolve beneath an
|
||||
approved canonical root before it is retained. Relationship IDs improve
|
||||
Collection counts and aggregate script-source bytes are not capped. Like every
|
||||
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.
|
||||
|
||||
Inspection does not retain DOM values, execute renderer code, navigate, click,
|
||||
invoke Electron IPC, close a target, or terminate the application.
|
||||
|
||||
## Active Electron scenarios
|
||||
|
||||
The scenario request supplies the Electron executable and application entry
|
||||
point, plus any arguments and actions. The application root defaults to the
|
||||
entry point's parent directory and can be supplied when the app uses a different
|
||||
root. REA launches that executable through
|
||||
the official Playwright Electron API and owns its lifetime. It accepts click/wait actions,
|
||||
window-targeted renderer reload/crash actions, and synthetic `open-url` or
|
||||
`second-instance` deep-link delivery. It records capture-scoped window and
|
||||
WebContents identities, process metrics, and IPC channel and value-shape metadata.
|
||||
Payload values are not retained.
|
||||
|
||||
Run the real fixture verifier with an operator-provided Electron runtime:
|
||||
|
||||
```bash
|
||||
REA_ELECTRON_EXECUTABLE=/absolute/path/to/electron npm run verify:electron
|
||||
```
|
||||
|
||||
This operation actively launches and interacts with the selected target. It is
|
||||
not a passive CDP observation. The owned process keeps normal host filesystem
|
||||
and network privileges, so lifecycle ownership does not make it a sandbox.
|
||||
|
||||
The CLI and MCP surfaces accept the same schema. For a JSON request file:
|
||||
|
||||
```bash
|
||||
rea capture-electron-scenario scenario.json --json
|
||||
```
|
||||
|
||||
The result records action status and targets, correlated app/window/WebContents,
|
||||
preload, session, navigation, shell, permission, popup, download, protocol,
|
||||
native-addon, process, and IPC timeline events. IPC channels are capped at
|
||||
1,024 characters and argument-shape metadata at 32 entries; 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
|
||||
is explicitly partial when attachment starts after application activity. The
|
||||
owned experiment has an internal 60-second deadline and a 5-second per-action
|
||||
timeout so hung work reaches process cleanup; these are lifecycle timeouts, not
|
||||
caller-selected capture limits.
|
||||
|
||||
Active Electron Evidence can also be supplied to
|
||||
[`reconcile_javascript_runtime`](javascript-runtime-reconciliation.md). That
|
||||
projection is intentionally target-only and partial: it binds the selected
|
||||
application path and capture outcome to the static graph, while frames, scripts,
|
||||
workers, and execution claims remain unavailable until a separate passive runtime
|
||||
capture provides them.
|
||||
|
||||
+32
-321
@@ -9,7 +9,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -23,12 +22,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -48,7 +43,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -62,12 +56,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -87,7 +77,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -101,12 +90,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -126,7 +111,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -140,12 +124,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -165,7 +145,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -179,12 +158,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -204,7 +179,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -218,12 +192,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -243,7 +213,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -257,12 +226,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -304,7 +269,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -318,12 +282,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -343,7 +303,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -357,12 +316,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -382,7 +337,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -396,12 +350,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -413,137 +363,6 @@
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string", "const": "permission_required" },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
},
|
||||
"message": { "type": "string", "minLength": 1 },
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"capability": { "type": "string" },
|
||||
"requested": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"capability": { "type": "string" },
|
||||
"roots": { "type": "array", "items": { "type": "string" } },
|
||||
"executables": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"environment_names": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"origins": { "type": "array", "items": { "type": "string" } },
|
||||
"network": {
|
||||
"type": "string",
|
||||
"enum": ["none", "loopback", "external"]
|
||||
},
|
||||
"mount": { "type": "boolean" }
|
||||
},
|
||||
"required": [
|
||||
"capability",
|
||||
"roots",
|
||||
"executables",
|
||||
"environment_names",
|
||||
"network",
|
||||
"mount"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"missing": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roots": { "type": "array", "items": { "type": "string" } },
|
||||
"executables": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"environment_names": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"origins": { "type": "array", "items": { "type": "string" } },
|
||||
"network": {
|
||||
"type": "string",
|
||||
"enum": ["none", "loopback", "external"]
|
||||
},
|
||||
"mount": { "type": "boolean", "const": true }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ceiling": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"capability": { "type": "string" },
|
||||
"roots": { "type": "array", "items": { "type": "string" } },
|
||||
"executables": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"environment_names": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"origins": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"network": {
|
||||
"type": "string",
|
||||
"enum": ["none", "loopback", "external"]
|
||||
},
|
||||
"mount": { "type": "boolean" }
|
||||
},
|
||||
"required": [
|
||||
"capability",
|
||||
"roots",
|
||||
"executables",
|
||||
"environment_names",
|
||||
"network",
|
||||
"mount"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{ "type": "null" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["capability", "requested", "missing", "ceiling"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -552,7 +371,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -566,12 +384,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -591,7 +405,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -605,12 +418,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -630,7 +439,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -644,51 +452,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string", "const": "outside_approved_root" },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
},
|
||||
"message": { "type": "string", "minLength": 1 },
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -708,7 +473,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -722,12 +486,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -747,7 +507,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -761,12 +520,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
@@ -786,7 +541,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
@@ -800,51 +554,8 @@
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
"additionalProperties": { "$ref": "#/$defs/__schema0" }
|
||||
}
|
||||
},
|
||||
"required": ["code", "category", "message", "retryable", "remediation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string", "const": "plan_stale" },
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"invalid_input",
|
||||
"permission_required",
|
||||
"unsupported_provider",
|
||||
"integrity_mismatch",
|
||||
"truncated",
|
||||
"cancelled",
|
||||
"timeout",
|
||||
"unavailable",
|
||||
"execution_failure"
|
||||
]
|
||||
},
|
||||
"message": { "type": "string", "minLength": 1 },
|
||||
"retryable": { "type": "boolean" },
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": { "type": "string", "minLength": 1 },
|
||||
"restart_required": { "type": "boolean" },
|
||||
"elicitation_supported": { "type": "boolean" }
|
||||
},
|
||||
"required": ["action", "restart_required"],
|
||||
"properties": { "action": { "type": "string", "minLength": 1 } },
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"details": {
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Firmware analysis
|
||||
|
||||
REA accepts **Binwalk 3.1.x** for region inspection and **Unblob 26.6.x** for
|
||||
explicit extraction. The report parsers are verified with Binwalk 3.1.0 and
|
||||
Unblob 26.6.4. Another build on those lines is accepted, the observed version
|
||||
is reported, and a limitation records that the parser was not verified against
|
||||
that build. This first firmware milestone is verified on Linux x64.
|
||||
No device, root privilege, mount, emulation or execution of extracted programs
|
||||
is needed. Arbitrary firmware bytes do not pass native executable admission;
|
||||
select a returned ELF separately for supported Ghidra analysis.
|
||||
|
||||
## Caller-supplied tools
|
||||
|
||||
Provide existing tools using absolute executable paths, scoped to your shell:
|
||||
|
||||
```sh
|
||||
export REA_BINWALK_COMMAND=/absolute/path/to/binwalk
|
||||
export REA_UNBLOB_COMMAND=/absolute/path/to/unblob
|
||||
```
|
||||
|
||||
REA does not install these tools, their dependencies or system packages. It
|
||||
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.
|
||||
|
||||
Unblob needs the external extractor for the selected format on its inherited
|
||||
`PATH`: for example, `debugfs` for ext2/ext3/ext4. Its default Landlock sandbox
|
||||
and skip rules stay enabled. Sandbox startup failures remain actionable errors;
|
||||
REA does not retry with `--no-sandbox`. Missing extractors reported in a valid
|
||||
analysis report retain partial results and dependency names inline.
|
||||
|
||||
Upstream sources are preserved unmodified as optional pinned submodules:
|
||||
|
||||
```sh
|
||||
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).
|
||||
|
||||
## CLI and MCP
|
||||
|
||||
```sh
|
||||
rea inspect-firmware-regions /path/to/firmware.bin
|
||||
rea extract-firmware /path/to/firmware.bin /path/to/new-output
|
||||
rea extract-firmware /path/to/firmware.bin /path/to/selected-output \
|
||||
--offset 64 --length 4096 --max-depth 3
|
||||
```
|
||||
|
||||
The corresponding MCP tools accept the same intent:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_firmware_regions",
|
||||
"arguments": { "path": "/path/to/firmware.bin" }
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "extract_firmware",
|
||||
"arguments": {
|
||||
"path": "/path/to/firmware.bin",
|
||||
"output_directory": "/path/to/new-output",
|
||||
"range": { "offset": 64, "length": 4096 },
|
||||
"max_depth": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Both return Evidence with the original path and SHA-256, raw producer reports,
|
||||
actual executable path/digest, version and limitations. Binwalk signature UUIDs
|
||||
and Unblob blob IDs are producer-local identities; compare content hashes and
|
||||
source ranges across runs. A matching version does not attest an installed
|
||||
build's source revision, so that field remains unknown.
|
||||
|
||||
### Interpret the results
|
||||
|
||||
- Region offsets refer to bytes in the original firmware. `reported_size`
|
||||
preserves Binwalk's output; `size_basis=provider_reported_validation_unknown`
|
||||
reflects that Binwalk can replace unknown lengths with the next signature or
|
||||
EOF before serialization. Neither a signature nor its confidence byte proves
|
||||
a valid partition, encryption or runtime behavior.
|
||||
- Extraction returns regular file paths, independently verified SHA-256 values,
|
||||
producer hashes when available, chunks, diagnostics and task derivations.
|
||||
Unblob's task report is unordered; REA resolves relationships by paths and
|
||||
blob IDs. Logical references use `$input`, `$output`, or `$output/<relative-path>`
|
||||
so a real file named `$input` stays distinct from the original input.
|
||||
Chunk offsets belong to the task's input file. Only chunks in the
|
||||
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,
|
||||
unpublished links/special files or reached depth limits. `complete` means 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.
|
||||
- The destination must be absent, absolute, with an existing parent. Publication
|
||||
uses exclusive, symlink-resistant writes and durable digest readback. Existing
|
||||
destinations are never overwritten. This is not an atomic directory rename:
|
||||
a new tree can be visible while files are written. Failed uncommitted output
|
||||
is rolled back. Symlinks and special files are recorded without publication;
|
||||
empty directories, ownership and executable permission bits are not reproduced.
|
||||
|
||||
Select a verified native child for the existing tools:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "open_binary",
|
||||
"arguments": {
|
||||
"path": "/path/from/extraction/usr/bin/program",
|
||||
"provider_id": "ghidra"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then use `analyze_function`, `inspect_native_api` or the other native primitives.
|
||||
Native architecture and executable validation still apply. This milestone does
|
||||
not add MIPS/PPC/RISC-V decompilation, raw flash load profiles, bootloader/device
|
||||
tree semantics, vulnerability scanning, emulation or hardware runtime capture.
|
||||
|
||||
## Resource and lifecycle policy
|
||||
|
||||
- Nonempty regular firmware inputs: at most **128 MiB**, copied and hashed into
|
||||
a private workspace before analysis. No in-place source modification.
|
||||
- One queued heavy operation per provider instance; Binwalk one thread and
|
||||
Unblob one worker. No automatic parallel extraction engines.
|
||||
- **120-second** deadline; inherited **1 GiB per-process address-space** limit,
|
||||
120 CPU seconds and zero core-dump allowance through `prlimit`.
|
||||
- JSON reports: at most **8 MiB**; oversized reports fail without a truncated
|
||||
success. 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.
|
||||
|
||||
Staging budgets are polled and rechecked after exit; short-lived overshoot is
|
||||
possible. 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
|
||||
before removing its workspace; uncertain cleanup retains resources and blocks
|
||||
further operations in that provider instance.
|
||||
|
||||
## Verification
|
||||
|
||||
Generate small public, source-derived fixtures into ignored `_reference/`:
|
||||
|
||||
```sh
|
||||
npm run fixtures:firmware
|
||||
npm run verify:firmware
|
||||
```
|
||||
|
||||
Fixture generation requires existing Python 3 and a host C compiler (`cc`);
|
||||
override with `REA_FIRMWARE_PYTHON` / `REA_FIRMWARE_CC`. The independent oracle
|
||||
records gzip offset/length and extracted file hashes. Real verification checks
|
||||
CLI/MCP, full-file and selected-range extraction, unknown chunks, depth limits,
|
||||
content parity and unchanged source bytes. No binary fixture is committed.
|
||||
|
||||
Additional lanes are explicit, so the base lane stays small:
|
||||
|
||||
```sh
|
||||
# Existing mke2fs and debugfs required; put debugfs on the scoped PATH.
|
||||
REA_FIRMWARE_VERIFY_EXT4=1 npm run fixtures:firmware
|
||||
REA_FIRMWARE_VERIFY_EXT4=1 npm run verify:firmware
|
||||
|
||||
# Existing Ghidra 12.1.x and its declared JDK required.
|
||||
REA_FIRMWARE_VERIFY_GHIDRA=1 npm run verify:firmware
|
||||
```
|
||||
|
||||
`REA_FIRMWARE_MKE2FS_COMMAND` selects the optional ext4 fixture producer.
|
||||
`REA_FIRMWARE_FIXTURE_ROOT` selects generated data storage.
|
||||
`REA_FIRMWARE_TEST_ENTRYPOINT` can select an isolated installed npm package's
|
||||
`scripts/rea.mjs` to verify the packaged CLI/MCP workflow. Real coverage here is
|
||||
generated gzip/USTAR/ext4 with a Linux x64 ELF child; SquashFS, UBIFS, proprietary
|
||||
firmware, other hosts and architectures remain unverified.
|
||||
@@ -0,0 +1,188 @@
|
||||
# DOS MZ and COM analysis with Ghidra
|
||||
|
||||
REA can analyze DOS MZ executables and explicitly selected COM images through the bring-your-own Ghidra adapter.
|
||||
The Linux x64 verification lane exercises actual 16-bit disassembly and
|
||||
decompilation through the CLI and stdio MCP. Provider admission accepts Ghidra
|
||||
12.1.x and that installation's declared JDK range. The verification lane uses
|
||||
Ghidra 12.1.4 and JDK 21; no DOS emulator or cross-compiler is required.
|
||||
|
||||
## Open and inspect
|
||||
|
||||
```bash
|
||||
rea inspect /absolute/path/to/legacy.exe --provider ghidra --json
|
||||
rea search /absolute/path/to/legacy.exe entry --kind procedures --provider ghidra --json
|
||||
rea instructions /absolute/path/to/legacy.exe entry --provider ghidra --json
|
||||
rea function /absolute/path/to/legacy.exe entry --provider ghidra --json
|
||||
rea inspect-native-load-image /absolute/path/to/legacy.exe --provider ghidra --json
|
||||
rea read-bytes /absolute/path/to/legacy.exe 0x10000 --length 16 --provider ghidra --json
|
||||
rea address-to-file-offset /absolute/path/to/legacy.exe 0x10000 --provider ghidra --json
|
||||
```
|
||||
|
||||
Use an observed procedure name or address from discovery when the target does
|
||||
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
|
||||
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.
|
||||
|
||||
Ghidra imports a private target snapshot with `MzLoader`, language
|
||||
`x86:LE:16:Real Mode`, and compiler specification `default`. These settings,
|
||||
the fixed Ghidra load segment `0x1000`, and the linear address convention are
|
||||
committed in the analysis profile. The handshake rejects language or compiler
|
||||
drift. Closing the session deletes the temporary project and snapshot.
|
||||
|
||||
The profile also commits `function_body_evidence: complete-inclusive-ranges-v1`,
|
||||
so snapshots from older length-only Ghidra results do not satisfy the current
|
||||
profile.
|
||||
|
||||
## Headerless COM images
|
||||
|
||||
COM has no identifying header. Select its interpretation explicitly; neither the
|
||||
`.com` suffix nor arbitrary unrecognized bytes are enough to admit it.
|
||||
|
||||
```bash
|
||||
rea inspect /absolute/path/to/legacy.com --target-format dos-com --provider ghidra --json
|
||||
rea function /absolute/path/to/legacy.com 0x10100 --target-format dos-com --provider ghidra --json
|
||||
rea inspect-native-load-image /absolute/path/to/legacy.com --target-format dos-com --provider ghidra --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "open_binary",
|
||||
"arguments": {
|
||||
"path": "/absolute/path/to/legacy.com",
|
||||
"format": "dos-com",
|
||||
"provider_id": "ghidra"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The caller's interpretation applies to the complete file, even if its filename
|
||||
suggests another format. Files must contain 1..65280 bytes: the 64 KiB COM segment
|
||||
minus the 256-byte PSP prefix. Ghidra uses `BinaryLoader` with real-mode language
|
||||
and maps file offset zero at `1000:0100` (linear `0x10100`). A packaged pre-analysis
|
||||
script seeds the entry function and CS/DS/ES/SS context `0x1000`. These settings
|
||||
and the preparation policy are committed in the analysis profile; a failed
|
||||
preparation prevents the resident bridge from serving results.
|
||||
|
||||
Load-image verification checks all original and modified file bytes, complete
|
||||
source mapping/memory digests, absence of relocations, entry and measured register
|
||||
context. The context is imposed for analysis, not observed from execution. PSP
|
||||
memory, initial stack contents/SP, DOS interrupts and PC-98 devices are not
|
||||
modeled. COM has no MZ header, relocation table or appended-overlay model; all
|
||||
selected bytes belong to its analysis module. The original file stays unchanged.
|
||||
|
||||
## Addresses and function extent
|
||||
|
||||
Returned default-space addresses are linear byte coordinates. A Ghidra
|
||||
segment:offset address has linear value `segment * 16 + offset`; multiple
|
||||
segment aliases can identify the same byte. REA accepts the returned linear
|
||||
address for subsequent lookups and leaves instruction decoding context with
|
||||
Ghidra. These coordinates are analysis load addresses, not file offsets or
|
||||
observations of a running DOS system.
|
||||
|
||||
`procedure_info` and function dossiers include `body` evidence:
|
||||
|
||||
```json
|
||||
{
|
||||
"available": true,
|
||||
"provenance": "ghidra-function-body-address-set",
|
||||
"ranges": [
|
||||
{ "start": "0x10000", "end": "0x1000d" },
|
||||
{ "start": "0x10060", "end": "0x10065" }
|
||||
],
|
||||
"total_bytes": 20,
|
||||
"span_bytes": 102,
|
||||
"non_contiguous": true,
|
||||
"contains_entry": true
|
||||
}
|
||||
```
|
||||
|
||||
Each end is **inclusive**. `total_bytes` counts the complete observed
|
||||
AddressSet; `span_bytes` measures its enclosing span and is null across address
|
||||
spaces. Gaps and shared tails must not be treated as owned bytes. This is
|
||||
Ghidra's database observation, not a proof of original source ownership or
|
||||
exhaustive code discovery. Providers that do not report complete ranges return
|
||||
`available: false` and an explicit reason.
|
||||
|
||||
## Loaded-image evidence
|
||||
|
||||
`inspect_native_load_image` independently checks the immutable session snapshot
|
||||
against the loaded Ghidra Program. It measures complete original and modified
|
||||
FileBytes digests, every source mapping and its memory digest, relocation records
|
||||
(including multiplicity), and external entry points. It verifies MZ header and
|
||||
module partitions, relocation fixups and linear coordinates rather than relying
|
||||
on the imported executable SHA alone.
|
||||
|
||||
The result is `verified`, `mismatch`, or `unsupported`. Every check retains its
|
||||
expected and observed values. A memory-digest mismatch identifies the source
|
||||
range's file offset and analysis address; it does not locate the first differing
|
||||
byte inside that range. Mapping ends are **inclusive**, unlike the exclusive ends
|
||||
from `list_segments`. Appended overlays remain in source-file identity and are
|
||||
excluded from initialized module coverage. Uninitialized allocations are reported;
|
||||
their contents and DOS runtime semantics are not verified. Formats other than MZ and explicit COM return
|
||||
`unsupported` with measured observations, not a successful MZ verification.
|
||||
|
||||
`read_bytes` returns initialized provider memory, including loader fixups; an
|
||||
unmapped or uninitialized byte ends the read with an explicit returned length and
|
||||
`complete: false`. `address_to_file_offset` uses the provider's source mapping;
|
||||
unmapped, uninitialized and ambiguous mappings fail explicitly. A relocated word
|
||||
can differ from the original file word even when its source offset is known.
|
||||
|
||||
## Coverage boundaries
|
||||
|
||||
Static decompilation does not execute the target or emulate BIOS, DOS
|
||||
interrupts, device ports, or self-modifying code. PC-98 hardware behavior and
|
||||
recovered calling conventions can remain uncertain; provider pseudocode and
|
||||
diagnostics are retained for analyst review.
|
||||
|
||||
For a packed executable, importing the original can analyze its unpacking
|
||||
stub. Analyze a separately prepared unpacked copy to inspect the original
|
||||
program's recovered code. Retain both artifact digests and the transformation
|
||||
evidence; REA never silently replaces the admitted target. Appended overlays
|
||||
are separate from the initialized MZ module. Raw COM files and DOS extenders
|
||||
without an admitted MZ real-mode entry are outside this import boundary.
|
||||
|
||||
DOS MZ is available through Ghidra, with no Hopper support claim. It is outside
|
||||
the Windows native PE P0 boundary. macOS DOS verification remains unverified.
|
||||
|
||||
## Real verification
|
||||
|
||||
```bash
|
||||
npm run verify:ghidra:dos
|
||||
```
|
||||
|
||||
The lane generates a small public fixture from source at runtime. It verifies
|
||||
16-bit instruction bytes, a near call, a segment-relocated far call, actual
|
||||
decompilation, disjoint function ranges, stable CLI/MCP observations, unchanged
|
||||
input bytes, and session/process cleanup. It does not require external
|
||||
executable fixtures, an emulator, or an existing analysis database.
|
||||
|
||||
Provider p-code representations can contain process-specific address-space selector
|
||||
tokens. The lane preserves those reported values and compares stable dossier
|
||||
observations rather than asserting cross-process identity of raw p-code.
|
||||
|
||||
## Function annotations
|
||||
|
||||
`annotate_native_function` changes the name and/or comments at one exact local function entry, verifies readback, and returns a refreshed dossier. This is useful when recording recovered DOS function roles; it does not claim original-source names. Name writes preserve the existing namespace; readback returns the fully qualified name used by function inventories. Regular comments map to Ghidra PRE comments and inline comments to EOL comments. Empty text clears a comment; omitted fields preserve it. The whole edit rolls back if a setter, readback or analysis fails.
|
||||
|
||||
```json
|
||||
{
|
||||
"procedure": "0x10100",
|
||||
"name": "entry",
|
||||
"comment": "Recovered startup role",
|
||||
"inline_comment": "Inspect segment setup"
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
rea annotate-native-function /local/program.com 0x10100 --target-format dos-com --provider ghidra --name entry --comment 'Recovered startup role' --json
|
||||
```
|
||||
|
||||
The CLI returns the updated analysis before closing its isolated session. MCP edits remain visible to later reads until `close_binary`. Original bytes remain unchanged, database edits are discarded on close, and immutable analysis snapshots are invalidated after edits. This operation does not save a Ghidra project, change ABI/types/body ranges, patch instructions, or emulate a DOS runtime. Windows P0 does not admit it.
|
||||
@@ -0,0 +1,102 @@
|
||||
# Optional NativeAOT metadata recovery
|
||||
|
||||
REA integrates [Washi1337/ghidra-nativeaot](https://github.com/Washi1337/ghidra-nativeaot)
|
||||
through an optional headless adapter. The original source is an opt-in submodule,
|
||||
pinned to `effeb734fc570c32650f88b159608979dc7b423e`; its MIT license and existing
|
||||
source notices are retained. No upstream source or algorithms are rewritten.
|
||||
|
||||
The initial tested layout is .NET runtime **8.0.22**, NativeAOT RTR **9.1**,
|
||||
x86-64 ELF and native Windows PE **targets analyzed on Linux x64**, using
|
||||
Ghidra **12.1.4** and JDK **21**. The build accepts any Ghidra 12.1.x release and
|
||||
javac 21 or newer, and it compiles with `--release 21`. Real verification of
|
||||
this layout remains the Ghidra 12.1.4 and JDK 21 pair. Other NativeAOT layouts,
|
||||
architectures, and hosts are unsupported.
|
||||
Windows Ghidra P0 has no database mutation authority. A PE/CLI or ReadyToRun
|
||||
assembly should use `inspect_managed_artifact`; it is not NativeAOT.
|
||||
|
||||
## Build and configure
|
||||
|
||||
Bring your own supported Ghidra/JDK installation. This developer build needs
|
||||
existing `git`, `javac` and `jar`; it does not invoke Gradle, download toolchains,
|
||||
or change host configuration:
|
||||
|
||||
```sh
|
||||
git submodule update --init third_party/ghidra-nativeaot
|
||||
npm run build:ghidra:nativeaot
|
||||
export REA_GHIDRA_NATIVEAOT_JAR="$PWD/_reference/nativeaot-integration/extension/rea-ghidra-nativeaot.jar"
|
||||
```
|
||||
|
||||
The build requires a clean pinned upstream checkout, compiles unchanged upstream
|
||||
Java plus the REA facade, uses a fresh classes directory, and writes the JAR and
|
||||
build manifest under ignored `_reference/`. `REA_NATIVEAOT_BUILD_ROOT` can select
|
||||
another local output directory; `REA_NATIVEAOT_SOURCE` can select an equivalent
|
||||
clean pinned source checkout. Neither output nor compiled fixture belongs in Git
|
||||
or the npm package. An installed REA accepts an existing explicitly supplied JAR.
|
||||
|
||||
REA commits the actual JAR SHA-256 into the analysis profile, copies identical
|
||||
bytes into the owned runtime, rechecks the digest before loading, and closes the
|
||||
classloader/project on session close. Profiles cannot enable code absent from
|
||||
explicit local configuration. JARs execute within Ghidra; supply a build you trust.
|
||||
The producer's reported source revision is build metadata, not an attestation of
|
||||
arbitrary caller-supplied bytes. The bounded loader accepts regular JARs up to
|
||||
8 MiB to limit startup artifact memory; this does not truncate analysis results.
|
||||
|
||||
## Use existing tools
|
||||
|
||||
Open the executable with provider `ghidra`. `inspect_native_load_image` returns
|
||||
optional `observations.metadata_recovery` inline, including the detected header,
|
||||
layout, recovery status, type category paths/addresses, diagnostics, annotation
|
||||
coverage and derived-memory SHA. The enclosing `unsupported` load-image status
|
||||
for PE/ELF concerns independent DOS loader verification; metadata recovery has
|
||||
its own status. It is not a failed import.
|
||||
|
||||
Pass one returned type path or its typed address to `inspect_native_data_type`.
|
||||
Its optional `metadata_recovery` includes related/base type, implemented
|
||||
interfaces and virtual slots with targets and known procedure names. Follow
|
||||
these with `analyze_function`, string searches and ordinary cross-references.
|
||||
For CLI use:
|
||||
|
||||
```sh
|
||||
rea inspect-native-load-image ./NativeAotFixture --provider ghidra --json
|
||||
rea inspect-native-data-type ./NativeAotFixture --type /NativeAOT/MethodTables/Class_ADDRESS_MT --provider ghidra --json
|
||||
rea function ./NativeAotFixture 0xADDRESS --provider ghidra --json
|
||||
```
|
||||
|
||||
No new MCP workflow or approval flag is required. Configuration opts into the
|
||||
adapter; without it ordinary Ghidra profiles/results remain unchanged.
|
||||
|
||||
## Evidence boundaries
|
||||
|
||||
Discovery prefers an imported RTR symbol, otherwise the upstream initialized,
|
||||
non-executable-memory signature heuristic. REA rejects ambiguous candidates,
|
||||
unknown layout versions, malformed directory rows, unsupported flags and
|
||||
unmapped ranges before recovery. It requires the verified dehydrated layout.
|
||||
|
||||
The adapter performs rehydration, method-table recovery and frozen-object
|
||||
annotation in one ephemeral Program transaction. Fatal failure/cancellation
|
||||
rolls it back. Recovery may change analysis-memory bytes, never the original
|
||||
executable file. Derived ranges have a digest and **no original file offset**;
|
||||
they are not captured runtime state. Candidate/committed instance annotation
|
||||
counts distinguish partial frozen-object coverage. Coverage concerns the
|
||||
recovered candidate set, not every possible object or every runtime type.
|
||||
|
||||
REA preserves pre-recovery calling conventions and uses the loaded compiler
|
||||
specification default for newly created methods. The upstream universal
|
||||
`__thiscall` assignment is not treated as x64 ABI authority. Parameters and
|
||||
method prototypes remain inferences; verify them against instructions/call sites.
|
||||
|
||||
Type relationships and System.Object/System.String identification use upstream
|
||||
heuristics. Generated names are explicitly marked; original class/member names
|
||||
and custom field layouts remain unknown. Pseudocode is recovered native logic,
|
||||
not original C# source. Unsupported recovery reports its stage and reason; it
|
||||
does not imply that ordinary native disassembly/decompilation is impossible.
|
||||
|
||||
## Verification
|
||||
|
||||
See [the dedicated lane](testing.md#optional-nativeaot-ghidra-analysis).
|
||||
Fixtures are source-built benign .NET programs with inheritance, an interface,
|
||||
virtual dispatch, representative arithmetic and frozen strings. Independent
|
||||
compiler/linker symbols and raw directory bytes provide the oracle. Stripped
|
||||
fixtures exercise heuristic detection. Tests do not execute the resulting
|
||||
NativeAOT binaries. Fixture generation and adapter compilation are optional and
|
||||
never prerequisites of ordinary native analysis or setup.
|
||||
@@ -0,0 +1,111 @@
|
||||
# IDA Pro provider
|
||||
|
||||
REA adapts an existing [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) server into its native analysis tools. Install and configure IDA, Hex-Rays, Python, and upstream MCP using that repository's instructions. REA does not install, upgrade, activate, or license them, and does not bundle another IDA server.
|
||||
|
||||
Choose one lifecycle mode in an upstream MCP JSON registration. Set `REA_IDA_MCP_CONFIG` to that file, then select `ida` once through `REA_ANALYSIS_PROVIDER`, the CLI's `--provider ida`, or MCP's `open_binary.provider_id`. No provider startup occurs during discovery.
|
||||
|
||||
## Analyze an already-open GUI target
|
||||
|
||||
The attached profile supports upstream **1.4.0 legacy tools**, including `get_metadata`, `list_functions`, and `decompile_function`. Copy the upstream registration's `command`, `args`, and optional `env` into a JSON file. An existing `mcpServers` object with an `ida-pro-mcp` entry is also accepted. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "python",
|
||||
"args": [
|
||||
"/absolute/path/to/ida_pro_mcp/server.py",
|
||||
"--ida-rpc",
|
||||
"http://127.0.0.1:13337"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use the executable and arguments from your working upstream registration. The legacy GUI's `/mcp` endpoint is its internal RPC endpoint; connect REA through the upstream stdio proxy rather than treating that URL as Streamable HTTP.
|
||||
|
||||
```bash
|
||||
export REA_IDA_MCP_CONFIG=/absolute/path/to/ida-mcp.json
|
||||
export REA_ANALYSIS_PROVIDER=ida
|
||||
rea function /absolute/path/to/program main --json
|
||||
rea decompile /absolute/path/to/program main --json
|
||||
```
|
||||
|
||||
Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its SHA-256 must match IDA's recorded input hash. REA checks the attached identity and image base around each analysis request. A mismatch or target switch fails with recovery guidance. The original input must remain readable by REA; WSL can use its mounted alias while the GUI runs on Windows.
|
||||
|
||||
REA's close operation releases its MCP connection or proxy. It never saves or closes the existing GUI database. External edits remain visible because results are live and are not replayed from snapshots. These checks do not lock the GUI or prove that the database is unpatched.
|
||||
|
||||
## Give REA a target for headless analysis
|
||||
|
||||
The headless profile supports upstream's **database supervisor** API: `idb_open`, `idb_list`, `idb_close`, explicit `database` arguments, and modern analysis tools. Legacy 1.4.0 `idalib-mcp`, which accepts one initial filename and serves SSE, does not implement this profile. Follow the [upstream headless instructions](https://github.com/mrexodia/ida-pro-mcp#headless-idalib-session-model) for an installation with the supervisor API.
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "idalib-mcp",
|
||||
"args": ["--stdio", "--max-workers", "1"],
|
||||
"mode": "headless"
|
||||
}
|
||||
```
|
||||
|
||||
Use an already-installed executable. When required by your existing idalib installation, include `"env": { "IDADIR": "/absolute/path/to/ida" }`. REA neither edits the global activation configuration nor installs dependencies. A configured local Streamable HTTP supervisor can instead use `"url": "http://127.0.0.1:8745/mcp"` and `"mode": "headless"`.
|
||||
|
||||
The same CLI commands work in both modes. For a Windows-native REA process:
|
||||
|
||||
```powershell
|
||||
$env:REA_IDA_MCP_CONFIG = 'D:\analysis\ida-mcp.json'
|
||||
$env:REA_ANALYSIS_PROVIDER = 'ida'
|
||||
rea function 'D:\samples\program.exe' main --json
|
||||
```
|
||||
|
||||
Headless REA and the configured supervisor must share the host filesystem and native path syntax. Run REA on Windows when the supervisor runs on Windows. The attached profile can span WSL and a Windows GUI because it binds recorded input hashes instead of asking the GUI to open a WSL path.
|
||||
|
||||
REA copies the admitted binary into a private temporary directory, checks its digest, and requests an owned worker with `force_headless`. POSIX workspaces use mode `0700`; 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.
|
||||
|
||||
Headless cleanup uses the upstream database lifecycle. Abruptly terminating REA, its proxy, or the supervisor does not establish worker release. Prefer `close_binary` for MCP sessions; after an interrupted process, inspect upstream session inventory and retained workspace diagnostics before removing working files. This adapter does not claim Windows Job Object containment for the upstream detached workers.
|
||||
|
||||
## Connect an agent once
|
||||
|
||||
Add the registration path to the **REA** MCP entry's environment, keeping the upstream registration in its separate JSON file:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@<version-with-ida>", "mcp"],
|
||||
"env": {
|
||||
"REA_ANALYSIS_PROVIDER": "ida",
|
||||
"REA_IDA_MCP_CONFIG": "/absolute/path/to/ida-mcp.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use a package version that actually includes this adapter; repository main can be ahead of the npm release. After reconnecting, call:
|
||||
|
||||
```json
|
||||
{"name":"open_binary","arguments":{"path":"/absolute/path/to/program","provider_id":"ida"}}
|
||||
{"name":"analyze_function","arguments":{"procedure":"main"}}
|
||||
{"name":"search_strings","arguments":{"pattern":"license"}}
|
||||
{"name":"close_binary","arguments":{}}
|
||||
```
|
||||
|
||||
Opening an MCP target starts its selected provider session; headless opening waits for auto-analysis. Set the agent's MCP request deadline long enough for the target. The registration's optional `timeoutMs` defaults to 300000 for upstream requests. Cancellation drains the current upstream request before cleanup; it does not force-stop GUI analysis. An open request that times out without a completed response retains the private workspace because worker inventory alone cannot prove the request has stopped. `binary_session` reports actual tool availability. Repeated doctor/setup calls are unnecessary when the registration already works.
|
||||
|
||||
For guided registration, set `REA_IDA_MCP_CONFIG` before `rea setup --client <client>`. Setup includes that file reference in its reviewed plan and preserves it after approval; it does not copy upstream credentials into the agent configuration. `rea doctor --provider ida --json` validates the registration without starting IDA and explicitly leaves runtime compatibility and target binding for analysis.
|
||||
|
||||
## Coverage and evidence
|
||||
|
||||
Both profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
|
||||
|
||||
The legacy profile additionally supports direct callers. Its producer may report call-site or interior addresses; REA resolves the reported function symbol to its canonical entry. The modern profile does not prove direct callers from its code/data xref labels, so that capability is unavailable and the dossier marks it unknown.
|
||||
|
||||
Complete body ranges, typed reference edges, CFG, referenced data, source locals, comment kinds, and database revision are unavailable. Empty unsupported dossier facets mean **unknown**, not observed absence. `binary_overview`, whole-symbol inventory, mutation, debugging, arbitrary Python forwarding, and GUI navigation are outside this adapter's coverage. Check session availability before composing a workflow that needs those facets.
|
||||
|
||||
`provider.version` identifies the REA adapter; the profile explicitly records `version_scope: "rea-ida-adapter"`. The MCP handshake's `serverInfo` is separate and does not establish IDA or Python distribution versions. Raw observations preserve producer metadata and output. Attached hashes identify the recorded original input; headless identity uses the digest-verified private copy and observed worker input path. Neither proves source-code recovery or an unmodified analysis database.
|
||||
|
||||
## Verification
|
||||
|
||||
Run `npm run verify:ida -- --target /absolute/path/to/program --procedure main` with a configured upstream server. It exercises production CLI and MCP tools, validates their output contracts and Evidence, checks original-input preservation and session cleanup, and keeps local target data out of its console summary. Supply `--package-root` to verify an extracted or installed package. See [testing](testing.md) for the distinction between adapter tests and real-provider evidence.
|
||||
|
||||
The initial real workflows use stdio registrations for the legacy Windows GUI profile and a Windows x64 headless supervisor with IDA 9.3. Streamable HTTP is tested against a loopback MCP fixture with the pinned client SDK; a real IDA HTTP-supervisor workflow remains unverified. Linux/macOS headless engine operation, other IDA releases, architectures, and modern attached GUI tools require their corresponding real workflows before support is claimed.
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: REA
|
||||
text: Reverse Engineer Anything
|
||||
tagline: Understand binaries, applications, and runtime behavior with one CLI and MCP server.
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Get started
|
||||
link: /installation
|
||||
- theme: alt
|
||||
text: View on GitHub
|
||||
link: https://github.com/morluto/rea
|
||||
|
||||
features:
|
||||
- title: Investigate with your agent
|
||||
details: Connect REA to your coding agent, describe the app or feature you want to understand, and follow the evidence.
|
||||
link: /mcp-prompts
|
||||
- title: Inspect across formats
|
||||
details: Explore native binaries, JavaScript and Electron apps, .NET assemblies, Android packages, firmware, and websites.
|
||||
link: /installation
|
||||
- title: Keep the evidence
|
||||
details: Analysis runs locally and returns observations, limitations, and unknowns through the CLI and MCP.
|
||||
link: /mcp-contracts
|
||||
---
|
||||
|
||||
## Start with one command
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
Choose your agent, review the planned changes, and approve setup. Native binary
|
||||
analysis can use an existing Hopper, Ghidra, or IDA installation; static
|
||||
JavaScript analysis needs no native analysis engine.
|
||||
|
||||
For a first result from an extracted JavaScript application or ASAR:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
See [installation and setup](./installation.md) for requirements and configuration.
|
||||
|
||||

|
||||
+269
-108
@@ -4,24 +4,27 @@ REA separates installing its CLI from configuring external software and agents.
|
||||
|
||||
## Start setup
|
||||
|
||||
The recommended setup entrypoint is:
|
||||
Start setup with:
|
||||
|
||||
```bash
|
||||
npx rea-agents setup
|
||||
```
|
||||
|
||||
If npm asks to download and run the package, that approval applies only to the
|
||||
current package-runner invocation. REA still prints its own setup plan and asks
|
||||
for separate approval before changing agent configuration or installing a
|
||||
product-owned component.
|
||||
If npm asks to download and run REA, that approval applies only to downloading
|
||||
the package. REA shows its own plan and asks before changing agent configuration
|
||||
or installing Hopper.
|
||||
|
||||
When bare `npx` resolves `rea-agents` from the current project's
|
||||
`node_modules`, the selected dispatcher restarts setup through
|
||||
`rea-agents@latest` before REA plans or writes configuration. This protects
|
||||
current and future releases from silently persisting stale integration state.
|
||||
Code in an already-published older release cannot acquire that check
|
||||
retroactively; invoke `npx -y rea-agents@latest setup` once to repair any
|
||||
registration it created.
|
||||
The short command can use a REA version installed in the current project. To
|
||||
request the latest release explicitly, use:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
```
|
||||
|
||||
REA runs the version npm selects. To update older agent registrations, run the
|
||||
latest-version command and review its setup plan. For unattended package
|
||||
downloads, add `--yes` before the package name; this does not approve REA's
|
||||
setup changes.
|
||||
|
||||
For an intentional rollback, make the package request explicit:
|
||||
|
||||
@@ -33,12 +36,12 @@ Setup continues to pin persistent MCP registrations to the exact version that
|
||||
performed setup. Running current setup later migrates unversioned or older
|
||||
managed registrations through the normal reviewed setup transaction.
|
||||
|
||||
REA supports Node.js 22.19+ and 24.11+ (including newer releases). It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
|
||||
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.
|
||||
|
||||
Running `npm install rea-agents` without `--global` installs the executable only
|
||||
in the current project's `node_modules/.bin`; it does not make `rea` available
|
||||
on the shell `PATH`. Use `npx rea-agents setup` for the guided setup journey,
|
||||
`npx -y rea-agents@latest` for unattended one-off commands, or install globally
|
||||
on the shell `PATH`. Use the setup command above for the guided setup
|
||||
journey, `npx -y rea-agents@latest` for unattended one-off commands, or install globally
|
||||
with `npm install --global rea-agents` for a shell-visible `rea` command.
|
||||
|
||||
The optional curl wrapper installs only the global npm package:
|
||||
@@ -58,60 +61,121 @@ curl -fsSL https://raw.githubusercontent.com/morluto/rea/main/install.sh |
|
||||
|
||||
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.
|
||||
|
||||
## Released package and main
|
||||
|
||||
Repository main documents its current code and generated catalog. `@latest`
|
||||
selects the npm release, and persistent MCP registrations are pinned to the
|
||||
version that performed setup. Installing newer instructions does not update a
|
||||
running server or its registration.
|
||||
|
||||
The release checked on **2026-10-06** was **4.1.0** (125 MCP tools). That artifact
|
||||
includes main's Windows native control bundle, Android/JADX and firmware
|
||||
tools, Ghidra function annotation workflow, and retained application-Evidence
|
||||
references.
|
||||
Main's catalog describes the current code; a source build or a subsequent
|
||||
release containing later changes is required for anything newer. Package startup alone does not verify a
|
||||
provider's real platform workflow.
|
||||
|
||||
To check the published version, run `npm view rea-agents dist-tags.latest`.
|
||||
Use the connected server's actual tool list and advertised input schemas for
|
||||
feature selection. The same package version string in a development checkout
|
||||
does not establish that its bytes match the npm tarball. Update a registration
|
||||
through a reviewed, scoped setup plan, then restart/reconnect the agent.
|
||||
|
||||
## Skill-only installation
|
||||
|
||||
```bash
|
||||
npx skills add morluto/rea --skill reverse-engineer-anything
|
||||
```
|
||||
|
||||
This installs agent instructions and bundled references, not REA MCP
|
||||
registration or analysis engines. Follow the skill's
|
||||
[conditional connection guide](https://github.com/morluto/rea/blob/main/skills/reverse-engineer-anything/SKILL.md#connect-only-when-needed).
|
||||
Working tools can be used immediately. If tools are missing, inspect the current
|
||||
client's registration with `doctor --client codex --json` (substitute its client
|
||||
ID), then plan repairs with `setup --client codex --dry-run --json`. Show and
|
||||
approve the exact changes before applying that scope. An aligned registration
|
||||
with tools absent from the active session needs a restart/reconnection;
|
||||
`doctor` checks files and prerequisites, not the live agent connection.
|
||||
|
||||
Guided setup installs the package's matching skill by default. The skills.sh
|
||||
route can select newer repository instructions, so follow actual server schemas
|
||||
and the release boundary above. Static JavaScript CLI inspection can proceed
|
||||
while MCP is unavailable:
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||||
```
|
||||
|
||||
It returns the complete Evidence record directly and requires no native engine.
|
||||
Provider failures in doctor do not prevent unrelated target-free tools. To check
|
||||
readiness for one task instead of auditing every integration, see
|
||||
[Check readiness for the task at hand](https://github.com/morluto/rea/blob/main/README.md#check-readiness-for-the-task-at-hand).
|
||||
|
||||
## Supported agents
|
||||
|
||||
Setup can configure these clients for REA's local MCP server:
|
||||
|
||||
| Client | `--client` value |
|
||||
| ------------------ | ---------------- |
|
||||
| Claude Code | `claude_code` |
|
||||
| Claude Desktop | `claude_desktop` |
|
||||
| Codex | `codex` |
|
||||
| Cursor | `cursor` |
|
||||
| Gemini CLI | `gemini_cli` |
|
||||
| Windsurf | `windsurf` |
|
||||
| Devin | `devin` |
|
||||
| OpenCode | `opencode` |
|
||||
| Antigravity | `antigravity` |
|
||||
| GitHub Copilot CLI | `copilot_cli` |
|
||||
| Command Code | `commandcode` |
|
||||
| VS Code | `vscode` |
|
||||
|
||||
For OpenCode, setup writes the V1 `mcp.rea` entry, which OpenCode V1 and V2
|
||||
both load. If the configuration already uses OpenCode V2's native
|
||||
`mcp.servers` table, setup registers REA there instead and replaces any earlier
|
||||
`mcp.rea` entry from REA.
|
||||
|
||||
## Review setup changes
|
||||
|
||||
`rea setup` uses an inline, scroll-preserving journey inspired by the clarity of
|
||||
PostHog's CLI wizard. It begins with the outcome instead of the installer
|
||||
mechanics:
|
||||
`rea setup` first offers the supported agents in a multi-select. Existing REA
|
||||
registrations are selected by default. Newly detected clients remain available
|
||||
but unselected: detection gives setup context, not permission to add a new
|
||||
registration. Clients without a detected configuration can still be selected.
|
||||
Explicit `--client` flags skip this question.
|
||||
|
||||
- investigate local applications from a supported agent;
|
||||
- recover evidence through an available deep-analysis provider;
|
||||
- use the bundled skill for a repeatable investigation workflow.
|
||||
Setup adds MCP access for selected clients. It installs REA's bundled workflow
|
||||
with those integrations by default; use `--skill=false` to omit it. If no agent
|
||||
is selected, setup offers the workflow separately for CLI use, with No as the
|
||||
default. Hopper is a separate optional choice: setup shows its proposed
|
||||
installation or connection and requires its own explicit approval. It can also
|
||||
save verified paths for an existing Ghidra installation.
|
||||
|
||||
REA then summarizes the detected clients and asks which capabilities to set up.
|
||||
The capability picker pre-selects `Agent integration` (the MCP registration and
|
||||
matching guided workflow together) when detected agents exist, and presents the
|
||||
optional Hopper provider alongside it. Selecting agent integration opens a second
|
||||
checklist for the exact detected agents that should receive a registration; all
|
||||
detected agents are pre-selected. Detection provides context and pre-selection;
|
||||
it does not authorize a configuration write until the user confirms.
|
||||
After selection, review the plan's exact paths and changes and approve before
|
||||
REA writes files or installs Hopper. You can cancel at any prompt.
|
||||
|
||||
Because the user has explicitly invoked `rea setup`, the wizard biases toward the
|
||||
happy path: capabilities and agents are pre-selected, and the final approval
|
||||
defaults to **Yes**. The user can still deselect any item or cancel at any prompt.
|
||||
Choosing no capabilities exits without changes. Selecting agent integration
|
||||
always includes the bundled skill, even if all agents are deselected. The picker
|
||||
keeps its navigation, selection, confirmation, and cancellation keys visible
|
||||
instead of relying on a transient hint.
|
||||
Before applying changes, REA checks your current configuration. The plan lists:
|
||||
|
||||
Every selected path converges on the same exact preflight. REA validates the
|
||||
current state, prints the proposed effects, and asks for final approval with
|
||||
**Yes** as the default. Selection alone never authorizes a mutation. The plan
|
||||
identifies:
|
||||
|
||||
- an existing Hopper installation, a validated bring-your-own Ghidra environment, or the official Hopper package it proposes to install;
|
||||
- an existing Hopper installation, a verified existing Ghidra installation, or the official Hopper package it proposes to install;
|
||||
- each detected agent configuration path;
|
||||
- the REA skill destination;
|
||||
- external software, network origins, integrity evidence, and package-manager
|
||||
commands.
|
||||
|
||||
Malformed or unsafe existing configuration blocks the whole transaction before
|
||||
Hopper installation or any file write. Declining, pressing Ctrl-C, or selecting
|
||||
nothing makes no changes. Agent configuration writes preserve unrelated
|
||||
Hopper installation or any file write. Declining or pressing Ctrl-C makes no
|
||||
changes. Agent configuration writes preserve unrelated
|
||||
entries, create backups, use atomic replacement, and verify their result.
|
||||
|
||||
Progress remains append-only so completed and failed operations stay visible in
|
||||
terminal history. After a successful run, the completion message names the
|
||||
verified capabilities now available—for example configured MCP clients, the
|
||||
selected analysis provider, and the installed skill—and gives the corresponding
|
||||
next action. When an agent must restart to load its registration, REA says so;
|
||||
otherwise it suggests beginning an investigation. It does not advertise a
|
||||
capability that the final diagnostic check did not verify.
|
||||
After setup, REA reports which agents, analysis tools, and workflow files passed
|
||||
its final checks. Restart any agent named in the completion message, then begin
|
||||
your investigation. Failed steps and diagnostics remain in terminal history.
|
||||
|
||||
Select exact clients in scripts with repeatable `--client` flags, or retain
|
||||
automatic discovery explicitly with `--all-detected`. Use `--skill=false` to
|
||||
override the normal bundled-skill installation and `--dry-run` for a read-only
|
||||
plan:
|
||||
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:
|
||||
|
||||
```bash
|
||||
rea setup --client codex --client cursor --skill=false --dry-run
|
||||
@@ -119,13 +183,20 @@ rea setup --client codex --client cursor --skill=false --dry-run
|
||||
|
||||
Prompt UI and progress are written to stderr so stdout remains available for
|
||||
structured results and pipelines. `NO_COLOR=1` disables color. Use
|
||||
`--accessible` for sequential, vertically rendered yes/no prompts.
|
||||
`--accessible` for sequential, vertically rendered yes/no prompts. Implicit
|
||||
interactive setup requires stdin, stdout, and stderr to all be terminals; when
|
||||
any stream is redirected, setup stays non-interactive. Declining or cancelling
|
||||
returns status `cancelled` with exit code 0.
|
||||
|
||||
For automation, `rea setup --json` reports the plan without applying it.
|
||||
Prefer pairing `--yes` with explicit scope such as `--client codex`,
|
||||
or `--all-detected`. Legacy unscoped `--yes` remains compatible for
|
||||
this release but emits a deprecation warning. Installing missing Hopper
|
||||
non-interactively additionally requires `--install-hopper`:
|
||||
For automation, `rea setup --json` reports the plan and a compact `.doctor`
|
||||
readiness projection without applying it. Use `rea doctor --json` for full
|
||||
health diagnostics and canonical tool catalog details. Pair
|
||||
`--yes` with explicit scope such as `--client codex`, or use
|
||||
`--all-detected` when the broad scope is intended. Without a scope flag,
|
||||
`--yes` is limited to existing REA-owned registrations; it does not select all
|
||||
detected clients. An unapproved actual apply reports `needs_confirmation` and
|
||||
exits 1. Installing missing Hopper non-interactively additionally requires
|
||||
`--install-hopper`:
|
||||
|
||||
```bash
|
||||
rea setup --yes --all-detected --install-hopper --json
|
||||
@@ -133,11 +204,14 @@ rea setup --yes --all-detected --install-hopper --json
|
||||
|
||||
Setup pins package-runner MCP registrations to the exact installed REA version,
|
||||
installs the matching skill and on-demand references in the same plan, and adds
|
||||
`startup_timeout_sec = 30` for Codex. Interactive `rea upgrade` installs the new
|
||||
executable and then opens the updated `rea setup --all-detected` plan for
|
||||
separate approval. Structured or non-TTY upgrades defer that integration sync.
|
||||
Use the same setup command to migrate floating, unversioned, or stale
|
||||
registrations, then restart changed clients.
|
||||
`startup_timeout_sec = 30` for Codex. `rea update` installs the exact resolved
|
||||
release into the npm prefix that owns the running package, then checks the new
|
||||
executable's version before reporting success. It does not reopen onboarding.
|
||||
Release lookup and installation both use npm's configured registry.
|
||||
Its maintenance plan selects only existing REA registrations and an already
|
||||
installed REA skill. Run the returned scoped setup command to review and approve
|
||||
those changes, then restart affected agents. The plan is returned in terminal,
|
||||
non-TTY, and JSON modes without applying configuration changes.
|
||||
|
||||
## Hopper
|
||||
|
||||
@@ -151,6 +225,16 @@ and digest, validates the application bundle, and atomically installs it to
|
||||
operator can choose its demo mode or activate an existing license. Homebrew and
|
||||
administrator access are not used.
|
||||
|
||||
The Hopper launcher action used by REA creates a document from an executable;
|
||||
its supported command-line interface does not attach to an already-open
|
||||
document. While another live REA session owns the same target and loader
|
||||
profile, a second session reports the owning run ID instead of opening a
|
||||
duplicate document. Closing the owning REA session releases this guard; Hopper
|
||||
keeps its document open. A later REA session can therefore open another
|
||||
document for the same target. REA does not currently identify, focus, or reuse
|
||||
that existing GUI document, and the supported launcher exposes no attach or
|
||||
reuse action for it.
|
||||
|
||||
On supported Linux distributions, approved setup verifies Hopper's official
|
||||
`.deb`, `.rpm`, or Arch package before invoking the native package manager.
|
||||
REA runs the supported demo build on a private Xvfb display and selects Hopper's
|
||||
@@ -160,7 +244,7 @@ desktop display. Unattended package-manager access requires
|
||||
mount, REA first verifies the conflict and then uses an unprivileged user and
|
||||
mount namespace with a private mode-1777 tmpfs over that directory only. The
|
||||
host mount and the rest of `/tmp` remain unchanged; this fallback never invokes
|
||||
`sudo`. `rea doctor --provider hopper --detail full --json` reports the selected
|
||||
`sudo`. `rea doctor --provider hopper --json` reports the selected
|
||||
strategy and both host and effective mount facts.
|
||||
|
||||
### Hopper in CI
|
||||
@@ -177,7 +261,7 @@ starting an unattended job. Ephemeral macOS runners therefore need a
|
||||
pre-provisioned user session or a deliberate interactive bootstrap step.
|
||||
|
||||
When Hopper cannot start in CI, run
|
||||
`rea doctor --provider hopper --detail full --json` in the failing runner.
|
||||
`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.
|
||||
Apply the reported remediation rather than exposing the runner's desktop,
|
||||
@@ -185,77 +269,154 @@ copying license secrets into logs, or killing unrelated Hopper processes.
|
||||
|
||||
## Ghidra
|
||||
|
||||
REA's Ghidra provider is bring-your-own and supports Linux x64 with the exact
|
||||
official Ghidra 12.1.2 release and a 64-bit full JDK 21. An experimental
|
||||
Windows x64 P0 supports approved native x86-64 PE applications. It supplies
|
||||
discovery, analysis-profile commitment, an isolated read-only headless session,
|
||||
ten bounded inventory/name/search operations, and nine function-analysis
|
||||
operations covering metadata, decompilation, assembly, resolved calls, typed
|
||||
references, xrefs, CFG, and dossiers. GUI state and analysis mutations remain
|
||||
unavailable through Ghidra.
|
||||
REA connects to an existing Ghidra installation on Linux x64, macOS x64/arm64,
|
||||
or experimental Windows x64 P0.
|
||||
It accepts Ghidra 12.1.x and the 64-bit full JDK declared by that installation's
|
||||
`application.java.min` and `application.java.max`. Current 12.1 releases require
|
||||
JDK 21 or newer and set no maximum. The bridge is verified with Ghidra 12.1.4
|
||||
and JDK 21. On macOS, the installation
|
||||
must include the native decompiler for the host architecture; REA does not
|
||||
build it or change Gatekeeper quarantine settings.
|
||||
|
||||
The adapter exposes 25 read-only operations: thirteen inventory/name/search
|
||||
operations and twelve function-analysis operations. These cover metadata,
|
||||
decompilation, assembly, resolved calls, typed references, xrefs, function
|
||||
dossiers, instructions, recovered data types, measured load mappings, loaded
|
||||
memory bytes, and observed file offsets. Independent load-image attestation
|
||||
supports DOS MZ and explicitly selected COM; PE returns its measurements with that limitation.
|
||||
On Linux and macOS, `annotate_native_function` also edits a function name and/or
|
||||
entry comments atomically and returns refreshed analysis. These session metadata
|
||||
edits leave executable bytes unchanged and are discarded on close. GUI controls
|
||||
require Hopper; Windows P0 remains read-only.
|
||||
|
||||
Windows P0 admits native x86-64 PE applications on fixed local NTFS volumes.
|
||||
The npm package bundles native Job Object ownership, protected private runtime
|
||||
DACLs, and handle-based path admission; no separate addon installation is needed.
|
||||
See the [Windows Ghidra P0 guide](windows-ghidra-p0.md) for verified scope and
|
||||
limitations.
|
||||
|
||||
Extract Ghidra and install the JDK outside REA, then export absolute paths:
|
||||
|
||||
```bash
|
||||
export GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra_12.1.2_PUBLIC
|
||||
export GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra_12.1.4_PUBLIC
|
||||
export JAVA_HOME=/absolute/path/to/jdk-21 # optional if java/javac are on PATH
|
||||
rea doctor --json
|
||||
rea setup
|
||||
```
|
||||
|
||||
PowerShell configuration for Windows uses the same non-secret paths:
|
||||
On Windows, configure the existing installation in PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:GHIDRA_INSTALL_DIR = "C:\tools\ghidra_12.1.2_PUBLIC"
|
||||
$env:GHIDRA_INSTALL_DIR = "C:\tools\ghidra_12.1.4_PUBLIC"
|
||||
$env:JAVA_HOME = "C:\Program Files\Java\jdk-21"
|
||||
rea doctor --json
|
||||
rea providers --json
|
||||
```
|
||||
|
||||
`rea setup` does not mutate Windows client configuration or install Hopper,
|
||||
Ghidra, Java, Python, or another package. Register the built `rea mcp` command
|
||||
manually in the intended client and preserve the two environment variables.
|
||||
See [Windows Ghidra P0](windows-ghidra-p0.md) for a complete example.
|
||||
`rea setup` can configure supported Windows agents and install the bundled
|
||||
REA skill after approval. Direct registrations use Node to launch REA's entry
|
||||
script; package-runner registrations use the pinned `npx` command. Hopper
|
||||
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.
|
||||
|
||||
Doctor validates the platform, architecture, application version,
|
||||
`support/analyzeHeadless` or `support/analyzeHeadless.bat`, Java
|
||||
version/bitness, and the presence of `javac`/`javac.exe`.
|
||||
Doctor validates the platform, architecture, Ghidra 12.1.x application version,
|
||||
`support/analyzeHeadless` or `support/analyzeHeadless.bat`, the installation's
|
||||
Java major range, 64-bit JDK bitness, and the presence of `javac`/`javac.exe`.
|
||||
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
|
||||
environment entry in its plan, writes only after approval, and never downloads,
|
||||
installs, upgrades, or modifies Ghidra or Java.
|
||||
|
||||
Each verified session uses an ephemeral temporary project and isolated
|
||||
home/cache/config/temp paths. REA passes `-readOnly`, `-deleteProject`, a
|
||||
per-file analysis timeout, CPU and heap bounds, and its packaged Java bridge via
|
||||
`-scriptPath`; it never opens an existing user project. Linux uses a
|
||||
current-user-only local bridge socket and descriptor. Windows P0 uses
|
||||
token-authenticated IPv4 loopback, a token-free endpoint record, and bounded
|
||||
process-tree termination. It does not yet prove private DACL,
|
||||
reparse-point-safe, or Job Object semantics; use only approved non-sensitive
|
||||
fixtures.
|
||||
home/cache/config/temp paths. REA passes `-readOnly`, `-deleteProject`, uses
|
||||
Ghidra's default analysis and resource settings, and loads its packaged Java
|
||||
bridge via `-scriptPath`; it never opens an existing user project. Linux and
|
||||
macOS use a current-user-only local bridge socket and descriptor. The
|
||||
experimental Windows transport uses authenticated IPv4 loopback with a
|
||||
private native-owned bearer descriptor and Job Object process ownership.
|
||||
|
||||
Operations begin only after default auto-analysis completes. A timeout fails
|
||||
the open rather than exposing partial analysis. One session contains exactly
|
||||
one imported Program; use `provider_id: "ghidra"`, `--provider ghidra`, or
|
||||
Operations begin only after default auto-analysis completes. `open_binary`
|
||||
selects and validates the target/provider binding; it does not wait for Ghidra
|
||||
import and auto-analysis. The first Ghidra-backed query starts that work lazily.
|
||||
The provider startup deadline is 330,000 ms for import, analysis, bridge, and
|
||||
health readiness; a startup failure is returned by the query that triggered it,
|
||||
without exposing partial analysis. This deadline is separate from MCP transport
|
||||
initialization and the client's deadline for that individual tool call. A client
|
||||
can time out earlier even when Ghidra would complete within its startup deadline.
|
||||
See [Ghidra first-query deadlines and recovery](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
|
||||
for client options and the close/reopen recovery flow.
|
||||
|
||||
One session contains exactly one imported Program; use `provider_id: "ghidra"`, `--provider ghidra`, or
|
||||
`REA_ANALYSIS_PROVIDER=ghidra` when both Hopper and Ghidra support the target.
|
||||
One persistent decompiler is owned by the Program, Ghidra API calls cross a
|
||||
bounded 32-request serial queue, and each native decompile is limited to 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.
|
||||
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
|
||||
remain unknown, reference-kind provenance is preserved, and provider-specific
|
||||
pseudocode is never treated as original source or Hopper-equivalent text.
|
||||
|
||||
Unexpected request failures retain their original internal cause. CLI and MCP
|
||||
errors expose Error names, messages, and available codes under
|
||||
`details.diagnostics.failure_cause`; other rejection values retain their
|
||||
primitive value or an explicit type. Shutdown warnings include the failure
|
||||
kind, message, and diagnostics, while process and temporary-project cleanup
|
||||
continues. These messages redact known bridge authentication tokens and
|
||||
preserve local paths and other analysis context.
|
||||
|
||||
Run `GHIDRA_INSTALL_DIR=... npm run verify:ghidra` from a source checkout to
|
||||
compile and analyze source-owned x86-64 debug/stripped ELF, AArch64 ELF,
|
||||
x86-64 PE, and x86-64 Mach-O conformance fixtures. The verifier expects `cc`,
|
||||
`clang`, and `lld-link` on `PATH`; `REA_CC`, `REA_CLANG`, and `REA_LLD_LINK` can
|
||||
select alternate command paths.
|
||||
compile and analyze debug and stripped host-native fixtures (ELF on Linux x64
|
||||
or Mach-O on macOS), plus a native DWARF 4 type-layout object. This lane needs a
|
||||
host C compiler in addition to Ghidra and its JDK.
|
||||
|
||||
Run `GHIDRA_INSTALL_DIR=... npm run verify:ghidra:cross-format` to add AArch64
|
||||
ELF, x86-64 PE, and x86-64 Mach-O fixture coverage. This separate lane needs
|
||||
`clang`, LLD, and `lld-link` on `PATH`; `REA_CLANG` and `REA_LLD_LINK` select
|
||||
alternate command paths. Missing cross-target tooling does not block the
|
||||
host-native Ghidra acceptance lane.
|
||||
|
||||
On a controlled Windows x64 runner, use
|
||||
`npm run verify:ghidra:windows`. The verifier generates a deterministic native
|
||||
PE fixture from source bytes, exercises every admitted Ghidra operation, checks
|
||||
target/snapshot/import digest identity, and requires complete runtime cleanup.
|
||||
PE fixture from source bytes and requires the Windows native authority before
|
||||
opening the provider. The controls are implemented on main; see the
|
||||
[release boundary](#released-package-and-main). The lane verifies all 25 admitted
|
||||
read-only operations, digest identity, and runtime cleanup. The separate
|
||||
`verify:ghidra:windows:package` lane checks the packed CLI/MCP boundary with an
|
||||
ordinary user. It requires the built Windows native bundle and an existing
|
||||
Ghidra/JDK installation; neither is inferred from startup alone.
|
||||
|
||||
## Diagnose, update, and remove
|
||||
|
||||
`rea doctor --json` is strictly read-only. `rea upgrade` updates only the npm installation that owns the running CLI. `rea uninstall` removes only REA-owned agent registrations and skill files; `--purge-data` additionally removes REA cache and state paths.
|
||||
`rea doctor --json` is strictly read-only. `rea update` updates only the npm installation that owns the running CLI. Source checkouts and package-runner copies must be updated through the mechanism that owns them; a fresh package-runner invocation can use `npx rea-agents@latest`. `rea uninstall` removes only REA-owned agent registrations and skill files; `--purge-data` additionally removes REA cache and state paths.
|
||||
|
||||
## MCP Registry
|
||||
|
||||
REA is published in the official MCP Registry as `io.github.morluto/rea`. Registry
|
||||
clients can discover the server and install the existing public `rea-agents` npm
|
||||
package; the Registry entry does not introduce a second distribution artifact.
|
||||
|
||||
For a client that supports Registry discovery, search for `io.github.morluto/rea`
|
||||
and select the npm package. The published metadata launches the existing stdio
|
||||
server through `npx` with the `mcp` command.
|
||||
|
||||
For a client that requires manual configuration, use:
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@5.0.0", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
Persistent registrations should use one exact package version. `rea setup`
|
||||
writes the same package-runner shape and pins it to the exact version that
|
||||
performed setup. Run current setup to refresh an older registration, then
|
||||
restart the client.
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
# Persistent investigation workspaces
|
||||
|
||||
REA can run a deterministic artifact investigation across two versions and
|
||||
persist its Evidence beyond a CLI process or MCP session. The CLI and MCP use
|
||||
the same application workflow.
|
||||
|
||||
## CLI
|
||||
|
||||
First allow the directory that will own the workspace, then explicitly approve
|
||||
the write:
|
||||
|
||||
```bash
|
||||
export REA_EVIDENCE_ROOTS_JSON='["/absolute/path/to/evidence"]'
|
||||
export REA_INVESTIGATION_INPUT_ROOTS_JSON='["/absolute/path/to/releases"]'
|
||||
|
||||
rea investigate-versions \
|
||||
/absolute/path/to/version-1 \
|
||||
/absolute/path/to/version-2 \
|
||||
/absolute/path/to/evidence/releases.json \
|
||||
--workspace-name releases \
|
||||
--yes
|
||||
```
|
||||
|
||||
The two versions may be regular artifacts or directories supported by the
|
||||
artifact graph provider. Both must resolve beneath an explicitly configured
|
||||
`REA_INVESTIGATION_INPUT_ROOTS_JSON` root. The workspace parent must already
|
||||
exist beneath `REA_EVIDENCE_ROOTS_JSON`. REA never creates an allowlisted root
|
||||
implicitly.
|
||||
|
||||
Use `--expected-revision N` when an external coordinator needs an explicit
|
||||
compare-and-swap guard. Traversal, byte, page, and comparison limits are also
|
||||
available as CLI options.
|
||||
|
||||
After a completed run returns its stable run ID, `--replay-run-id RUN_ID`
|
||||
selects that exact result without reading or rescanning either version input.
|
||||
The workspace still requires read authority. REA rejects a selector that is
|
||||
missing, incomplete, or inconsistent with the requested paths and controls.
|
||||
|
||||
## MCP
|
||||
|
||||
`find_changed_behavior` retains its existing aggregation mode. Its additive
|
||||
`investigation_run` mode starts or resumes the same persistent workflow:
|
||||
|
||||
```json
|
||||
{
|
||||
"investigation_run": {
|
||||
"approved": true,
|
||||
"workspace_path": "/absolute/path/to/evidence/releases.json",
|
||||
"workspace_name": "releases",
|
||||
"left_path": "/absolute/path/to/version-1",
|
||||
"right_path": "/absolute/path/to/version-2",
|
||||
"options": {
|
||||
"max_entries": 10000,
|
||||
"max_total_bytes": 1073741824,
|
||||
"max_entry_bytes": 268435456,
|
||||
"max_compression_ratio": 1000,
|
||||
"max_depth": 20,
|
||||
"max_path_bytes": 4096,
|
||||
"page_size": 500,
|
||||
"change_limit": 500
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Exactly one of `comparisons` or `investigation_run` is accepted. The MCP tool
|
||||
inventory remains unchanged.
|
||||
|
||||
## Checkpoints and reuse
|
||||
|
||||
One run has four ordered stages:
|
||||
|
||||
1. Inventory the left version.
|
||||
2. Inventory the right version.
|
||||
3. Compare the complete artifact Evidence page sets.
|
||||
4. Derive the changed-behavior report.
|
||||
|
||||
The inventory checkpoint is written first, followed by the comparison and final
|
||||
report checkpoints. A process interrupted after a checkpoint can resume from
|
||||
the last complete stage. A completed run is reused when both graph commitments
|
||||
and every bounded option are identical.
|
||||
|
||||
By default, REA rescans the version inputs so changed content creates a new
|
||||
run. For an explicit cache-only replay, add the returned `run_id` as
|
||||
`investigation_run.replay_run_id`. REA validates the workspace revision and
|
||||
name, the complete run, its exact paths and controls, and every linked Evidence
|
||||
record before it skips input-read authorization.
|
||||
|
||||
The run ID is a SHA-256 commitment to both root and graph digests plus the
|
||||
normalized options. Local paths and timestamps do not affect that identity.
|
||||
Changing either artifact or a budget creates a new run in the same workspace;
|
||||
it does not overwrite the earlier run.
|
||||
|
||||
## Integrity and concurrency
|
||||
|
||||
A workspace contains a canonical Evidence v2 bundle plus run manifests. Each
|
||||
workspace revision has a deterministic digest and points to the prior revision
|
||||
digest. Parsing verifies:
|
||||
|
||||
- the workspace, run, Evidence, graph, and revision identities;
|
||||
- canonical ordering and unique run IDs;
|
||||
- every run-to-Evidence reference;
|
||||
- stage and completion-state consistency.
|
||||
|
||||
Writes are canonical JSON, atomic, fsynced, and mode `0600`. REA rejects
|
||||
workspace paths outside `REA_EVIDENCE_ROOTS_JSON`, symlink destinations,
|
||||
artifact inputs outside `REA_INVESTIGATION_INPUT_ROOTS_JSON`,
|
||||
oversized or deeply nested JSON, stale expected revisions, and concurrent lock
|
||||
holders. Lock or CAS conflicts fail without mutating the workspace; callers may
|
||||
retry the same idempotent request.
|
||||
|
||||
## Truth boundary
|
||||
|
||||
The automatic run currently observes shipped artifact structure. Artifact
|
||||
changes are static behavior candidates, not runtime observations or causal
|
||||
claims. Without controlled process comparison Evidence, `behavior_status`
|
||||
remains `unknown` even when static differences are present. Provider
|
||||
limitations, incomplete comparison pages, and unsupported container expansion
|
||||
remain explicit in the final Evidence.
|
||||
|
||||
Automatic Hopper function matching, process replay, protocols, UI comparison,
|
||||
and reconstruction verification are not yet stages of this run. Existing
|
||||
manual comparison Evidence can still be aggregated through the original
|
||||
`find_changed_behavior` mode.
|
||||
@@ -1,6 +1,6 @@
|
||||
# JavaScript Application Graph
|
||||
|
||||
JavaScript Application Graph v1 is REA's provider-neutral domain model for
|
||||
JavaScript Application Graph is REA's provider-neutral domain model for
|
||||
connecting shipped application bytes, JavaScript structure, Electron process
|
||||
boundaries, passive browser observations, and native add-ons. It is a durable,
|
||||
canonical data contract; it is not an extractor or a new CLI/MCP tool.
|
||||
@@ -32,13 +32,12 @@ adds capture-scoped targets, frames, scripts, workers, and inferred
|
||||
verification remains a separate later layer. The domain graph itself never
|
||||
reaches outward to obtain any of those facts.
|
||||
|
||||
## Versioned record
|
||||
## Stored record
|
||||
|
||||
A stored graph has this top-level shape:
|
||||
|
||||
```text
|
||||
schema: "JavaScriptApplicationGraph"
|
||||
schema_version: 1
|
||||
graph_id: jag_<sha256 of normalized graph semantics>
|
||||
root_node_ids: jag_node_<sha256>[]
|
||||
nodes: application entities with one or more observations
|
||||
@@ -47,14 +46,14 @@ coverage: complete, partial, unknown, or unavailable
|
||||
limitations: explicit graph-wide constraints
|
||||
```
|
||||
|
||||
Parsing rejects unknown fields, unsupported versions, non-canonical ordering,
|
||||
duplicate IDs, stale semantic commitments, missing roots, dangling endpoints,
|
||||
self-edges, and `changed_from` edges between unlike node kinds. Constructors
|
||||
normalize set-like arrays before deriving their IDs.
|
||||
Parsing rejects unknown fields, non-canonical ordering, duplicate IDs, stale
|
||||
semantic commitments, missing roots, dangling endpoints, self-edges, and
|
||||
`changed_from` edges between unlike node kinds. Constructors normalize set-like
|
||||
arrays before deriving their IDs.
|
||||
|
||||
## Entities and relationships
|
||||
|
||||
The v1 node vocabulary covers:
|
||||
The node vocabulary covers:
|
||||
|
||||
- package, installer, and artifact;
|
||||
- ASAR entry;
|
||||
@@ -98,7 +97,7 @@ correspondence is a separately identified cross-layer inference.
|
||||
Every observation and edge carries all of the following:
|
||||
|
||||
- an authority, such as artifact bytes, AST analysis, static relationship
|
||||
inference, passive CDP runtime, cross-layer reconciliation, controlled replay,
|
||||
inference, passive CDP runtime, and cross-layer reconciliation,
|
||||
native analysis, historical reference, or user assertion;
|
||||
- an epistemic state: `observed`, `inferred`, `unknown`, or `unavailable`;
|
||||
- confidence independently bounded as exact, high, medium, low, or unknown;
|
||||
@@ -107,7 +106,7 @@ Every observation and edge carries all of the following:
|
||||
location, or an unavailable reason;
|
||||
- extractor name, version, operation, and optional executable digest;
|
||||
- coverage status, truncation, omitted count, and named limits;
|
||||
- limitations and links to existing Evidence v2 records;
|
||||
- limitations and links to existing Evidence records;
|
||||
- a content-derived identifier and its declared stability strategy.
|
||||
|
||||
Authority and epistemic state are separate fields. An AST observation does not
|
||||
@@ -158,16 +157,12 @@ stability. Edge IDs use the same derivation with `relationship-exact`
|
||||
stability. Node, observation, edge, and graph IDs are recomputed during parsing,
|
||||
so callers cannot preserve a stale ID after changing semantic content.
|
||||
|
||||
## Bounds
|
||||
## Structure
|
||||
|
||||
The v1 boundary admits at most 1,000 roots, 100,000 nodes, 200,000 edges, and 64
|
||||
observations per node. Each properties object is valid bounded JSON with at most
|
||||
64 keys, depth 6, 512 structural nodes, and 4,096 characters per string. Arrays
|
||||
of evidence links, limits, and limitations have independent caps.
|
||||
|
||||
These are admission limits, not completeness claims. When an extractor reaches
|
||||
a limit, it must record partial coverage and the exact named limit. It must not
|
||||
return a bounded prefix labeled complete.
|
||||
The graph does not impose aggregate caps on roots, nodes, edges, observations,
|
||||
or JSON properties. Properties must be valid JSON; field-specific validation
|
||||
still applies to paths, identifiers, and evidence values. Extractors report
|
||||
actual omissions in coverage instead of silently dropping a prefix.
|
||||
|
||||
## Domain API
|
||||
|
||||
@@ -184,7 +179,3 @@ The public domain functions are:
|
||||
- `serializeJavaScriptApplicationGraph` for canonical JSON;
|
||||
- `computeJavaScriptApplicationGraphSha256` for a byte-stable verified graph
|
||||
digest.
|
||||
|
||||
Schema evolution is explicit. A future incompatible shape receives a new
|
||||
`schema_version`; v1 parsing fails with an unsupported-version diagnostic rather
|
||||
than guessing how to reinterpret it.
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
# Cross-layer JavaScript application workflows
|
||||
|
||||
REA derives a bounded feature trace from one authenticated JavaScript
|
||||
REA derives a complete reachable feature trace from one authenticated JavaScript
|
||||
Application Graph and compares two authenticated graph versions. The MCP tools
|
||||
are `trace_application_feature`, `trace_javascript_semantics`,
|
||||
`compare_application_versions`, `compare_source_to_bundle`, and
|
||||
`compare_javascript_export_shapes`; their CLI equivalents use the same names
|
||||
with hyphens.
|
||||
|
||||
Both workflows consume Evidence v2 produced by
|
||||
These workflows consume Evidence produced by
|
||||
`analyze_javascript_application` or `reconcile_javascript_runtime`. They do not
|
||||
read an artifact, execute application code, attach to a process, or open a
|
||||
native-analysis provider. Static artifact observations, passive runtime
|
||||
observations, relationship inferences, and unknown or unavailable facts retain
|
||||
their original graph authority.
|
||||
|
||||
Current `analyze_javascript_application` Evidence uses result schema v2. It
|
||||
retains the structural JavaScript Application Graph and a separate semantic
|
||||
relation graph bound to the same root artifact digest and structural graph ID.
|
||||
Legacy v1 Evidence remains valid for structural workflows, but semantic tracing
|
||||
returns an actionable request to reanalyze it rather than treating missing
|
||||
semantic data as an empty graph.
|
||||
`analyze_javascript_application` Evidence retains the structural JavaScript
|
||||
Application Graph and a separate semantic relation graph bound to the same root
|
||||
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.
|
||||
|
||||
## Feature tracing
|
||||
|
||||
Select one literal seed kind: node ID, route, string, API, IPC channel, module,
|
||||
or native export. Matching is exact or literal substring matching; regular
|
||||
expressions and executable predicates are not accepted. Direction, depth,
|
||||
nodes, edges, paths, and seed matches all have caller-visible limits.
|
||||
expressions and executable predicates are not accepted. The trace includes all
|
||||
matching seeds and the complete graph reachable in the selected direction.
|
||||
|
||||
The result contains the matching basis, a bounded subgraph, terminal paths,
|
||||
authority summaries, truncation frontier, and native handoffs. A handoff binds
|
||||
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
|
||||
matches exactly. Otherwise the result recommends provider-neutral follow-up
|
||||
@@ -42,27 +41,36 @@ provider implicitly.
|
||||
rereading mutable files. A seed may identify a semantic or application node,
|
||||
literal, function fingerprint, property, endpoint, event, or boundary field.
|
||||
The query declares backward provenance, forward influence, callers, or
|
||||
ownership and can restrict admitted relation kinds. Node, relation, depth,
|
||||
function, module, seed-match, and page limits are validated against exact
|
||||
published ranges. A committed cursor returns the next deterministic relation
|
||||
page only for the same graph, query, and limits.
|
||||
ownership and can restrict admitted relation kinds. REA returns every matching
|
||||
seed and the complete reachable subgraph; callers do not choose node, relation,
|
||||
depth, function, module, seed-match, or page budgets.
|
||||
|
||||
The extractor covers lexical definitions and reads, literal seeds, static
|
||||
property slots, object reads/writes/spreads/destructuring, closure captures,
|
||||
uniquely resolved local calls, argument/return flow, and explicit Promise
|
||||
construction, chaining, aggregation, await, return, assignment, and
|
||||
detachment. It also recovers bounded static candidates for EventEmitter
|
||||
detachment. It also recovers static candidates for EventEmitter
|
||||
registrations/removals and dispatch, Node timers and cancellation handles, asynchronous
|
||||
`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.
|
||||
|
||||
Mutation tracking covers explicit member assignments, updates, deletion, and
|
||||
loop assignment targets, including the supported local aliases and shared
|
||||
nested references. Calls such as `Object.assign`, `Reflect.set`, and
|
||||
`Reflect.deleteProperty`, and writes reaching a caller's object through another
|
||||
function's parameter, are not tracked by this mutation pass. These channels
|
||||
can leave an initializer-derived literal in the graph even after the runtime
|
||||
value changes. Treat such a result as an uncovered mutation channel, not as
|
||||
proof of the current runtime value; a follow-up needs to model that channel
|
||||
and verify its caller/alias behavior.
|
||||
|
||||
Function fingerprints commit normalized syntax, control-flow shape, relation
|
||||
shape, literal sets, arity, and detected effects without using local names or
|
||||
source offsets. Equal duplicate fingerprints remain ambiguous. Dynamic
|
||||
calls/properties/names, parser recovery, unresolved handles, unsupported API
|
||||
shapes, and every reached bound stay explicit unknowns; a missing relation is
|
||||
never reported as proven behavioral absence.
|
||||
calls/properties/names, parser recovery, unresolved handles, and unsupported API
|
||||
shapes stay explicit unknowns; a missing relation is never reported as proven
|
||||
behavioral absence.
|
||||
|
||||
Semantic relations use static-inference authority. `resolved` means the static
|
||||
target identity was unique within admitted analysis; it does not mean the path
|
||||
@@ -83,19 +91,21 @@ persistent identities. Duplicate candidates remain ambiguous. Source-map and
|
||||
structural matches are high/medium-confidence inferences rather than exact
|
||||
facts. Each item is `unchanged`, `added`, `removed`, `changed`, or `unknown`,
|
||||
and the result includes its basis, candidates, changed dimensions, Evidence
|
||||
links, limitations, and a bounded `changed_from` graph.
|
||||
links, limitations, and a `changed_from` graph containing all compared nodes,
|
||||
their observations, and relationships.
|
||||
|
||||
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`. Output truncation separately reports omitted comparison
|
||||
items, candidate references, graph nodes, edges, and observations. With
|
||||
`unknown_registry_approved: true`, unresolved comparison items can be retained
|
||||
as a residual unknown in a live session.
|
||||
condition is `unknown`. The comparison retains every classified item and
|
||||
ambiguity candidate. Coverage carries omitted counts and limits from each input
|
||||
graph; comparison adds no caller-selected item, candidate, graph node, or edge
|
||||
budget. Unresolved comparison items are recorded as a residual unknown linked
|
||||
to the comparison Evidence in a live session.
|
||||
|
||||
## Historical source-to-bundle comparison
|
||||
|
||||
`compare_source_to_bundle` compares one cryptographically committed
|
||||
`HistoricalSourceGraph/v1` with authenticated application-graph Evidence. The
|
||||
`HistoricalSourceGraph` with authenticated application-graph Evidence. The
|
||||
stable scoring model reports every admitted signal and weight: exact source
|
||||
digest, source-map original path, exact current path, path suffix, basename, and
|
||||
language extension. Exact digest wins over location inference; weak basename
|
||||
@@ -103,12 +113,13 @@ or extension evidence never forces a mapping.
|
||||
|
||||
Each historical source file is classified as `unchanged`, `modified`,
|
||||
`removed`, `split`, `merged`, `duplicated`, or `unknown`. Multiple equally
|
||||
scored path mappings form a bounded static split inference. Multiple current
|
||||
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
|
||||
complete historical and application inventories with no relevant comparison
|
||||
truncation. Ambiguous candidates, partial inventories, and exhausted limits
|
||||
remain unknown. These static mappings do not claim runtime loading or semantic
|
||||
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
|
||||
be established. These static mappings do not claim runtime loading or semantic
|
||||
equivalence.
|
||||
|
||||
## Export return-shape comparison
|
||||
@@ -120,9 +131,9 @@ match. An export is linked to a callable only through exact lexical/module
|
||||
relationships recovered by the AST analysis.
|
||||
|
||||
Direct return expressions, including expression-bodied arrows, are evaluated
|
||||
through a bounded execution-free value lattice. Literal object fields are
|
||||
static observations. Calls, dynamic spreads, computed keys, parser recovery,
|
||||
and exhausted value, property, or return-site limits remain partial or unknown.
|
||||
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.
|
||||
Nested callable returns are not assigned to their parent callable. Projected
|
||||
graph observations carry source ranges but never source text.
|
||||
|
||||
@@ -131,21 +142,48 @@ unique occurrence on each side and pairing is reciprocal. Changes use JSON
|
||||
Pointer paths with `added`, `removed`, `changed`, or `unknown` status. A missing
|
||||
field is added or removed only when the relevant parent-property coverage is
|
||||
complete on both shapes. The output includes exact selector candidates,
|
||||
omissions, Evidence links, coverage, limitations, and a separate controlled
|
||||
replay recommendation; it does not execute JavaScript.
|
||||
omissions, Evidence links, coverage, and limitations; it does not execute
|
||||
JavaScript. When runtime semantics matter, run behavioral probes directly
|
||||
against the relevant application versions and capture them through the
|
||||
available browser, Electron, or process workflows.
|
||||
|
||||
## CLI and verification
|
||||
|
||||
All five CLI commands accept inline JSON or a JSON file up to 64 MiB. The input
|
||||
is the same object used by the corresponding MCP tool.
|
||||
All five CLI commands accept inline JSON or a path to a JSON file. The CLI
|
||||
returns an Evidence record directly. Put the full records in a later CLI input;
|
||||
a separate CLI process has no retained MCP connection state.
|
||||
|
||||
For a literal string trace, analyze your supplied tree once, then build the
|
||||
input from the saved Evidence (replace the target and seed):
|
||||
|
||||
```bash
|
||||
rea analyze-javascript-application /absolute/path/to/app --json > application-evidence.json
|
||||
node --input-type=module -e '
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
const application = JSON.parse(readFileSync("application-evidence.json", "utf8"));
|
||||
writeFileSync("trace-input.json", JSON.stringify({
|
||||
application, seed: { kind: "string", value: "search-result" }, direction: "both"
|
||||
}));'
|
||||
rea trace-application-feature ./trace-input.json --json
|
||||
```
|
||||
|
||||
MCP analysis returns an envelope containing `result`, `evidence_id`, and full
|
||||
`evidence`. If the connected server advertises retained references, reuse its
|
||||
exact returned ID as `{"kind":"retained-evidence","evidence_id":"RETURNED_ID"}`
|
||||
in `application`, or `left`/`right` for comparisons. `RETURNED_ID` is a template,
|
||||
not a literal valid ID. Native Evidence arrays still use complete records.
|
||||
References are scoped to one connection; `close_binary` clears them. Export a
|
||||
bundle before closing and import it on another connection, or supply the full
|
||||
inline Evidence there. Versions before 4.1.0 accept full inline Evidence only; installing
|
||||
newer skill instructions does not change that schema. See
|
||||
[MCP Evidence inputs](mcp-contracts.md#retained-application-evidence-inputs).
|
||||
|
||||
For two operator-provided directories or ASARs, run:
|
||||
|
||||
```bash
|
||||
npm run verify:application-workflows -- \
|
||||
--left /absolute/path/to/version-a \
|
||||
--right /absolute/path/to/version-b \
|
||||
--source-map-read-approved
|
||||
--right /absolute/path/to/version-b
|
||||
```
|
||||
|
||||
The verifier reconstructs both versions independently, compares them, runs one
|
||||
@@ -158,20 +196,13 @@ all four `--left-module-path`, `--left-export-name`, `--right-module-path`, and
|
||||
`--right-export-name` options to verify an explicit export pair.
|
||||
|
||||
Because source Evidence and derived Evidence are retained by the normal session
|
||||
ledger, evidence bundles, analysis snapshots, and investigation workspaces can
|
||||
carry these records without another persistence format.
|
||||
ledger, evidence bundles and analysis snapshots can carry these records without
|
||||
another persistence format.
|
||||
|
||||
## Controlled replay boundary
|
||||
## Runtime evidence
|
||||
|
||||
Static graph workflows never execute a graph node or recovered module.
|
||||
`run_controlled_replay` is the separate extracted-module boundary: it requires
|
||||
the `javascript_replay` authority, a plan call followed by exact content-bound
|
||||
approval, and the mandatory Linux OS sandbox fixed by
|
||||
[ADR-0002](adr/0002-controlled-replay-authority-and-sandbox.md). Browser,
|
||||
Electron, Process Capture, artifact-read, and static-analysis approvals do not
|
||||
authorize that execution.
|
||||
|
||||
Replay observations retain `controlled-replay` authority. They cannot
|
||||
promote static inference into passive runtime observation or prove that the
|
||||
original application, renderer, preload, main process, or remote service
|
||||
behaved identically.
|
||||
Static graph workflows do not execute recovered code. When runtime semantics
|
||||
matter, run behavioral probes directly against the relevant application
|
||||
versions and capture them through the available browser, Electron, or process
|
||||
workflows. Those observations do not prove behavior in an unobserved app
|
||||
version or environment.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# JavaScript artifact reconstruction
|
||||
|
||||
REA can project an operator-supplied Electron/JavaScript application directory
|
||||
or ASAR into [JavaScript Application Graph v1](javascript-application-graph.md)
|
||||
or ASAR into [JavaScript Application Graph](javascript-application-graph.md)
|
||||
without executing application code. The target-free
|
||||
`analyze_javascript_application` MCP tool, dedicated
|
||||
`rea analyze-javascript-application` CLI command, and generic
|
||||
`rea analyze PATH --approved` directory/ASAR route expose the same application
|
||||
service and return an Evidence v2 envelope containing the graph and an Electron
|
||||
`rea analyze PATH` directory/ASAR route expose the same application
|
||||
service and return an Evidence envelope containing the graph and an Electron
|
||||
boundary summary.
|
||||
|
||||
The resulting Evidence can be combined with passive web or Electron capture
|
||||
@@ -17,53 +17,58 @@ separate static, runtime, and cross-layer inference authorities.
|
||||
|
||||
## Public workflow
|
||||
|
||||
The administrator first grants a canonical input root. Every request still
|
||||
requires explicit per-call approval, and the requested ASAR or directory must
|
||||
remain beneath that root after canonicalization:
|
||||
The tool reads the caller-selected local directory or ASAR path directly:
|
||||
|
||||
```bash
|
||||
export REA_INVESTIGATION_INPUT_ROOTS_JSON='["/absolute/path/to/apps"]'
|
||||
rea analyze /absolute/path/to/apps/app.asar --approved --json
|
||||
rea analyze /absolute/path/to/apps/app.asar --json
|
||||
rea analyze-javascript-application /absolute/path/to/apps/app.asar \
|
||||
--approved \
|
||||
--json
|
||||
```
|
||||
|
||||
Generic `rea analyze` selects this static provider for directories and `.asar`
|
||||
paths when neither `--provider` nor `--snapshot` is supplied. Use the dedicated
|
||||
command when setting explicit format or reconstruction limits. Native targets,
|
||||
single JavaScript files, `.app` bundles, and explicit deep-provider or snapshot
|
||||
requests retain the native deep-analysis route.
|
||||
paths when neither `--provider` nor `--snapshot` is supplied. The dedicated
|
||||
command and the generic route return their complete results inline. Native
|
||||
targets, single JavaScript files, `.app` bundles, and explicit deep-provider or
|
||||
snapshot requests retain the native deep-analysis route.
|
||||
|
||||
When configuring an MCP client, set the variable before approved setup so REA
|
||||
copies the explicit non-secret root policy into the managed registration:
|
||||
Configure an MCP client with the ordinary REA setup command:
|
||||
|
||||
```bash
|
||||
export REA_INVESTIGATION_INPUT_ROOTS_JSON='["/absolute/path/to/apps"]'
|
||||
rea setup
|
||||
```
|
||||
|
||||
Restart the configured MCP client after changing this administrator ceiling.
|
||||
An already-running server cannot acquire a new process environment through
|
||||
MCP elicitation or `SIGHUP`.
|
||||
|
||||
The equivalent MCP input is:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_path": "/absolute/path/to/apps/app.asar",
|
||||
"format": "auto",
|
||||
"approved": true,
|
||||
"source_map_read_approved": false
|
||||
"format": "auto"
|
||||
}
|
||||
```
|
||||
|
||||
`input_path` must be absolute. `format` accepts `auto`, `asar`, or `directory`.
|
||||
The result retains the canonical local path, root artifact digest, artifact
|
||||
manifest and graph commitments, JavaScript Application Graph v1, static
|
||||
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.
|
||||
|
||||
## Large results
|
||||
|
||||
Graph and Evidence identifiers hash canonical JSON incrementally, without
|
||||
assembling a single string for the whole graph. The canonical bytes and existing
|
||||
identifiers remain unchanged. The opt-in regression check is
|
||||
`npm run verify:javascript:digests`;
|
||||
it hashes a value larger than the running Node engine's single-string limit.
|
||||
|
||||
Output formatters and MCP transport serialization still assemble whole strings.
|
||||
For a large CLI result, select the needed fields
|
||||
before formatting, for example `--format json --filter-output
|
||||
evidence_id,normalized_result.statistics`. Complete serialization of a result
|
||||
beyond the engine's string limit is not established by this digest check.
|
||||
Client framing limits also apply: the pinned Node MCP SDK's stdio transport
|
||||
defaults to a 10 MiB buffer. Its caller-selected `maxBufferSize` must accommodate
|
||||
the complete response, including text and structured Evidence projections.
|
||||
|
||||
## What is reconstructed
|
||||
|
||||
The projector reuses the content-addressed artifact inventory and safe artifact
|
||||
@@ -90,7 +95,7 @@ Selected bounded text is then parsed as inert data to recover:
|
||||
- static imports, dynamic imports, CommonJS `require` calls, workers, and
|
||||
service workers;
|
||||
- route, network endpoint, storage, and vendor-marker observations;
|
||||
- local source-map declarations and, when separately approved, bounded original
|
||||
- local source-map declarations and original
|
||||
source names and content digests;
|
||||
- explicit BrowserWindow options and `webPreferences`, including statically
|
||||
resolvable preload entrypoints;
|
||||
@@ -119,11 +124,7 @@ JavaScript file, package record, or source map produces an `unknown` graph scope
|
||||
with `state: unavailable`; it is not treated as evidence that modules or source
|
||||
are absent.
|
||||
|
||||
Source-map content has a separate `source_map_read_approved` input. Without that
|
||||
approval, REA inventories the map, links a static declaration when present, and
|
||||
records partial non-truncated coverage plus an unavailable parse scope. With
|
||||
approval, the graph stores original source names and optional content digests,
|
||||
not the raw `sourcesContent` text.
|
||||
Source-map contents are parsed as local input. The graph stores original source names and optional content digests, not raw `sourcesContent` text.
|
||||
|
||||
Only explicitly present BrowserWindow values are observations. REA does not
|
||||
substitute version-dependent Electron defaults for omitted `webPreferences`.
|
||||
@@ -141,9 +142,10 @@ 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.
|
||||
|
||||
Endpoint observations preserve useful local diagnostics while removing URL
|
||||
credentials, fragments, and query values. Artifact paths, digests, parse
|
||||
locations, and analysis metadata remain actionable because REA is local-only.
|
||||
Endpoint observations preserve useful local diagnostics, including query values
|
||||
and fragments, while removing only URL username/password credentials. Artifact
|
||||
paths, digests, parse locations, and analysis metadata remain actionable
|
||||
because REA is local-only.
|
||||
|
||||
## Safety boundary
|
||||
|
||||
@@ -160,30 +162,29 @@ represented by metadata only.
|
||||
|
||||
## Bounds and coverage
|
||||
|
||||
The input schema bounds artifact entries, cumulative artifact bytes, bytes per
|
||||
entry, compression ratio, path depth and length, selected text files, text bytes,
|
||||
AST nodes, static findings, bundle modules, source-map originals, and cooperative
|
||||
parse time. Source-map original limits are shared across all maps in one
|
||||
reconstruction. Combined input limits are rejected if their conservative graph
|
||||
projection could exceed the v1 contract's 100,000 nodes or 200,000 edges.
|
||||
The reconstruction keeps internal resource safeguards for artifact entries,
|
||||
cumulative artifact bytes, bytes per entry, compression ratio, path depth and
|
||||
length, selected text files, text bytes, AST nodes, and cooperative parse time.
|
||||
These safeguards are not caller-selectable output budgets. Source-map parsing
|
||||
reports truncation when its format or parser safety boundary is reached. The
|
||||
application graph has no aggregate node, edge, root, or observation prefix cap.
|
||||
|
||||
Byte, entry, path, and graph-shape bounds are hard limits. The parse deadline is
|
||||
checked before and between bounded parsing and traversal phases; the synchronous
|
||||
Babel and JSON parser calls cannot be preempted mid-call, so their input byte
|
||||
bounds remain the hard protection for an individual call.
|
||||
|
||||
Byte-identical assets share a content-digest node. At most 64 distinct
|
||||
observations are retained on that node, as required by the graph contract; every
|
||||
inventoried containment edge remains present, and any omitted observations make
|
||||
top-level coverage partial and truncated. When an exact omission count is not
|
||||
knowable, `omitted_count` is `null` rather than a guessed value.
|
||||
Byte-identical assets share a content-digest node. Every distinct observation
|
||||
and inventoried containment edge is retained. If parsing or artifact safety
|
||||
boundaries omit input, coverage reports that omission; when an exact omission
|
||||
count is not knowable, `omitted_count` is `null` rather than a guessed value.
|
||||
|
||||
## Verification boundary
|
||||
|
||||
The source-owned fixture covers an extracted directory, a direct ASAR with an
|
||||
unpacked native add-on, a direct ASAR whose unpacked native companion is
|
||||
missing, and an ASAR nested beneath a directory. Tests also cover deterministic
|
||||
reruns, parse-not-execute behavior, source-map approval, global source-map
|
||||
reruns, parse-not-execute behavior, source-map parsing, global source-map
|
||||
limits, malformed structured data, invalid containers, traversal, symlink
|
||||
escape, oversized text, cancellation, AST truncation, and repeated content
|
||||
identities. A separate synthetic Electron fixture covers explicit safe and
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# JavaScript source recovery
|
||||
|
||||
`recover_javascript_sources` / `recover-javascript-sources` derives readable
|
||||
modules from one explicitly selected UTF-8 JavaScript file or bundle. Use it on
|
||||
files returned by `export_web_scripts` or other local scripts. The operation
|
||||
never executes the selected or recovered application code.
|
||||
|
||||
## Caller-supplied engine
|
||||
|
||||
The initial adapter is verified on Linux x64 with Wakaru **1.13.0**. Set
|
||||
`REA_WAKARU_COMMAND` to the absolute path of an executable supplied by the caller.
|
||||
The adapter uses util-linux `prlimit` (default `/usr/bin/prlimit`; override with
|
||||
an absolute `REA_JAVASCRIPT_PRLIMIT_COMMAND`). REA does not install either tool.
|
||||
Engine configuration is checked only when recovery is requested; other REA
|
||||
capabilities remain available without it.
|
||||
|
||||
Unmodified upstream source is retained in `third_party/wakaru`, pinned to
|
||||
`6070266d24b32951fa2fe1d2dad5b4313bd5c81b` (v1.13.0, Apache-2.0).
|
||||
The result reports the executed binary's exact path, SHA-256 and observed version.
|
||||
The audited source revision is distinct from `executed_source_revision: null`:
|
||||
a version string cannot prove which source produced a caller-supplied binary.
|
||||
|
||||
## Recover and analyze
|
||||
|
||||
```sh
|
||||
rea recover-javascript-sources /tmp/web-scripts/app.js /tmp/recovered-app --json
|
||||
rea analyze-javascript-application /tmp/recovered-app/modules --json
|
||||
```
|
||||
|
||||
MCP input:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/tmp/web-scripts/app.js",
|
||||
"output_directory": "/tmp/recovered-app"
|
||||
}
|
||||
```
|
||||
|
||||
The output directory must be absent, absolute, and have an existing parent.
|
||||
The source is snapshotted in a private workspace, and the engine writes only to
|
||||
its own staging directory. Reported module paths, input identities, UTF-8 byte
|
||||
ranges, regular files and emitted map representations are validated before
|
||||
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.
|
||||
|
||||
Published contents:
|
||||
|
||||
- `inputs/bundle.js`: exact original snapshot; its digest binds the Evidence.
|
||||
- `modules/`: derived modules and any upstream-emitted source maps.
|
||||
- `reports/stdout.json` and `reports/provenance.json`: exact producer reports.
|
||||
- `manifest.json`: identities, modules, transform choices, warnings and limitations.
|
||||
|
||||
The inline result includes complete file paths/digests/sizes, extraction ranges,
|
||||
warnings and `analysis_input`, directly usable as the arguments to
|
||||
`analyze_javascript_application`. Raw Evidence retains selected command arguments,
|
||||
reported input paths, exit status, stderr and reports; temporary paths are recorded
|
||||
as historical execution coordinates after their workspace is removed.
|
||||
|
||||
## Transform choices and interpretation
|
||||
|
||||
`extraction_mode` defaults to `structural` (Wakaru `--unpack=strict`). `heuristic`
|
||||
uses `--unpack=auto`; `inspection` uses `--unpack=inspect` and can return finer,
|
||||
non-executable regions. `rewrite_level` accepts `minimal`, `standard` (default)
|
||||
and `aggressive`. Maps and provenance are requested in every operation.
|
||||
|
||||
Structural extraction can fall back to a single file. In the real fixtures,
|
||||
webpack 5 yields two modules; the esbuild scope-hoisted fixture remains one
|
||||
unknown-format script. Returned module counts and detected formats report those
|
||||
actual outcomes. A valid report with failed transformations returns `partial`
|
||||
with original warnings and files. An unusable report, inconsistent provenance,
|
||||
escaping path, symlink or missing module is an error, with owned cleanup.
|
||||
|
||||
Evidence confidence is `derived`. Extraction ranges are half-open **UTF-8 byte
|
||||
offsets in the original snapshot**, rather than rewritten line positions or a
|
||||
coverage assertion. Source maps are preserved without claiming independently
|
||||
verified mapping accuracy. Original variable names and arbitrary runtime
|
||||
equivalence remain unknown. No network fetching occurs.
|
||||
|
||||
## Resource policy and verification
|
||||
|
||||
Each operation uses one worker, a 1 GiB address-space ceiling and a 120-second
|
||||
deadline; it accepts up to 64 MiB of input and 128 MiB/10000 entries of staged
|
||||
output, with 8 MiB per diagnostic/report stream. Exceeding a limit is an explicit
|
||||
failure with rollback. These limits apply per operation and do not claim aggregate
|
||||
host containment across concurrent callers.
|
||||
|
||||
See [testing.md](testing.md#javascript-source-recovery) for the focused real
|
||||
Wakaru CLI/MCP lane and its source-owned compiler fixtures. Other host platforms
|
||||
and Bun/Vue recovery are not claimed by this adapter.
|
||||
@@ -1,35 +1,79 @@
|
||||
# Passive Node and Electron runtime observation
|
||||
|
||||
REA can attach to an already-running Node.js or Electron V8 Inspector target
|
||||
and retain bounded script-load and execution-context metadata. The
|
||||
and retain script-load and execution-context metadata. The
|
||||
`list_javascript_runtime_targets` and `observe_javascript_runtime` MCP tools
|
||||
have equivalent `rea list-javascript-runtime-targets` and
|
||||
`rea observe-javascript-runtime` commands. Successful calls return
|
||||
deterministic Evidence v2.
|
||||
deterministic Evidence.
|
||||
|
||||
This authority is disabled by default and is separate from browser CDP,
|
||||
Electron file-page inspection, Process Capture, and controlled JavaScript
|
||||
replay:
|
||||
Each request supplies the loopback Inspector endpoint and, for observation, the
|
||||
target ID. This surface is separate from browser CDP, Electron file-page
|
||||
inspection, and Process Capture:
|
||||
|
||||
```bash
|
||||
export REA_V8_INSPECTOR_OBSERVE_ENABLED=true
|
||||
export REA_V8_INSPECTOR_ENDPOINTS_JSON='["http://127.0.0.1:9229"]'
|
||||
export REA_V8_INSPECTOR_FILE_ROOTS_JSON='["/absolute/path/to/app"]'
|
||||
export REA_V8_INSPECTOR_ALLOWED_ORIGINS_JSON='[]'
|
||||
|
||||
rea list-javascript-runtime-targets http://127.0.0.1:9229 \
|
||||
--approved --json
|
||||
--json
|
||||
rea observe-javascript-runtime http://127.0.0.1:9229 TARGET_ID \
|
||||
--runtime-kind node --approved --json
|
||||
--runtime-kind node --json
|
||||
```
|
||||
|
||||
At least one canonical file root or exact HTTP(S) origin is required.
|
||||
Discovery is restricted to a literal-loopback HTTP endpoint. A target must
|
||||
match the exact requested target ID, expose a same-port WebSocket reported by
|
||||
the approved discovery endpoint, and remain inside the requested file or
|
||||
origin scope. Targets already marked attached are rejected rather than
|
||||
displacing another debugger. Canonical file paths are checked after symlink resolution.
|
||||
Excluded locations are counted but never retained.
|
||||
## Start a caller-owned Node target
|
||||
|
||||
Run your selected application with Inspector bound to loopback:
|
||||
|
||||
```bash
|
||||
node --inspect=127.0.0.1:9229 ./app.mjs
|
||||
```
|
||||
|
||||
Keep it running while listing targets and observing the selected ID. Use a free
|
||||
port and the same literal endpoint in both REA calls. Stop the application
|
||||
through its normal lifecycle when finished; REA disconnects but does not own or
|
||||
terminate it. Node.js must already be installed.
|
||||
|
||||
Use `--inspect` for this passive workflow. `--inspect-brk` pauses before the
|
||||
application starts, and REA never resumes it or sends
|
||||
`Runtime.runIfWaitingForDebugger`. Resume a paused target with a separate
|
||||
caller-owned debugger before requesting application runtime observations.
|
||||
|
||||
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 already marked attached are rejected rather than displacing another debugger. Local paths are canonicalized after symlink resolution.
|
||||
|
||||
`list_javascript_runtime_targets` returns every available target in one inline
|
||||
array.
|
||||
|
||||
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
|
||||
|
||||
Node's `/json/list` reports a best-effort pathname prefixed with `file://`,
|
||||
without URI encoding. On POSIX, REA interprets unambiguous paths literally,
|
||||
including `#` and `%` in filenames. Node also replaces backslashes and double
|
||||
quotes with underscores. That transformation cannot be reversed reliably:
|
||||
an underscore may already be part of the original filename.
|
||||
|
||||
Windows paths, ambiguous underscore paths, and local discovery paths that
|
||||
cannot be verified remain eligible by their endpoint and exact target ID.
|
||||
Their target location preserves the reported value:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "unresolved",
|
||||
"reported_url": "file://C:_tools_entry.js",
|
||||
"reason": "unverifiable-file-location"
|
||||
}
|
||||
```
|
||||
|
||||
This location does not claim that a file exists or authorize reading its
|
||||
contents. Listing remains HTTP-only. Observation resolves loaded script
|
||||
locations independently from `Debugger.scriptParsed` file URLs, preserving
|
||||
the existing checks for local files and encoded separators. Unsupported
|
||||
schemes, remote file hosts, and a bare `file://` remain excluded. These Node
|
||||
discovery rules do not apply to page targets or other runtime products.
|
||||
|
||||
Reconciliation retains an unresolved `target_location` object and reports
|
||||
`runtime-location-unresolved` with unknown confidence for that target. It
|
||||
does not derive a static match from the reported pathname; verified loaded
|
||||
script locations remain available for independent matching.
|
||||
|
||||
## Passive protocol boundary
|
||||
|
||||
@@ -70,31 +114,30 @@ Electron role, so Evidence records the role authority as
|
||||
`caller-declared-unverified`.
|
||||
|
||||
Electron main, preload, and renderer behavior can therefore be observed only
|
||||
as separate approved Inspector targets. The provider does not infer that two
|
||||
as separate Inspector targets. The provider does not infer that two
|
||||
targets belong to the same Electron application.
|
||||
|
||||
## Bounds and determinism
|
||||
## Capture and determinism
|
||||
|
||||
Every observation commits the window and exact limits for events, scripts,
|
||||
execution contexts, per-location bytes, and total retained metadata bytes.
|
||||
Limit hits increment dropped-event counts and set `capture.truncated`; they
|
||||
never become completeness claims. Scripts are authorized after capture,
|
||||
deduplicated by stable metadata, and canonically sorted. Wall-clock timestamps
|
||||
and protocol script IDs are excluded from the durable result, so identical
|
||||
inputs and captured metadata produce the same Evidence ID.
|
||||
Every observation records its time window and returns all valid script and
|
||||
execution-context events received during it. Per-location protocol validation
|
||||
remains in force. Scripts are validated after capture, deduplicated by stable
|
||||
metadata, and canonically sorted. Wall-clock timestamps and protocol script IDs
|
||||
are excluded from the durable result, so identical inputs and captured metadata
|
||||
produce the same Evidence ID.
|
||||
|
||||
Inspector attach is inherently incomplete. Enabling `Debugger` reports known
|
||||
and uncollected scripts, but scripts collected before attachment may be
|
||||
missing. Absence from a bounded capture never proves that a script or behavior
|
||||
does not occur.
|
||||
missing. Absence from the observation window never proves that a script or
|
||||
behavior does not occur.
|
||||
|
||||
## Static correlation
|
||||
|
||||
Pass the resulting observation Evidence and an
|
||||
`analyze_javascript_application` Evidence record to
|
||||
`reconcile_javascript_runtime`. The reconciliation accepts this provider
|
||||
alongside passive web and Electron page captures. Exact approved file or URL
|
||||
mappings can correlate script presence with JavaScript Application Graph
|
||||
alongside passive web and Electron page captures. Exact file or URL mappings
|
||||
supplied with the request can correlate script presence with JavaScript Application Graph
|
||||
assets. Because this provider never reads source bytes, matches normally use a
|
||||
unique authorized location and remain weaker than captured-byte identity.
|
||||
Builtin `node:` scripts remain runtime-only nodes unless a future explicit
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
REA can combine an existing static JavaScript Application Graph with one or
|
||||
more passive browser, Electron page, or Node/Electron V8 Inspector captures without collapsing their different
|
||||
authorities. The `reconcile_javascript_runtime` MCP tool and
|
||||
`rea reconcile-javascript-runtime` CLI command accept Evidence v2 records and
|
||||
return derived Evidence v2 containing a combined JavaScript Application Graph
|
||||
`rea reconcile-javascript-runtime` CLI command accept Evidence records and
|
||||
return derived Evidence containing a combined JavaScript Application Graph
|
||||
and explicit match, ambiguity, mismatch, and unknown classifications.
|
||||
|
||||
The operation performs no filesystem, browser, or Electron I/O. It accepts only
|
||||
semantically verified Evidence produced by `analyze_javascript_application`,
|
||||
`inspect_web_page`, `inspect_electron_page`, or
|
||||
`observe_javascript_runtime`; matching operation names alone
|
||||
is insufficient. Provider identity, predicate version, subject digest, target
|
||||
is insufficient. Provider identity, predicate type, subject digest, target
|
||||
parameters, and normalized-result schemas are checked before reconciliation.
|
||||
|
||||
## Workflow
|
||||
@@ -21,7 +21,7 @@ parameters, and normalized-result schemas are checked before reconciliation.
|
||||
supplied as additional layers.
|
||||
2. Run `inspect_web_page`, `inspect_electron_page`, or the attach-only
|
||||
`observe_javascript_runtime`. Capturing script sources
|
||||
is optional, but an approved source capture provides the strongest byte
|
||||
is optional, but a source capture provides the strongest byte
|
||||
identity.
|
||||
3. Pass the resulting Evidence records to `reconcile_javascript_runtime`.
|
||||
Browser URLs normally need an explicit URL-prefix mapping. Extracted
|
||||
@@ -30,7 +30,7 @@ parameters, and normalized-result schemas are checked before reconciliation.
|
||||
and asset roots need explicit mappings.
|
||||
|
||||
The MCP input has this shape; the `analysis` and `runtime_observations` values
|
||||
are complete Evidence v2 objects, not only evidence IDs:
|
||||
are complete Evidence objects, not only evidence IDs:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -47,12 +47,7 @@ are complete Evidence v2 objects, not only evidence IDs:
|
||||
]
|
||||
}
|
||||
],
|
||||
"runtime_observations": ["INSPECT_WEB_PAGE_EVIDENCE"],
|
||||
"limits": {
|
||||
"max_runtime_entities": 10000,
|
||||
"max_reconciliation_items": 20000,
|
||||
"max_static_load_states": 20000
|
||||
}
|
||||
"runtime_observations": ["INSPECT_WEB_PAGE_EVIDENCE"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -82,18 +77,16 @@ npm run verify:javascript-runtime -- \
|
||||
--mapping '{"layer":1,"kind":"file-root","root":"/runtime/cache","artifact_prefix":""}'
|
||||
```
|
||||
|
||||
The verifier analyzes the supplied static paths under a temporary in-process
|
||||
permission policy, reads bounded Evidence JSON files, and prints graph IDs,
|
||||
evidence IDs, paths, digests, counts, reasons, and coverage. It does not print
|
||||
captured source text. `--source-map-read-approved` remains a distinct explicit
|
||||
approval.
|
||||
The verifier analyzes the supplied static paths, reads bounded Evidence JSON
|
||||
files, and prints graph IDs, evidence IDs, paths, digests, counts, reasons, and
|
||||
coverage. It does not print captured source text.
|
||||
|
||||
## Matching rules
|
||||
|
||||
Runtime targets, frames, scripts, and workers become capture-scoped graph
|
||||
nodes. REA evaluates compatible static candidates conservatively:
|
||||
|
||||
1. An approved captured-source SHA-256 plus one matching mapped location yields
|
||||
1. A captured-source SHA-256 plus one matching mapped location yields
|
||||
a content-and-location match.
|
||||
2. One unique exact content digest can match even when no location mapping is
|
||||
available. Whole-asset digests and reconstructed module-factory source
|
||||
@@ -101,13 +94,13 @@ nodes. REA evaluates compatible static candidates conservatively:
|
||||
3. Without captured bytes, one unique mapped artifact path can produce a
|
||||
medium-confidence location match.
|
||||
4. Multiple candidates remain `ambiguous`; no `observed_as` edge is emitted.
|
||||
`candidate_static_count` reports the full qualified layer/node count while
|
||||
`candidate_static_nodes` retains a deterministic bounded prefix.
|
||||
`candidate_static_count` reports the full qualified layer/node count, and
|
||||
`candidate_static_nodes` includes every unique candidate reference.
|
||||
5. If captured bytes disagree with the static candidate at the mapped path,
|
||||
the result is an explicit mismatch. A path never overrides a digest
|
||||
disagreement.
|
||||
6. Missing authorization mappings, incomplete input coverage, and output
|
||||
limits remain unknown or truncated rather than becoming absence claims.
|
||||
6. Missing authorization mappings and incomplete or truncated input coverage
|
||||
remain unknown or truncated rather than becoming absence claims.
|
||||
|
||||
Every successful cross-layer `observed_as` edge has
|
||||
`authority: cross-layer-reconciliation` and `state: inferred`. The underlying
|
||||
@@ -124,7 +117,7 @@ classification:
|
||||
- `resident-in-loaded-asset` means a containing asset was observed, but the
|
||||
embedded module was not independently observed or shown to execute;
|
||||
- `not-observed-in-capture` means the entity did not appear in the complete
|
||||
bounded captures supplied to this call and its artifact path falls beneath
|
||||
authorized captures supplied to this call and its artifact path falls beneath
|
||||
an exercised automatic or operator-declared mapping prefix;
|
||||
- `unknown` covers incomplete captures, layers outside runtime scope, and
|
||||
unresolved correspondence. Nodes outside a mapped artifact prefix stay
|
||||
@@ -149,25 +142,25 @@ EventEmitter activity, Electron IPC, or script-unload evidence. A unique
|
||||
authorized file or URL location can still correlate script presence with a
|
||||
static asset, but it does not prove module initialization or feature execution.
|
||||
|
||||
Source-map declarations and approved original-source reads stay under static
|
||||
source-map authority. They are reported in `source_map_authority` but never used
|
||||
Source-map declarations and original-source reads remain static evidence. They
|
||||
are reported in `source_map_authority` but never used
|
||||
as primary runtime byte matches. A source name or `sourcesContent` digest is not
|
||||
silently equated with the generated bytes observed by CDP.
|
||||
|
||||
## Bounds and determinism
|
||||
|
||||
Inputs allow up to eight static layers and 32 passive captures. Default output
|
||||
bounds retain 10,000 runtime entities, 20,000 reconciliation records, and
|
||||
20,000 static load states; each caller-visible maximum is 50,000. At least one
|
||||
target node per capture must fit. The combined graph also preserves the JAG v1
|
||||
caps of 1,000 roots, 100,000 nodes, 200,000 edges, and 64 observations per
|
||||
node. Reaching a limit reports exact omitted counts for runtime entities,
|
||||
reconciliation items, static load states, and merged graph items, plus
|
||||
`coverage.status: truncated`.
|
||||
The workflow does not impose aggregate count caps on static layers, passive
|
||||
captures, runtime entities, reconciliation items, static load states, runtime
|
||||
mapping declarations, or candidate references. These collections remain
|
||||
subject to the structural validation applied to each Evidence record and
|
||||
application graph; passive observation returns all in-window collection items
|
||||
that pass per-value validation. Reconciliation reports zero
|
||||
omitted items when it projects the complete supplied inputs. Static JavaScript
|
||||
analysis can still report its own bounded AST coverage as partial or truncated.
|
||||
|
||||
Layers, captures, nodes, edges, matches, and evidence links are canonically
|
||||
ordered. Repeating the same verified Evidence, mappings, and limits produces
|
||||
the same reconciliation, graph, and Evidence identifiers.
|
||||
ordered. Repeating the same verified Evidence and mappings produces the same
|
||||
reconciliation, graph, and Evidence identifiers.
|
||||
|
||||
## Non-goals
|
||||
|
||||
|
||||
+171
-97
@@ -1,26 +1,45 @@
|
||||
# Managed-code analysis plan
|
||||
# Managed-code analysis and planned extensions
|
||||
|
||||
This document turns
|
||||
[ADR-0003](adr/0003-managed-code-evidence-and-provider-boundary.md) into an
|
||||
implementation and verification plan. REA currently ships read-only PE/CLI
|
||||
triage and exact identity through `inspect_managed_artifact` and
|
||||
`rea inspect-managed-artifact`, plus bounded metadata, signature, method-body
|
||||
CIL, exception-region, call-edge, and field-access inspection through
|
||||
`inspect_managed_members` and `rea inspect-managed-members`, and
|
||||
declared ModuleRef/ImplMap/PInvoke and native implementation boundary inventory
|
||||
through `inspect_managed_native_boundaries` and
|
||||
`rea inspect-managed-native-boundaries`, plus obfuscation-resistant member
|
||||
comparison through `compare_managed_members` and `rea compare-managed-members`,
|
||||
managed/native export or function Evidence matching through
|
||||
`verify_managed_native_boundaries` and `rea verify-managed-native-boundaries`,
|
||||
decompiler reconstruction import through `import_managed_reconstruction` and
|
||||
`rea import-managed-reconstruction`, and default-disabled runtime-correlation
|
||||
admission planning through `plan_managed_runtime_correlation` and
|
||||
`rea plan-managed-runtime-correlation`, plus static managed graph projection
|
||||
through `project_managed_application_graph` and
|
||||
`rea project-managed-application-graph`. Native-body bridge mapping and runtime
|
||||
execution remain planned behavior. The current product inventory remains the
|
||||
one in [`product-catalog.json`](product-catalog.json).
|
||||
REA inspects .NET PE/CLI artifacts without loading or executing their code.
|
||||
It can identify an assembly, inspect metadata and CIL, compare members across
|
||||
builds, and connect declared native calls to supplied native-analysis evidence.
|
||||
CIL is Common Intermediate Language, the instruction format used by managed
|
||||
assemblies.
|
||||
|
||||
The seven shipped tools are:
|
||||
|
||||
| Task | MCP tool |
|
||||
| --------------------------------------------------------------------------- | ----------------------------------- |
|
||||
| Identify an artifact and its managed deployment form | `inspect_managed_artifact` |
|
||||
| Inspect types, signatures, method bodies, calls, and field access | `inspect_managed_members` |
|
||||
| List declared native calls and native implementation indicators | `inspect_managed_native_boundaries` |
|
||||
| Compare members across builds and map build-local tokens | `compare_managed_members` |
|
||||
| Check native call declarations against supplied export or function evidence | `verify_managed_native_boundaries` |
|
||||
| Import decompiled code against verified static member identities | `import_managed_reconstruction` |
|
||||
| Add managed findings to an application graph | `project_managed_application_graph` |
|
||||
|
||||
Each has a matching CLI command: replace underscores with hyphens and prefix
|
||||
the name with `rea`, for example `rea inspect-managed-artifact`.
|
||||
Native-body bridge mapping remains planned; managed runtime execution is not
|
||||
part of the current tool set.
|
||||
|
||||
This guide describes the implementation and verification of
|
||||
[ADR-0003](adr/0003-managed-code-evidence-and-provider-boundary.md). The canonical
|
||||
tool inventory is [`product-catalog.json`](https://github.com/morluto/rea/blob/main/docs/product-catalog.json).
|
||||
|
||||
## Shipped scope
|
||||
|
||||
The canonical parser admits PE/CLI bytes and their metadata/CIL without loading
|
||||
an assembly. It reports observed implementation markers and unavailable facts;
|
||||
it does not unpack .NET single-file hosts, decode IL2CPP metadata, or infer a
|
||||
NativeAOT identity from an ordinary native PE. Inputs without admitted CLI
|
||||
metadata do not become managed assemblies through naming or routing guesses.
|
||||
Inspect separately obtained components explicitly and preserve their identities.
|
||||
|
||||
The broader deployment classification and native-body mapping below are design
|
||||
goals from ADR-0003, not claims that every row has an implemented parser. A
|
||||
valid marker can establish a candidate native boundary, not recovered native
|
||||
semantics or runtime behavior.
|
||||
|
||||
## Analysis objective
|
||||
|
||||
@@ -32,15 +51,15 @@ collapsing them:
|
||||
3. What source-like or behavioral structure can be reconstructed or inferred?
|
||||
4. Which findings survive an exact-build check or a cross-build structural
|
||||
comparison?
|
||||
5. Which remaining questions require native analysis or a separately
|
||||
authorized runtime experiment?
|
||||
5. Which remaining questions require native analysis or runtime evidence that
|
||||
static inspection cannot supply?
|
||||
|
||||
The ordinary workflow ends at question four. Static analysis never loads or
|
||||
executes the target.
|
||||
|
||||
## Classification workflow
|
||||
## Planned deployment classification
|
||||
|
||||
Classification proceeds from the outermost authenticated bytes inward:
|
||||
The intended extended classifier proceeds from outermost authenticated bytes inward:
|
||||
|
||||
```text
|
||||
source path
|
||||
@@ -53,7 +72,7 @@ source path
|
||||
-> managed-only, native-only, composed, degraded, or unsupported route
|
||||
```
|
||||
|
||||
The result is a vector rather than one label. For example, a single-file modern
|
||||
The planned result is a vector rather than one label. For example, a single-file modern
|
||||
.NET deployment can contain a native host, ordinary CIL assemblies, and
|
||||
ReadyToRun components. Each component receives its own digest, classification,
|
||||
coverage, and route while retaining the outer bundle commitment.
|
||||
@@ -71,6 +90,51 @@ coverage, and route while retaining the outer bundle commitment.
|
||||
| Obfuscated assembly | Same positive byte observations as its underlying deployment form | Meaning inferred only from names |
|
||||
| Malformed/unsupported | Exact admitted regions and failure locations | Completeness, successfully skipped rows, or semantics beyond the admitted parser boundary |
|
||||
|
||||
### NativeAOT metadata recovery
|
||||
|
||||
NativeAOT removes the ordinary CIL method bodies, so a CIL decompiler cannot
|
||||
reconstruct those bodies as C# source. The executable remains a native target
|
||||
for Hopper or Ghidra, where decompilation produces pseudocode and analyst
|
||||
inference. This is a different route from ordinary managed member inspection.
|
||||
|
||||
NativeAOT is not limited to Windows `.exe` files. The native image can be PE,
|
||||
ELF, or Mach-O, and NativeAOT can also publish shared libraries (PE DLL, ELF
|
||||
`.so`, or Mach-O `.dylib`) with explicitly exported entry points. Identify the
|
||||
image from its bytes and loader metadata, not its filename extension. When the
|
||||
image is inside an application bundle, inventory or extract the bundle first
|
||||
and carry the component identity into native analysis. Matching PDB, ELF debug,
|
||||
or dSYM sidecars can add symbol evidence when the selected provider supports
|
||||
them; verify their identity and keep them separate from observations made from
|
||||
the executable itself. [Microsoft's NativeAOT overview](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/)
|
||||
lists supported OS/architecture targets and deployment limitations, while its
|
||||
[native library guide](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/libraries)
|
||||
describes exported shared-library entry points.
|
||||
|
||||
The [Ghidra NativeAOT analyzer](https://github.com/Washi1337/ghidra-nativeaot)
|
||||
is a useful optional companion for recovering ReadyToRun metadata. It
|
||||
rehydrates the `DEHYDRATED_DATA` section and annotates method tables, type
|
||||
relationships, vtable slots, frozen objects, and strings. It does not recover
|
||||
original C# method bodies. It is a separate Ghidra extension and interactive
|
||||
metadata browser; REA does not currently install or invoke it through its
|
||||
headless bridge. Its README describes ReadyToRun header discovery, including
|
||||
symbol-based and heuristic paths, so a missed header or unsupported binary
|
||||
should remain an explicit limitation rather than a failed claim about the
|
||||
binary's contents.
|
||||
|
||||
The upstream analyzer currently gates its own analysis to x86-64. NativeAOT's
|
||||
broader platform matrix does not establish REA coverage: PE/COFF, ELF, Mach-O,
|
||||
shared libraries, target architectures, and runtime metadata versions each need
|
||||
their own provider verification. REA's experimental Windows Ghidra boundary is
|
||||
limited to admitted native x86-64 PE applications, so it does not currently
|
||||
establish coverage for PE DLLs. Mobile NativeAOT targets should remain
|
||||
experimental until REA has a matching provider and package-level verification.
|
||||
|
||||
For REA's native analysis, preserve the same artifact path and digest used by
|
||||
`inspect_managed_artifact`, and report recovered type data as provider
|
||||
observations. Linking those types to native function addresses requires verified
|
||||
provider evidence; names or vtable similarity alone do not prove that a native
|
||||
function implements a specific managed method.
|
||||
|
||||
## Evidence record shape
|
||||
|
||||
Every planned operation returns a provider result and Evidence with four
|
||||
@@ -91,7 +155,7 @@ commitment groups:
|
||||
- module name, generation, and MVID;
|
||||
- target framework and runtime/version strings with their exact metadata
|
||||
locations;
|
||||
- parser/provider version, profile schema, limits, and profile digest.
|
||||
- provider identity, analysis-affecting parameters, and their profile digest.
|
||||
|
||||
CLI `#GUID` heap values are committed as their exact 16-byte GUID text. REA
|
||||
does not impose RFC 4122 UUID version or variant bits on MVID, EncId, or
|
||||
@@ -106,23 +170,22 @@ EncBaseId because ECMA-335 metadata does not require those bit patterns.
|
||||
- exact raw bytes or bounded value digest;
|
||||
- raw CIL digest plus separately reported method-header, locals-token, and
|
||||
exception-region observations where available;
|
||||
- a deterministic v1 decoded-instruction-tuple digest for completely decoded
|
||||
- a deterministic decoded-instruction-tuple digest for completely decoded
|
||||
CIL, with the limitations below.
|
||||
|
||||
### Shipped decoded-CIL fingerprint v1
|
||||
### Shipped decoded-CIL fingerprint
|
||||
|
||||
`inspect_managed_members` exposes two method CIL hashes with different byte
|
||||
boundaries:
|
||||
|
||||
| Field | Shipped v1 input | Excluded from the digest |
|
||||
| Field | Input | Excluded from the digest |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `il_sha256` | The exact `il_size` raw CIL bytes after the tiny or fat method header | Method header, locals signature blob, alignment, and extra sections including exception clauses |
|
||||
| `normalized_il_sha256` | UTF-8 `JSON.stringify` output for the instruction-order array of `[opcode, operand_kind, operand]` tuples | Instruction offsets except projected scalar branch targets, switch targets, resolved metadata/string identities, method header, locals, and exception regions |
|
||||
|
||||
The normalized field is available only when the entire CIL stream decodes with
|
||||
status `present`. A partial, malformed, absent, or over-limit body reports
|
||||
`null`. The containing member result's `schema_version: 1` scopes the tuple
|
||||
format; v1 does not expose a separate algorithm-version field.
|
||||
status `present`. A malformed or absent body reports `null`. The tuple format
|
||||
is defined here.
|
||||
|
||||
Tuple operands use these exact projections:
|
||||
|
||||
@@ -134,17 +197,17 @@ 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.
|
||||
|
||||
Opcode names retain short/long forms and prefix opcodes. This makes v1 a
|
||||
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
|
||||
exception semantics, and does not replace the raw CIL digest. Cross-build
|
||||
comparison uses the value only as one unique-only structural tier and reports
|
||||
ambiguity when that evidence is insufficient. A future normalized-CIL v2 must
|
||||
have an explicit algorithm version, canonical token/literal identities,
|
||||
ambiguity when that evidence is insufficient. A future normalized-CIL
|
||||
commitment would need canonical token/literal identities,
|
||||
complete branch and switch targets, and explicit locals/exception semantics.
|
||||
|
||||
For example, a decoded `nop; ret` body serializes exactly as
|
||||
`[["nop","none",null],["ret","none",null]]` and has v1 digest
|
||||
`[["nop","none",null],["ret","none",null]]` and has digest
|
||||
`5e5fad7741cb44bca3a4f045546b7449990da343f612f5f34c0ca30e9eee0636`.
|
||||
This vector specifies the tuple order, `null` handling, absence of whitespace,
|
||||
UTF-8 encoding, and lowercase hexadecimal output.
|
||||
@@ -152,7 +215,7 @@ UTF-8 encoding, and lowercase hexadecimal output.
|
||||
### Epistemic commitment
|
||||
|
||||
- authority: static bytes, reconstruction, structural inference, independent
|
||||
validation, native provider, or separately authorized runtime;
|
||||
validation, native provider, or future runtime observation;
|
||||
- state: observed, inferred, unknown, or unavailable;
|
||||
- confidence independent of authority;
|
||||
- coverage and admitted/dropped counts;
|
||||
@@ -170,7 +233,7 @@ that implement them. The underlying capability slices are:
|
||||
1. **Triage and inventory**: container/component classification, assembly and
|
||||
module identity, references, files, exported types, resources, attributes,
|
||||
and runtime markers.
|
||||
2. **Member inventory**: bounded types, methods, fields, properties, events,
|
||||
2. **Member inventory**: file-backed types, methods, fields, properties, events,
|
||||
interfaces, nesting, generics, and normalized signatures.
|
||||
3. **Method inspection**: headers, max stack, locals, CIL instructions,
|
||||
constants, metadata operands, exception regions, and implementation flags.
|
||||
@@ -191,9 +254,11 @@ that implement them. The underlying capability slices are:
|
||||
Evidence-backed application graph vocabulary as JavaScript/Electron
|
||||
findings while preserving managed static-analysis authority.
|
||||
|
||||
Every list has a deterministic order and hard limits. Search exposes scanned,
|
||||
matched, returned, and dropped counts. An unresolved indirect call or a failed
|
||||
signature decode is not silently omitted from a completeness claim.
|
||||
Inventory lists preserve deterministic order and include every fact available
|
||||
from the admitted PE/CLI streams. Malformed ranges and unsupported metadata are
|
||||
reported as coverage issues instead of being silently omitted. Search exposes
|
||||
scanned, matched, returned, and dropped counts. An unresolved indirect call or
|
||||
a failed signature decode is not silently omitted from a completeness claim.
|
||||
|
||||
## Obfuscation-resistant method slices
|
||||
|
||||
@@ -205,7 +270,7 @@ bounded set of observations needed to investigate a proposition:
|
||||
- exact strings, numeric constants, enum-like values, and serialization keys;
|
||||
- branch, switch, exception, loop, call, and field-flow shape;
|
||||
- construction and generic-instantiation sites;
|
||||
- raw CIL and decoded-tuple-v1 commitments, plus separate header, locals, and
|
||||
- raw CIL and decoded-tuple commitments, plus separate header, locals, and
|
||||
exception observations;
|
||||
- callers, callees, shared fields, and competing candidates; and
|
||||
- limitations from reflection, dynamic dispatch, generated code, protection,
|
||||
@@ -215,32 +280,40 @@ The workflow produces separate observation, inference, validation, and unknown
|
||||
tables. It may say that a method is a strong candidate for a role; it cannot
|
||||
turn that role into the method's durable identity.
|
||||
|
||||
Cross-version matching is two-stage:
|
||||
Cross-version matching first prefers exact CIL/signature identity. When that
|
||||
does not match, it pairs an exact declared type, method name, and raw signature
|
||||
before trying structural body shape. The exact-signature key uses names only
|
||||
as part of that full tuple; names alone never select a pair. Duplicate tuples
|
||||
remain ambiguous rather than being paired by token order. Fields follow the
|
||||
same exact tiers. The raw signature is exact whether or not REA decoded it, so
|
||||
undecoded signatures pair by that tuple too, but they never enter structural
|
||||
rounds. An unmatched member is therefore reported as `unknown`, not added or
|
||||
removed, when its own signature was not decoded or when the other side has an
|
||||
unpaired member, one-sided or ambiguous, with the same declared type and name
|
||||
whose signature was not decoded.
|
||||
|
||||
1. Exact identity requires compatible artifact/module commitments and exact
|
||||
raw CIL identity; v1 does not expose a standalone complete-method-body
|
||||
digest.
|
||||
2. Structural identity compares schema-versioned signatures, the limited v1
|
||||
decoded-CIL tuple fingerprint, constants, and bounded shape context. It
|
||||
reports all candidates at the winning score and remains ambiguous when the
|
||||
evidence does not distinguish them. The v1 digest does not itself remap
|
||||
metadata tokens.
|
||||
For a matched method, an unavailable or partial body makes body-shape facets
|
||||
unknown while preserving observed signature differences. Structural identity
|
||||
compares normalized signatures, the limited decoded-CIL tuple fingerprint,
|
||||
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.
|
||||
|
||||
Tokens are always remapped through observed structure. A caller cannot carry
|
||||
`0x06001234` into a new MVID and assume it names the same method.
|
||||
|
||||
## Managed/native boundary rules
|
||||
|
||||
| Boundary | Managed observation | Native observation | Permitted link |
|
||||
| ----------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| P/Invoke | Module, entry point, charset/calling-convention flags, declaring method | Import/export/symbol/function evidence from the selected deep provider | Exact declared-name/module link or qualified resolution inference |
|
||||
| COM | Interop attributes, GUIDs, imported interfaces, method signatures | Native registration/vtable evidence when independently available | Identifier/signature inference with explicit environment limitations |
|
||||
| C++/CLI | Managed declaration and implementation flags | Native body/function evidence | Only a provider-supported bridge observation; never token-as-address |
|
||||
| ReadyToRun | Component/header and per-method CIL/native availability | Native section/function evidence | Authenticated image mapping with format/profile version |
|
||||
| Unmanaged export | Export metadata/attribute when present | PE export and native thunk/function | Exact export identity plus provider-qualified address |
|
||||
| Single-file host | Bundle entry/component identity | Host and native component evidence | Outer bundle plus component digest/extent commitment |
|
||||
| Unity IL2CPP | Supported metadata entity with authenticated pairing | Generated native function/type evidence | Versioned IL2CPP mapping only; no invented CIL |
|
||||
| Runtime-resolved native | API/constant/data-flow candidate | Loaded-module/symbol observation only under separate runtime execution authority | Static candidate remains inference until separately observed |
|
||||
| Boundary | Managed observation | Native observation | Permitted link |
|
||||
| ----------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| P/Invoke | Module, entry point, charset/calling-convention flags, declaring method | Import/export/symbol/function evidence from the selected deep provider | Exact declared-name/module link or qualified resolution inference |
|
||||
| COM | Interop attributes, GUIDs, imported interfaces, method signatures | Native registration/vtable evidence when independently available | Identifier/signature inference with explicit environment limitations |
|
||||
| C++/CLI | Managed declaration and implementation flags | Native body/function evidence | Only a provider-supported bridge observation; never token-as-address |
|
||||
| ReadyToRun | Component/header and per-method CIL/native availability | Native section/function evidence | Authenticated image mapping with format/profile version |
|
||||
| Unmanaged export | Export metadata/attribute when present | PE export and native thunk/function | Exact export identity plus provider-qualified address |
|
||||
| Single-file host | Bundle entry/component identity | Host and native component evidence | Outer bundle plus component digest/extent commitment |
|
||||
| Unity IL2CPP | Supported metadata entity with authenticated pairing | Generated native function/type evidence | Versioned IL2CPP mapping only; no invented CIL |
|
||||
| Runtime-resolved native | API/constant/data-flow candidate | Loaded-module/symbol observation from an independently collected runtime source | Static candidate remains inference until separately observed |
|
||||
|
||||
Declared import inventory supports a bounded positive claim. Its absence does
|
||||
not exclude dynamic resolution, generated code, protected code, native helpers,
|
||||
@@ -249,17 +322,20 @@ or server-side behavior.
|
||||
## Tool and packaging boundary
|
||||
|
||||
The production parser is REA-owned TypeScript and ships with the existing Node
|
||||
application. It is verified against the ECMA-335 format and independent pinned
|
||||
oracles but has no runtime dependency on them.
|
||||
application. Deterministic conformance uses source-owned byte-built PE/CLI
|
||||
fixtures and expected semantic facts. An optional real ILSpy lane runs only when
|
||||
its executable is explicitly supplied; the default lane does not establish
|
||||
independent pinned-oracle parity. Production inspection needs none of those tools.
|
||||
|
||||
- `System.Reflection.Metadata` is the primary independent metadata/CIL oracle.
|
||||
- `ICSharpCode.Decompiler` and `ilspycmd` are reconstruction and differential
|
||||
oracles. An admitted BYO reconstruction operation records its exact version
|
||||
- A pinned `System.Reflection.Metadata` differential oracle is planned; it is
|
||||
not part of the current verifier.
|
||||
- `ICSharpCode.Decompiler`/`ilspycmd` can supply reconstruction inference.
|
||||
A BYO reconstruction import records the supplied version
|
||||
and remains non-canonical. When `REA_ILSPY_CMD_PATH` points to an absolute
|
||||
runnable `ilspycmd`, `verify:managed` runs a source-owned real ILSpy oracle
|
||||
and imports its C# output as reconstruction inference against exact static
|
||||
member Evidence.
|
||||
- dnlib and Mono.Cecil may increase differential coverage. Their mutation APIs
|
||||
- dnlib and Mono.Cecil are potential future differential oracles. Their mutation APIs
|
||||
are not exposed or included in the production parsing boundary.
|
||||
- The package contains no .NET runtime, SDK, ILSpy installation, proprietary
|
||||
assembly, or compiled conformance fixture.
|
||||
@@ -280,6 +356,11 @@ by the selected Hopper/Ghidra host and format matrix.
|
||||
|
||||
## Source-built conformance corpus
|
||||
|
||||
The current lane uses source-owned byte-built fixtures and malformed-input
|
||||
regressions, with optional operator-supplied applications and ILSpy verification.
|
||||
The table below describes the desired expanded corpus; it is not a report that
|
||||
all compiler-generated forms have been independently verified.
|
||||
|
||||
Fixture sources are intentionally small and behavior-focused. Build outputs
|
||||
are generated outside tracked fixture directories and must not remain after
|
||||
verification or packaging.
|
||||
@@ -327,7 +408,6 @@ absolute or relative to the manifest file. A compact manifest contains:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"label": "operator-local osu!stable semantic slice",
|
||||
"target": {
|
||||
"path": "./osu!.exe",
|
||||
@@ -373,10 +453,10 @@ Before evaluating methods, the verifier fails closed on target SHA-256, MVID,
|
||||
assembly name, runtime-family, and managed-architecture mismatches whenever the
|
||||
manifest supplies those fields. It then pages each declared MethodDef token
|
||||
directly by row, so selected methods do not need to appear in the first member
|
||||
page of a large application. `il_length` remains accepted as a legacy alias for
|
||||
`il_size`; optional `il_sha256` locks the exact raw CIL bytes in addition to
|
||||
REA's member-result-v1 decoded-instruction-tuple digest. That normalized field
|
||||
has the v1 limits documented above and is not a complete semantic CIL identity.
|
||||
page of a large application. Optional `il_sha256` locks the exact raw CIL bytes
|
||||
in addition to REA's decoded-instruction-tuple digest. That normalized field
|
||||
has the byte boundaries documented above and is not a complete semantic CIL
|
||||
identity.
|
||||
|
||||
The optional `application_graph` block reuses those exact-build method
|
||||
commitments. For each referenced MethodDef token, the verifier builds a bounded
|
||||
@@ -398,25 +478,13 @@ The benchmark may prove that REA reproduces selected facts for that exact local
|
||||
build. It cannot establish general support on its own; source-built conformance
|
||||
remains the admission requirement.
|
||||
|
||||
## Runtime admission boundary
|
||||
## Runtime behavior boundary
|
||||
|
||||
The later runtime track is not an extension flag on a static operation. Its
|
||||
design must expose whether it attaches to an existing process, launches a new
|
||||
process, loads an assembly, uses reflection, installs a debugger/profiler, or
|
||||
instruments code. Each effect needs separate approval and exact-build checks.
|
||||
|
||||
At minimum, admission requires target SHA-256, MVID, normalized signature and
|
||||
body/CIL commitment, CLR family, OS, architecture, tool version, scenario
|
||||
limits, output policy, and cleanup ownership to match. Any mismatch fails before
|
||||
the experiment. It must not contact real services/accounts or claim that an
|
||||
instrumented path represents an ordinary launch unless that proposition is
|
||||
independently tested.
|
||||
|
||||
The shipped `plan_managed_runtime_correlation` path admits only a
|
||||
default-disabled, permission-gated experiment plan and records that no target
|
||||
code was executed. Until a separate executor is designed and shipped, runtime
|
||||
behavior questions remain explicit unknowns with suggested probes; static
|
||||
support does not perform them.
|
||||
Managed inspection does not execute assemblies or observe CLR internals.
|
||||
Questions about actual behavior remain unknown until supported runtime
|
||||
evidence is collected. A direct process capture can retain declared inputs,
|
||||
outputs, filesystem and protocol activity for a target run; it does not prove
|
||||
which managed methods executed or how the CLR resolved them.
|
||||
|
||||
## Delivery sequence
|
||||
|
||||
@@ -424,8 +492,8 @@ The managed-code track advances as reviewable pull requests:
|
||||
|
||||
1. accepted evidence/provider boundary (this document and ADR);
|
||||
2. read-only artifact triage and exact identity (shipped);
|
||||
3. bounded metadata, signatures, method bodies, raw CIL hashes, and the limited
|
||||
decoded-instruction-tuple fingerprint v1 (shipped; normalized-CIL v2
|
||||
3. file-backed metadata, signatures, method bodies, raw CIL hashes, and the
|
||||
decoded-instruction-tuple fingerprint (shipped; complete normalized-CIL
|
||||
semantics remain planned);
|
||||
4. obfuscation-resistant slices and cross-version comparison (shipped for
|
||||
static member observations);
|
||||
@@ -438,9 +506,15 @@ The managed-code track advances as reviewable pull requests:
|
||||
PE/CLI corpus shipped through `npm run verify:managed`; optional BYO
|
||||
`ilspycmd` real-tool oracle shipped through `REA_ILSPY_CMD_PATH`; dnSpy and
|
||||
pinned Windows checks remain planned); and
|
||||
8. separately authorized runtime-correlation admission planning (shipped; no
|
||||
runtime execution);
|
||||
9. managed static Evidence projection into the application graph (shipped).
|
||||
8. managed static Evidence projection into the application graph (shipped).
|
||||
|
||||
Each implementation PR updates generated product facts only for behavior it
|
||||
actually ships and states which real-tool checks were performed.
|
||||
|
||||
### Method metadata during build comparison
|
||||
|
||||
Managed member comparisons report a `metadata` dimension when an observed
|
||||
MethodDef flags or implementation flags value differs, including accessibility
|
||||
or synchronization changes. This is separate from the CIL/signature matching
|
||||
tiers: identical instructions do not establish unchanged method metadata. The
|
||||
reported difference is static metadata evidence, not proof of runtime behavior.
|
||||
|
||||
+180
-105
@@ -2,25 +2,24 @@
|
||||
|
||||
## Identity and discovery
|
||||
|
||||
`rea://server/identity` and `binary_session` report the package, server, SDK,
|
||||
negotiated protocol, skill, and schema-sensitive catalog identities separately.
|
||||
An absent live comparison is `unknown`, never aligned. Callers can supply the
|
||||
expected package version, catalog digest, or registered server path to
|
||||
`binary_session`; a mismatch means the registered MCP process must be restarted.
|
||||
`rea doctor` separately inspects supported JSON/TOML client registrations,
|
||||
reports their command vectors as aligned, stale, missing, or invalid, and keeps
|
||||
live-server state `unknown` unless the active connection supplies identity.
|
||||
`binary_session` reports the active package, server, SDK, and negotiated
|
||||
protocol details. `rea doctor` separately inspects supported JSON/TOML client
|
||||
registrations, reports their command vectors as aligned, stale, missing, or
|
||||
invalid, and keeps live-server state `unknown` unless the active connection
|
||||
supplies identity.
|
||||
|
||||
Canonical tool names remain stable, while `tools/list` advertises only the
|
||||
operations callable for the current target, provider, policy, host, and
|
||||
negotiated client capabilities. `binary_session.tool_availability` remains the
|
||||
complete inventory: it explains advertised and hidden operations with stable
|
||||
availability reasons and remediation. Each entry also reports required and
|
||||
optional negotiated client features plus the currently missing features. Form elicitation is optional for
|
||||
`capture_process_scenario`: an existing grant remains usable without it, while
|
||||
a missing grant cannot be elicited by a client that does not advertise it.
|
||||
Opening or closing a target, reloading policy, or observing a provider health
|
||||
transition emits `notifications/tools/list_changed`.
|
||||
`tools/list` returns the complete canonical tool inventory, including tools that
|
||||
are currently unavailable. Opening or closing a target or observing a provider
|
||||
health transition leaves that catalog unchanged and does not emit
|
||||
`notifications/tools/list_changed`.
|
||||
|
||||
Call `binary_session` with `{}` and read `result.tool_availability` to choose a
|
||||
callable operation for the current target, provider, host, and negotiated client
|
||||
capabilities. The default result includes the complete inventory with each
|
||||
tool's availability, reason, and remediation. Each entry also reports required
|
||||
and optional negotiated client features plus the currently missing features.
|
||||
The optional inputs `expected_package_version`, `expected_catalog_digest`, and
|
||||
`expected_server_path` compare the live session with the caller's expectations.
|
||||
|
||||
`binary_session.analysis_provider_candidates` is authoritative for deep-engine
|
||||
discovery. Target-free discovery is sorted by provider ID, reports host
|
||||
@@ -48,7 +47,7 @@ REA accepts ordinary `tools/call` progress tokens. Updates are monotonic,
|
||||
rate-bounded to at most one intermediate update per 100 ms, and always allow a
|
||||
terminal update. Unknown totals are omitted; REA does not fabricate percentages.
|
||||
Provider calls receive the request cancellation signal. Artifact traversal,
|
||||
hashing, cross-version scanning/checkpoints, Hopper requests, and process capture
|
||||
hashing, version comparisons, Hopper requests, and process capture
|
||||
check the same signal. Cancellation is distinct from timeout. A cleanup failure
|
||||
uses `cleanup_incomplete` and lists only the owned resource kinds that remain.
|
||||
Derived comparisons and reconstruction verification yield before computation
|
||||
@@ -58,106 +57,182 @@ CLI calls work without a progress token and translate SIGINT into the same
|
||||
AbortSignal used by providers. Existing controlled-process cleanup and provider
|
||||
shutdown rules still apply; REA never kills a process it cannot prove it owns.
|
||||
|
||||
## Evidence resources
|
||||
## Ghidra first-query deadlines and recovery
|
||||
|
||||
Evidence remains inline for compatibility and successful Evidence-producing
|
||||
tools also return `resource_link` content. Session resources are connection-local:
|
||||
A successful MCP initialize handshake establishes the REA connection.
|
||||
`open_binary` then selects a target and provider binding; it does not establish
|
||||
that Ghidra has finished importing the target. The first Ghidra-backed query,
|
||||
such as `binary_overview`, starts the engine and waits for import, default
|
||||
auto-analysis, bridge connection, and health readiness before returning analysis.
|
||||
|
||||
- `rea://evidence/{evidenceId}` returns the complete immutable Evidence v2 record.
|
||||
- `rea://evidence/{evidenceId}/section/{section}` returns a bounded result section.
|
||||
Stable sections include `result`, `terminal`, `filesystem`, `process`,
|
||||
`protocol`, `nodes`, `occurrences`, and `edges` when present.
|
||||
- `rea://unknown/{unknownId}` returns the current residual-unknown head and its
|
||||
immutable revision history.
|
||||
- `rea://unknowns/active` returns current unresolved heads.
|
||||
- `rea://snapshot/{snapshotDigest}` returns the current immutable analysis
|
||||
snapshot when its canonical content digest matches.
|
||||
- `rea://artifact/{manifestId}/{collection}` returns a canonical artifact
|
||||
`nodes`, `occurrences`, or `edges` page with Evidence provenance.
|
||||
- `rea://function/{targetSha256}/{address}` returns a retained function dossier
|
||||
for the exact target and address.
|
||||
- `rea://workspace/{workspaceId}/revision/{revision}` returns an immutable,
|
||||
CAS-linked investigation workspace revision retained by this session.
|
||||
These deadlines have different owners:
|
||||
|
||||
Successful automatic cross-version investigations include both Evidence and
|
||||
workspace `resource_link` blocks. Workspace resources preserve revision and
|
||||
`previous_revision_digest` commitments; persistent workspace files remain
|
||||
subject to configured read/write roots.
|
||||
| Deadline | Owner and effect |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| MCP initialize | The client bounds transport/REA connection startup, before any target query. |
|
||||
| Ghidra startup | REA allows 330,000 ms for engine readiness from the first provider query; startup failure is reported by that query. |
|
||||
| Individual tool call | The client bounds its wait, including cold engine startup. The pinned client SDK 2.3.1 defaults to 60,000 ms and can cancel earlier than REA's startup deadline. |
|
||||
|
||||
An Evidence URI is discoverability, not authorization. It cannot authorize file
|
||||
access, extraction, mounting, execution, or networking. IDs disappear when the
|
||||
session ends unless the existing Evidence bundle or workspace persistence flow
|
||||
explicitly retains them.
|
||||
For an already connected client using the pinned SDK, request options are the
|
||||
**second** argument of `callTool`:
|
||||
|
||||
## Permission policy
|
||||
```js
|
||||
const overview = await client.callTool(
|
||||
{ name: "binary_overview", arguments: {} },
|
||||
{
|
||||
timeout: 240000,
|
||||
onprogress: ({ progress, total, message }) => {
|
||||
console.error({ progress, total, message });
|
||||
},
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
All local side effects use one scope vocabulary: capability, canonical roots,
|
||||
executables, environment variable names, network mode, mount permission, exact
|
||||
operation identity, and grant lifetime. Environment values and captured content
|
||||
are never part of a grant or denial.
|
||||
The SDK's `onprogress` option supplies a progress token and handles matching
|
||||
notifications. A raw MCP client can instead send `_meta.progressToken` in the
|
||||
request and handle `notifications/progress`. Progress reports do not establish
|
||||
engine readiness or a percentage of Ghidra auto-analysis. A longer server
|
||||
startup allowance does not extend the client's deadline; progress also does not
|
||||
extend it unless the client explicitly implements that policy.
|
||||
|
||||
Existing environment settings map to administrator ceilings. They remain the
|
||||
maximum authority:
|
||||
The 240-second setting is a measured example, not a universal timeout: a
|
||||
controlled Linux x64/WSL2 fixture took about 64 seconds for its first overview
|
||||
with Ghidra 12.1.4 on a mounted NTFS installation, native JDK 21, two CPUs, and a
|
||||
512 MiB Java heap. Target size, storage, and analysis work can change that time.
|
||||
Configure the deadline in the caller's existing request settings; it is not a
|
||||
tool argument, setup grant, or server-side automatic extension. CLI analysis has
|
||||
no MCP client request deadline, but keeps the provider startup deadline and
|
||||
SIGINT cancellation.
|
||||
|
||||
- process roots, executables, environment names, and external networking;
|
||||
- Evidence read/write roots;
|
||||
- investigation input and workspace roots;
|
||||
- snapshot read/write roots;
|
||||
- reference-source roots;
|
||||
- native mount enablement.
|
||||
If the client timed out or cancelled during startup, the pending query may have
|
||||
been interrupted. Reissuing `open_binary` for the same active target can reuse
|
||||
the current client, so it is not a fresh-start recovery. On the same connection:
|
||||
|
||||
Artifact extraction retains its existing explicit per-call approval and maps to
|
||||
an administrator root ceiling of `/` for compatibility.
|
||||
1. Keep any useful inline Evidence, or export a bundle before closing if retained
|
||||
records are needed. `binary_session` can show the selected binding and its
|
||||
recorded state; target selection alone does not prove engine readiness.
|
||||
2. Call `close_binary` and check its result. It drains owned work, closes the
|
||||
provider, and clears retained session records. A `cleanup_incomplete` result
|
||||
must be addressed according to its reported owned resources before retrying.
|
||||
3. Call `open_binary` with the same caller-selected path and
|
||||
`provider_id: "ghidra"`, then retry the first query with an appropriate client
|
||||
deadline. The connection and selected provider do not need to change.
|
||||
|
||||
`rea policy status`, `list`, `explain`, and `revoke` inspect the same evaluator
|
||||
used by MCP. Optional project grants require both
|
||||
`REA_PERMISSION_PROJECT_ROOT` and `REA_PERMISSION_PROJECT_STORE`. The store is
|
||||
atomically written with mode `0600`, bound to the canonical project root, and is
|
||||
never enabled by default. Send `SIGHUP` to the REA MCP process after a trusted
|
||||
project-store change; project grants reload without restarting. Environment
|
||||
settings belong to the process environment, so changing an administrator
|
||||
ceiling in an MCP registration requires restarting that registered server or
|
||||
its owning client.
|
||||
Revocation affects future operations; an already-running operation retains the
|
||||
decision made at its preflight boundary.
|
||||
Once and session grants are connection-local overlays. They cannot authorize a
|
||||
second MCP connection, be consumed by another connection, or be cleared when a
|
||||
different connection closes. Live connections evaluate those overlays against
|
||||
the current process-wide ceilings and persisted grants, including successful
|
||||
SIGHUP reloads.
|
||||
`rea policy revoke <grant-id>` displays the exact grant and requires interactive
|
||||
confirmation; automation must pass `--yes` (or `-y`) explicitly.
|
||||
This close/reopen flow was exercised on one real Linux stdio connection after a
|
||||
controlled startup timeout, followed by a successful overview and function
|
||||
analysis. It is not a Windows/macOS coverage claim. REA cleans only resources it
|
||||
owns and never switches to another provider automatically. A provider timeout,
|
||||
installation failure, or host permission denial needs its own reported recovery;
|
||||
increasing a client deadline alone does not fix those failures.
|
||||
|
||||
Denials use the shared `permission_required` schema with requested scope, missing
|
||||
scope, administrator ceiling, elicitation support, and exact restart status.
|
||||
Client-provided roots are context only and never grants.
|
||||
## Tool results
|
||||
|
||||
Elicitation can add a once or session grant only inside an existing
|
||||
administrator ceiling. A request outside that ceiling reports
|
||||
`elicitation_supported: false` and `restart_required: true`; interactive consent
|
||||
cannot silently widen administrator policy.
|
||||
Evidence-producing tools return `{ result, evidence_id, evidence }` in both
|
||||
text and structured content. `evidence` is the complete canonical Evidence
|
||||
record, including `normalized_result`, which equals `result`. The same record
|
||||
is retained in the session bundle. Read `result` directly, or pass `evidence`
|
||||
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
|
||||
retained session history or an explicit bundle for transfer.
|
||||
|
||||
`analyze_javascript_application` uses the `investigation_input` capability. Its
|
||||
absolute `input_path` must be inside `REA_INVESTIGATION_INPUT_ROOTS_JSON` and the
|
||||
request must set `approved: true` before any artifact read. Reading source-map
|
||||
contents additionally requires `source_map_read_approved: true`; ordinary input
|
||||
approval does not imply that separate authority.
|
||||
## Retained application Evidence inputs
|
||||
|
||||
`trace_application_feature`, `trace_javascript_semantics`,
|
||||
`compare_application_versions`, `compare_source_to_bundle`, and
|
||||
`compare_javascript_export_shapes` accept complete inline application Evidence
|
||||
or an exact reference to a record already retained by the current connection:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "trace_application_feature",
|
||||
"arguments": {
|
||||
"application": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<64 lowercase hex characters>"
|
||||
},
|
||||
"seed": { "kind": "module", "value": "search.js", "match": "exact" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use the `evidence_id` returned by `analyze_javascript_application` (or another
|
||||
compatible application-graph producer). Comparisons accept this form in `left`
|
||||
and `right`; each side can independently be inline or retained. Native
|
||||
observation arrays continue to take complete inline Evidence. Results and their
|
||||
Evidence remain complete inline, and both input forms pass the same semantic,
|
||||
identity, authority, and provenance checks without running the producer again.
|
||||
|
||||
References belong to the current connection's Evidence ledger. Opening another
|
||||
target preserves retained records; `close_binary` clears them, even when no
|
||||
binary is active. A fresh connection has its own ledger. A missing reference
|
||||
reports its exact ID and `details.reason: "missing"`; the server cannot infer
|
||||
whether it was never recorded, cleared, or retained by another connection.
|
||||
Supply complete inline Evidence, repeat its producer, or import an exported
|
||||
Evidence bundle before referencing that imported record. Export a bundle before
|
||||
closing if the investigation needs it later.
|
||||
|
||||
CLI application workflows continue to read portable inline Evidence from files
|
||||
and run the same analysis workflows; a standalone CLI invocation cannot resolve
|
||||
another MCP connection's retained records. No additional lookup call, provider
|
||||
selection, or approval step is required for a same-session follow-up.
|
||||
|
||||
## Aggregate native context
|
||||
|
||||
`get_navigation_context` composes the selected document, current address, and
|
||||
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
|
||||
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.
|
||||
|
||||
## Request scope and local effects
|
||||
|
||||
Each tool request names the target and lifecycle it will use. Browser calls
|
||||
carry a loopback CDP endpoint and target ID, with optional origin filters;
|
||||
process and Electron scenarios carry the executable, arguments, actions, and
|
||||
cleanup behavior; artifact tools carry the input path and requested operation.
|
||||
REA runs the declared request directly and does not infer a broader target or
|
||||
action from it.
|
||||
|
||||
REA does not require permission grants or per-call approval flags. Setup still
|
||||
prints its plan and requires confirmation before changing configuration or
|
||||
installing Hopper. MCP clients control their own confirmation UI.
|
||||
|
||||
Tool annotations describe effects and are hints, not authorization controls
|
||||
([MCP ToolAnnotations](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations)).
|
||||
`readOnlyHint` includes session state: an analysis call that records additive
|
||||
Evidence is marked non-read-only even when it leaves the target unchanged.
|
||||
`destructiveHint` describes possible data loss, not ordinary Evidence recording.
|
||||
Effect metadata covers possible behavior across supported inputs: DMG inventory
|
||||
can launch `hdiutil` and create an owned temporary mount directory; extraction
|
||||
creates a fresh output directory on every call. Comparing supplied web captures
|
||||
or PNG artifacts uses local data without contacting the browser.
|
||||
|
||||
Host requirements remain in force. macOS may deny Accessibility,
|
||||
Screen Recording, or native mounting; provider tools require their selected
|
||||
analysis runtime. These failures are reported at the operation that needs
|
||||
them. A configured provider or an endpoint alone does not establish that a
|
||||
target is supported.
|
||||
|
||||
Evidence bundles, snapshots, and extraction use the paths and output behavior
|
||||
declared by their tools. Artifact extraction materializes the selected regular
|
||||
files into a fresh REA-chosen temporary directory and reports its path.
|
||||
|
||||
`analyze_javascript_application` reads the supplied local directory or ASAR path
|
||||
and returns its result and Evidence inline.
|
||||
|
||||
## Integrity record-and-continue
|
||||
|
||||
Artifact integrity remains fail-closed by default. Record-and-continue requires
|
||||
all three conditions:
|
||||
Artifact integrity fails closed by default. A request can explicitly select
|
||||
`integrity_policy=record-and-continue` when the investigation needs verified
|
||||
siblings to continue after a mismatch.
|
||||
|
||||
1. operator policy `REA_ARTIFACT_INTEGRITY_CONTINUE_ENABLED=true`;
|
||||
2. `integrity_policy=record-and-continue`;
|
||||
3. explicit per-call `integrity_continue_approved=true`.
|
||||
|
||||
Contradictory bytes are quarantined from nested expansion, recorded with declared
|
||||
and observed hashes, trust, provenance, path, and unpacked state, and bounded by
|
||||
`max_integrity_mismatches`. Verified siblings continue. Comparisons classify the
|
||||
result as a contradiction and reconstruction cannot treat it as unchanged.
|
||||
`investigate-versions` accepts the same policy through
|
||||
`--integrity-policy record-and-continue --integrity-continue-approved` and
|
||||
`--max-integrity-mismatches`. Its Evidence pages and CAS-linked workspace retain
|
||||
contradictions, so a completed run can resume without rescanning or weakening
|
||||
trust labels.
|
||||
Contradictory bytes are quarantined from nested expansion and recorded with
|
||||
declared and observed hashes, trust, provenance, path, and unpacked state.
|
||||
Verified siblings continue. Comparisons classify the result as a contradiction
|
||||
and reconstruction cannot treat it as unchanged.
|
||||
|
||||
+49
-42
@@ -6,19 +6,35 @@ invoke tools by themselves.
|
||||
|
||||
## Prompt inventory
|
||||
|
||||
| Prompt | Purpose |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `investigate_feature` | Trace a feature from discovery into bounded function evidence. |
|
||||
| `compare_application_versions` | Compare two shipped artifacts, with optional static and runtime follow-up. |
|
||||
| `verify_reconstruction` | Evaluate a finite reconstruction specification against retained comparison Evidence. |
|
||||
| `trace_crash` | Correlate a crash symptom with static paths and optional Process Capture v4 evidence. |
|
||||
| `audit_residual_unknowns` | Audit current residual-unknown heads and evidence-qualified resolution. |
|
||||
| `prepare_bounded_process_capture` | Design an approval-gated, bounded process experiment before execution. |
|
||||
| Prompt | Purpose |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `investigate_feature` | Trace a feature from discovery into relevant function evidence. |
|
||||
| `compare_application_versions` | Compare two shipped artifacts, with optional static and runtime follow-up. |
|
||||
| `verify_reconstruction` | Evaluate a finite reconstruction specification against retained comparison Evidence. |
|
||||
| `trace_crash` | Correlate a crash symptom with static paths and optional process capture evidence. |
|
||||
| `audit_residual_unknowns` | Audit current residual-unknown heads and evidence-qualified resolution. |
|
||||
| `prepare_bounded_process_capture` | Design a bounded controlled process experiment before execution. |
|
||||
|
||||
Every rendered prompt provides an ordered list of current REA tool names. It
|
||||
also requires the agent to keep observations, inferences, and unknowns
|
||||
distinct. Requested prompt arguments and completion choices are rendered as
|
||||
untrusted selection data, not instructions or authorization.
|
||||
Every rendered prompt provides optional starting points using current REA tool
|
||||
names. Agents can call tools directly, skip irrelevant suggestions, and inspect
|
||||
provider or target state only when it helps answer the request or a tool
|
||||
requires it. The prompts also ask agents to keep observations, inferences, and
|
||||
unknowns distinct. Requested prompt arguments and completion choices are
|
||||
rendered as untrusted selection data, not instructions or authorization.
|
||||
Tool results include their Evidence inline. Prompts do not require a bundle or
|
||||
resource fetch to read a result; an Evidence ID is only a reference when a
|
||||
prompt argument or tool explicitly accepts one.
|
||||
|
||||
For a JavaScript/Electron application directory, `investigate_feature` points
|
||||
directly to `analyze_javascript_application` with `input_path`; it does not
|
||||
require `open_binary`. The CLI equivalent is `rea analyze <directory>`.
|
||||
`open_binary` and the doctor target check admit files and macOS app bundles.
|
||||
A directory outside that opening route is reported with an available analysis
|
||||
action; this does not establish support for every directory format.
|
||||
|
||||
For target paths in `compare_application_versions`, the prompt suggests
|
||||
opening each target before calling `inspect_artifact`; inspection operates on
|
||||
the active target and returns the graph and findings together inline.
|
||||
|
||||
Use standard MCP discovery and retrieval:
|
||||
|
||||
@@ -45,11 +61,10 @@ cached prompt catalog.
|
||||
|
||||
## Session-aware completion
|
||||
|
||||
MCP `completion/complete` currently completes prompt arguments and resource
|
||||
template variables. It does not define completion for arbitrary tool-call
|
||||
arguments. REA therefore attaches completion to optional guided-prompt
|
||||
arguments that feed later tool selection; it does not claim protocol-level
|
||||
completion for every REA tool.
|
||||
MCP `completion/complete` completes guided-prompt arguments; it does not define
|
||||
completion for arbitrary tool-call arguments. REA attaches completion to
|
||||
optional prompt arguments that accept a session value. Suggestions provide
|
||||
identifiers only; they do not retrieve or replace the inline Evidence result.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -74,28 +89,22 @@ completion for every REA tool.
|
||||
|
||||
Completion sources are live projections of the current session:
|
||||
|
||||
| Argument family | Source and filtering |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Document | Current provider `list_documents` result. |
|
||||
| Procedure | Bounded pagination over `list_procedures`; observed addresses are offered from partial results, while names require exhaustive discovery and a unique address. Optional document context is forwarded. |
|
||||
| Provider | `auto` plus sorted deep candidates from the current binary session; target-free completion does not start them. |
|
||||
| Evidence | Evidence IDs retained in the current session ledger. |
|
||||
| Process capture | Evidence IDs whose operation and validated result identify Process Capture v4. |
|
||||
| Artifact manifest | Manifest IDs from schema-valid retained artifact inventories. |
|
||||
| Artifact occurrence | Occurrence IDs present in schema-valid retained artifact inventory pages. |
|
||||
| Residual unknown | Current non-resolved unknown heads only. |
|
||||
| Argument family | Source and filtering |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Document | Current provider `list_documents` result. |
|
||||
| Procedure | The complete `list_procedures` inventory; all procedure addresses and uniquely named procedures are offered. Optional document context is forwarded. |
|
||||
| Provider | `auto` plus sorted deep candidates from the current binary session; target-free completion does not start them. |
|
||||
| Evidence | Evidence IDs retained in the current session ledger, offered only for prompt arguments that accept Evidence references. |
|
||||
| Process capture | IDs for retained Evidence whose operation and validated result identify process capture. |
|
||||
| Artifact manifest | Manifest IDs from schema-valid retained artifact inventories. |
|
||||
| Artifact occurrence | Occurrence IDs present in schema-valid retained artifact inventory pages. |
|
||||
| Residual unknown | Current non-resolved unknown heads only. |
|
||||
|
||||
Suggestions are Unicode-normalized, case-insensitive prefix matches. The server
|
||||
deduplicates them, rejects values longer than 4,096 characters, and sorts by
|
||||
code point for deterministic results. It scans at most 10,000 candidates;
|
||||
procedure discovery is further bounded to 5,000 unique addresses in pages of 500. If that bound is reached before exhaustive procedure discovery, only
|
||||
observed addresses are suggested because global name uniqueness is unknown.
|
||||
|
||||
The MCP result carries at most 100 `values`. `total` is the number of matching
|
||||
candidates found within REA's scan bound, and `hasMore` reports whether more
|
||||
than 100 were found. The MCP completion response has no offset or cursor, so a
|
||||
client narrows a large result by sending a longer prefix rather than requesting
|
||||
another page.
|
||||
deduplicates them and sorts by code point for deterministic results. Procedure
|
||||
completion derives candidates from the complete inventory, so a name is offered
|
||||
only when it identifies one address in that inventory. Completion returns all
|
||||
matching values; it does not expose paging controls.
|
||||
|
||||
## Lifecycle and safety
|
||||
|
||||
@@ -106,8 +115,6 @@ session are no longer suggested. Provider errors, unsupported operations, and
|
||||
malformed provider output produce an empty completion list rather than an
|
||||
unverified identifier.
|
||||
|
||||
Suggestions never grant permission. In particular, selecting an artifact
|
||||
occurrence does not authorize extraction, selecting an unknown does not
|
||||
authorize mutation, and selecting a prior capture does not authorize process
|
||||
execution. The corresponding tools retain their existing approval, policy,
|
||||
effect, and validation boundaries.
|
||||
Suggestions are starting points, not tool calls. A tool acts on the target,
|
||||
operation, and lifecycle fields in its own request; selecting a suggestion does
|
||||
not execute it or broaden those fields.
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
# Native UI and dispatch investigation
|
||||
|
||||
REA joins static resources, native metadata and code facts while keeping runtime
|
||||
observations separate. Every result reports its evidence, target identity,
|
||||
coverage and unknowns. The CLI and MCP use the same application workflows.
|
||||
|
||||
## Static inspection
|
||||
|
||||
`inspect_macho` retains otool segment `file_offset` values relative to the
|
||||
selected Mach-O slice. Its evidence file-offset ranges address the original
|
||||
input file and include the observed lipo slice offset for universal binaries.
|
||||
If that offset is unavailable, segment evidence locations are omitted with an
|
||||
explicit limitation; architecture inventory locations remain available.
|
||||
|
||||
- `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.
|
||||
- `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
|
||||
`path`. Results retain original `$objects` indices, named `$top` roots,
|
||||
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.
|
||||
UID 0 cannot distinguish conditional nil from ordinary nil. Limits are
|
||||
64 MiB of input, 200,000 objects/references and 128 nesting levels.
|
||||
- `decode_interface_builder` / `rea decode-interface-builder <app>` reads
|
||||
compiled storyboard and nib resources into objects, outlets, actions,
|
||||
connections and controller/class names. `verify:interface-builder` compiles
|
||||
a source-owned AppKit XIB with `ibtool`. Storyboards require an installed
|
||||
iOS platform; unsupported archive forms remain explicit.
|
||||
- `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
|
||||
artifact evidence. Vtable indexes are metadata word offsets; witness indexes
|
||||
start after the conformance header. Names unavailable in metadata remain null.
|
||||
Pointers are decoded through the image's fixups:
|
||||
- `LC_DYLD_CHAINED_FIXUPS` rebases and binds, including the authenticated
|
||||
arm64e formats;
|
||||
- legacy `LC_DYLD_INFO` bind opcodes.
|
||||
|
||||
So an external superclass resolves from its `_OBJC_CLASS_$_` bind (for
|
||||
example `NSObject`). The `pointer_fixups` coverage facet names the mechanism.
|
||||
Class properties, with parsed attributes, and `__objc_catlist` categories are
|
||||
also decoded. Categories record the extended class, local or external, plus
|
||||
their methods, protocols and properties; category methods appear as
|
||||
implementations with a `category`. Swift field-offset globals that are only
|
||||
initialized at runtime stay unresolved. Generic, resilient, async and
|
||||
coroutine tables, and inherited overrides, have explicit unsupported or
|
||||
partial coverage. Other providers retain the
|
||||
existing symbol-based inventory with its narrower coverage.
|
||||
|
||||
## Native instruction, call and type primitives
|
||||
|
||||
Ghidra supplies three exact-object operations:
|
||||
|
||||
```bash
|
||||
rea inspect-native-instruction <binary> <address> --provider ghidra
|
||||
rea resolve-native-call-targets <binary> <call-site> --provider ghidra
|
||||
rea inspect-native-data-type <binary> --type /MyStruct --provider ghidra
|
||||
rea inspect-native-data-type <binary> --address <typed-data-address> --provider ghidra
|
||||
```
|
||||
|
||||
Instruction facts include bytes, length, decoder text, ordered register/scalar/
|
||||
address tokens, typed references and flow destinations. Mid-instruction, data,
|
||||
outside-memory and undecodable addresses have separate outcomes. Effective
|
||||
memory base/index/displacement roles and per-instruction context mode remain
|
||||
unavailable; `mode` is the program language variant.
|
||||
|
||||
Call resolution reports direct, resolved indirect, ambiguous, unresolved and
|
||||
non-call outcomes from static call references. It does not establish runtime
|
||||
execution or classify Objective-C/Swift/vtable/closure mechanisms from names.
|
||||
|
||||
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
|
||||
inspection, including recursive layouts. Source/debug authority and flexible
|
||||
array semantics are not inferred. The real lane uses a native DWARF 4 object
|
||||
with a struct containing a union, enum and recursive pointer; a stripped linked
|
||||
binary may have no corresponding recovered layout.
|
||||
|
||||
## Bounded static traces
|
||||
|
||||
`trace_native_ui_action` / `rea trace-native-ui-action <target> <seed>` accepts
|
||||
one authored selector/object ID, native symbol or exact function address. It
|
||||
joins UI wiring to uniquely matched class/selector implementations and follows
|
||||
bounded typed call references. Direct references, resolved indirect references,
|
||||
ambiguous candidates, inferred untyped provider callees and targetless call
|
||||
sites remain distinct. A static route does not establish runtime reachability.
|
||||
Swift/closure dispatch requires resolved static references; unavailable ABI
|
||||
forms remain unknown.
|
||||
|
||||
`trace_native_values` / `rea trace-native-values <binary> <procedure> --provider
|
||||
ghidra` composes high-p-code def-use graphs across statically resolved calls.
|
||||
Logical CALL arguments bind to recovered parameter ordinals; recovered RETURN
|
||||
values bind to CALL outputs. These edges are decompiler-derived. Constants,
|
||||
operators, LOAD/STORE and branch operands are included inline. Alias effects,
|
||||
persistent state, RNG roles, missing/variadic bindings and ambiguous destinations
|
||||
remain unknown. Budgets control depth, decompilations, call-site resolutions,
|
||||
nodes and edges. Node payloads are bounded to 8 MiB and serialized results to
|
||||
32 MiB. Pagination returns edges whose source is on the node page and preserves
|
||||
stable IDs for endpoints outside that page.
|
||||
|
||||
The raw Java `is_dead` flag can remain set after Ghidra decodes a live block;
|
||||
`block_membership` reports actual syntax-tree membership separately.
|
||||
|
||||
The provider dossier itself retains up to 3,000 p-code operations, 64 inputs per
|
||||
operation and 12,000 def-use edges, with explicit omitted counts. A `STORE`
|
||||
may describe stack memory and does not itself prove persistent state.
|
||||
|
||||
Jump-table evidence keeps numeric `mappings`, explicit `default_targets`, and
|
||||
backing `data_sources` separate. A null case value denotes an unresolved
|
||||
case, never a known default. Ghidra pairs typed case/default tokens with the
|
||||
recovered block entry and its unique indirect dispatch predecessor; it does
|
||||
not zip unequal label and destination arrays. Shared case bodies retain each
|
||||
label. Negative numeric tokens are checked against their encoded magnitude;
|
||||
nonliteral, ambiguous, or nonexact JSON integer labels remain unknown.
|
||||
Unknown-label diagnostics retain the token text, encoded unsigned magnitude,
|
||||
target and dispatch so callers can inspect the original observations.
|
||||
Decompiler load-table metadata supplies observed entry sizes and counts
|
||||
without assigning every backing table to each mapping. These observations
|
||||
describe the decompiler's recovered switch, not guaranteed original source.
|
||||
Legacy records without `default_targets` normalize to an empty array and
|
||||
retain their existing unresolved mappings.
|
||||
|
||||
AArch64 jump-table recovery additionally verifies byte and halfword relative
|
||||
forms from unsigned bounds, register definitions, table loads, branch bases,
|
||||
scaled ADD/BR instructions and the recovered target set. It reads exactly the
|
||||
proven count and preserves unknowns for other forms. Real ELF and host ARM64
|
||||
Mach-O fixtures check each case against source-owned return values.
|
||||
|
||||
## Native desktop observation
|
||||
|
||||
`observe_native_ui` captures one explicitly selected existing PID/window ID.
|
||||
Screenshots use a selected-window ScreenCaptureKit
|
||||
filter on macOS 14+; accessibility reads stay within that window. Missing Screen
|
||||
Recording/Accessibility permissions produce actionable errors without broad
|
||||
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.
|
||||
|
||||
`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
|
||||
ordered before/after captures and gaps; an action may have occurred before a
|
||||
post-action capture fails. Application state is left as-is; REA does not attempt
|
||||
to restore it.
|
||||
|
||||
Scenarios accept caller-selected action lists and accessibility node counts.
|
||||
Individual waits cannot exceed the operation's 180-second deadline, and the
|
||||
complete scenario result has a 64 MiB output budget. Screenshots are scaled to
|
||||
at most 2,048 pixels and captured only for the selected window. REA compiles one
|
||||
owned helper per scenario, removes its temporary compiler cache and stops its
|
||||
helper on cancellation. It does not launch or own the selected application. UI
|
||||
actions may change application data or trigger network activity.
|
||||
|
||||
## Provider and verification boundaries
|
||||
|
||||
Install a Ghidra 12.1.x release and the 64-bit full JDK it declares, then configure REA to
|
||||
use them. Ghidra analysis supports Linux x64 and macOS x64/arm64; macOS requires
|
||||
the matching native decompiler. Experimental Windows x64 P0 admits native
|
||||
x86-64 PE applications on local NTFS using bundled Job Object ownership,
|
||||
protected runtime DACLs, and handle-based path admission. See [Windows Ghidra P0](windows-ghidra-p0.md) and
|
||||
[issue #527](https://github.com/morluto/rea/issues/527).
|
||||
On Linux and macOS, `annotate_native_function` atomically edits a function name
|
||||
and entry comments in the ephemeral database, returning refreshed analysis
|
||||
without changing executable bytes. Windows P0 remains read-only. Ghidra has no
|
||||
GUI authority, and REA never falls back automatically to Hopper.
|
||||
|
||||
- `npm run verify:ghidra`: host-native debug/stripped targets, native type layout,
|
||||
instruction/call facts, value dependencies and process/project cleanup.
|
||||
- `npm run verify:ghidra:aarch64-jump-table`: optimized ELF and byte/halfword
|
||||
relative tables, plus ARM64 Mach-O on an ARM64 macOS host.
|
||||
- `npm run verify:apple-dispatch`: source-built Objective-C protocols, classes,
|
||||
properties and categories, and Swift conformances and vtables. Each is linked
|
||||
with legacy `LC_DYLD_INFO` and with chained fixups (and as arm64e on Apple
|
||||
silicon), then repeated after stripping local symbols.
|
||||
- `npm run verify:native-ui`: one source-owned fixture window, successful
|
||||
selected-window capture and actions, changed-target rejection, and cleanup.
|
||||
Missing OS permissions fail this lane.
|
||||
- `npm run verify:native-ui:permissions`: permits a permission-denial result and
|
||||
reports `positive_e2e: false` when capture is denied. That result verifies the
|
||||
OS permission boundary, not successful UI capture or actions.
|
||||
|
||||
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.
|
||||
+87
-228
@@ -1,79 +1,57 @@
|
||||
# Process Capture v4
|
||||
# Process capture
|
||||
|
||||
Process Capture records one approved command as deterministic Evidence. It is
|
||||
intended for authority-versus-reconstruction checks where terminal behavior,
|
||||
child lifetime, filesystem state, or dependency timing matters.
|
||||
Process Capture runs one caller-selected command and records bounded evidence
|
||||
about its terminal behavior, scheduled interactions, process lifetime, and
|
||||
selected filesystem state. Use it to compare two direct runs of the same
|
||||
scenario, such as an authority build and a reconstruction. It does not replay a
|
||||
previous execution or emulate dependencies.
|
||||
|
||||
The harness records both raw PTY chunks and terminal states rendered by xterm.
|
||||
It also records scripted input, resize and signal delivery, sampled process
|
||||
metadata, named filesystem checkpoints, command-shim invocations, loopback
|
||||
HTTP/WebSocket exchanges, and process exit ownership. Missing or bounded
|
||||
observations remain explicit; a truncated capture is never treated as
|
||||
equivalent to another capture.
|
||||
The command runs with the current user's permissions and inherits the host
|
||||
environment before applying the scenario's explicit overrides. This is not a
|
||||
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.
|
||||
|
||||
Every v4 capture includes a run manifest with canonical SHA-256 commitments for
|
||||
the secret-safe full scenario projection, comparison contract, executable,
|
||||
normalization rules, command-shim plan, and replay plan. The manifest also
|
||||
records the REA/provider versions, platform, architecture, PTY backend, and UTC
|
||||
start/completion timestamps. Capture creation, Evidence import, and comparison
|
||||
recompute the self-contained commitments and reject invalid lifecycle data.
|
||||
## Host support
|
||||
|
||||
## Enable the capability
|
||||
Capturing a new scenario currently requires Linux or macOS and a working
|
||||
native PTY backend. Native Windows capture is unavailable because the PTY
|
||||
adapter does not yet verify descendant cleanup. The native Job Object controls
|
||||
used by other REA providers do not establish PTY capture support. Reinstalling
|
||||
the Windows PTY binary does not enable this workflow.
|
||||
|
||||
Process execution is disabled until the operator supplies policy roots and
|
||||
approves host networking:
|
||||
For Linux commands, use Linux REA inside WSL. Adapt the scenario to that host;
|
||||
this does not establish capture of a native Windows process tree. Comparing
|
||||
existing capture Evidence through the CLI or MCP remains available on Windows
|
||||
and does not launch a PTY or the captured target.
|
||||
|
||||
```bash
|
||||
export REA_PROCESS_CAPTURE_ENABLED=true
|
||||
export REA_PROCESS_EXECUTABLE_ROOTS_JSON='["/usr/bin","/absolute/project/bin"]'
|
||||
export REA_PROCESS_WORKING_ROOTS_JSON='["/absolute/project"]'
|
||||
export REA_PROCESS_ALLOWED_ENV_JSON='["PATH"]'
|
||||
export REA_PROCESS_ALLOW_EXTERNAL_NETWORK=true
|
||||
On supported capture hosts, a missing or incompatible native PTY binary has a
|
||||
different recovery: reinstall REA for the active platform, architecture, and
|
||||
Node.js version with optional dependencies enabled. Capability diagnostics
|
||||
distinguish this from the Windows capture-adapter limitation.
|
||||
|
||||
## Capture a command
|
||||
|
||||
Write a JSON scenario and pass its path to the CLI:
|
||||
|
||||
```sh
|
||||
rea capture-process ./scenario.json --json > capture.json
|
||||
```
|
||||
|
||||
By default, enabling process capture also installs an administrator-lifetime
|
||||
grant for the configured boundary. Set
|
||||
`REA_PROCESS_CAPTURE_AUTO_GRANT=false` to install the same boundary only as an
|
||||
administrator ceiling. Captures then fail with a structured permission-required
|
||||
result until a narrower grant is established; this is the prerequisite mode for
|
||||
connection-scoped MCP elicitation. It does not itself authorize a capture.
|
||||
Each MCP connection owns its elicited once and session grants. A grant accepted
|
||||
on one connection is not visible to another connection, and disconnect cleanup
|
||||
removes only the grants owned by the connection that closed. Reloaded
|
||||
administrator ceilings and persisted grants still apply immediately to every
|
||||
live connection.
|
||||
`--json` is required when saving input for JSON consumers; the default terminal
|
||||
format is TOON. The file contains the complete capture Evidence record.
|
||||
Choose an output file distinct from the scenario input: shell redirection opens
|
||||
and truncates the output before REA reads the scenario.
|
||||
|
||||
REA offers the once-or-session grant form only after the connection negotiates
|
||||
an MCP revision with multi-round tool results and advertises form elicitation.
|
||||
Older connections keep returning the structured
|
||||
permission-required result and never wait for an unsupported elicitation
|
||||
response. The MCP SDK runtime currently pinned by REA negotiates the older
|
||||
protocol path, so the interactive flow remains fail-closed until its runtime
|
||||
protocol support is available.
|
||||
|
||||
Every scenario must also contain `"approved": true`. Executables, working
|
||||
directories, filesystem roots, and requested environment variables are checked
|
||||
against operator policy before launch. Process Capture is an observation tool,
|
||||
not a security sandbox: the target runs with the current user's permissions.
|
||||
|
||||
## Capture a scenario
|
||||
|
||||
`rea capture-process` reads a JSON scenario and writes Process Capture v4
|
||||
Evidence to stdout:
|
||||
|
||||
```bash
|
||||
rea capture-process ./scenario.json > capture.json
|
||||
```
|
||||
|
||||
A representative scenario is:
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"approved": true,
|
||||
"executable": "/absolute/path/to/node",
|
||||
"executable": "node",
|
||||
"arguments": ["./signup.mjs"],
|
||||
"working_directory": "/absolute/project",
|
||||
"filesystem_roots": ["/absolute/project/state"],
|
||||
"working_directory": ".",
|
||||
"environment": { "APP_MODE": "test" },
|
||||
"filesystem_observation_paths": ["./state"],
|
||||
"terminal": { "columns": 80, "rows": 24, "scrollback": 1000 },
|
||||
"events": [
|
||||
{
|
||||
@@ -82,182 +60,63 @@ A representative scenario is:
|
||||
"data": "user@example.test\r",
|
||||
"sensitive": true
|
||||
},
|
||||
{ "type": "resize", "at_ms": 500, "columns": 100, "rows": 30 }
|
||||
{ "type": "resize", "at_ms": 500, "columns": 100, "rows": 30 },
|
||||
{ "type": "signal", "at_ms": 1000, "signal": "SIGINT" }
|
||||
],
|
||||
"checkpoints": [
|
||||
{
|
||||
"name": "signup_ready",
|
||||
"trigger": { "type": "terminal_literal", "value": "All set!" }
|
||||
},
|
||||
{ "name": "root_exited", "trigger": { "type": "root_exit" } }
|
||||
],
|
||||
"command_shims": [
|
||||
{
|
||||
"name": "codex",
|
||||
"routes": [
|
||||
{
|
||||
"arguments": ["--version"],
|
||||
"outputs": [
|
||||
{ "at_ms": 0, "stream": "stdout", "data": "codex 1.2.3\n" }
|
||||
],
|
||||
"termination": { "type": "exit", "code": 0 },
|
||||
"max_calls": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"timeout_ms": 30000,
|
||||
"idle_timeout_ms": 30000,
|
||||
"settle_ms": 100
|
||||
}
|
||||
```
|
||||
|
||||
Input events marked `sensitive` are dispatched to the PTY but stored only as a
|
||||
byte-count placeholder. Environment values listed in `secret_aliases` are also
|
||||
redacted from recorded text. The command-shim directory is placed first in the
|
||||
captured process `PATH`; any remaining `PATH` must be explicitly supplied or
|
||||
inherited under operator policy.
|
||||
`executable` is required. `arguments`, `working_directory`, `environment`,
|
||||
`filesystem_observation_paths`, terminal settings, timed `events`, timeouts,
|
||||
resource limits, and normalization settings have defaults; see
|
||||
`processScenarioSchema` for the exact contract. Event times are milliseconds
|
||||
from launch, must be ordered, and must fall within `timeout_ms`. Inputs marked
|
||||
`sensitive` are sent to the process but persisted as a byte-count placeholder.
|
||||
Environment overrides are recorded in the scenario commitment; inherited
|
||||
values are not copied into Evidence.
|
||||
|
||||
## Reactive process scenarios
|
||||
The capture contains raw PTY output chunks and rendered terminal states,
|
||||
interaction dispatch outcomes, exit reason, sampled process-tree observations,
|
||||
and settlement status. When filesystem paths are selected, REA records
|
||||
`files_before` and `files_after`, then classifies observed entries as created,
|
||||
deleted, modified, or unchanged. These are bounded snapshots, not a syscall
|
||||
trace; short-lived changes between snapshots may be missed. With no selected
|
||||
paths, filesystem effects remain unknown.
|
||||
|
||||
Set `reactive` to a Process Reactive Scenario v1 declaration when interaction
|
||||
must follow observed output instead of guessed delays. The graph declares an
|
||||
initial state, absolute scenario and state deadlines, explicitly prioritized
|
||||
transitions, bounded uses and visits, trigger predicates, ordered actions, and
|
||||
either a next state or `passed` outcome.
|
||||
Output, file count, file size, process sampling, filesystem depth, total
|
||||
runtime, idle time, and post-exit settlement are bounded by the scenario's
|
||||
limits. The result marks truncated observations and residual unknowns rather
|
||||
than treating missing data as proof of equivalence. Cancellation and timeout
|
||||
run the same owned-process cleanup path. Settlement reports whether the
|
||||
sampled process group quiesced or whether cleanup was needed or unverifiable;
|
||||
sampling cannot prove that every short-lived or detached descendant was seen.
|
||||
|
||||
The admitted runtime slice matches decoded UTF-8 terminal literals and exact
|
||||
`terminal_raw`, `interaction`, `process`, `filesystem`, `http`, `websocket`, or
|
||||
`shim` journal events. Process events are sampled observations, not an
|
||||
event-complete lifecycle. Filesystem events identify named checkpoints; shim
|
||||
events identify a command and route. Live predicates receive the same normalized
|
||||
and redacted payload later persisted in Evidence. Predicates may compose with
|
||||
bounded `all`, `any`, `sequence`, and `repeat` nodes and may select a frontier at
|
||||
scenario start, state entry, a prior checkpoint, or an exact event ID. Actions
|
||||
can write PTY input, resize it, signal the root process, or capture a named
|
||||
filesystem checkpoint. `terminal_rendered`, `lifecycle`, and `replay_transition`
|
||||
event triggers and non-root signal selectors fail during preflight before the
|
||||
target launches.
|
||||
## Compare two captures
|
||||
|
||||
Reactive results are recorded in `reactive_run`, including the terminal outcome,
|
||||
active state, matched event IDs, action evidence IDs, and deterministic
|
||||
transition journal. Terminal controls also commit the last admitted observation
|
||||
order, so later output cannot be moved ahead of a timeout, cancellation, or
|
||||
target loss during validation. If the process exits before the graph finishes,
|
||||
the outcome is `target_lost`; state/scenario deadlines remain distinct
|
||||
`predicate_timeout`/`scenario_deadline` outcomes. Timed `events` remain
|
||||
available for fixed schedules, but they are not silently translated into the
|
||||
reactive graph.
|
||||
Compare saved capture Evidence with:
|
||||
|
||||
## Checkpoints and deterministic shims
|
||||
|
||||
Checkpoint triggers may use an elapsed time, a terminal literal and occurrence
|
||||
count, root exit, or settlement. REA always includes `before` and
|
||||
`after_settlement` snapshots. Snapshot effects are relative to the preceding
|
||||
checkpoint, so transient files remain visible even when the final tree matches
|
||||
the initial tree.
|
||||
|
||||
Each command shim matches an exact argument array. Routes can emit timed stdout
|
||||
or stderr chunks and then exit or receive `SIGINT`, `SIGTERM`, or `SIGKILL`.
|
||||
Unmatched and exhausted calls are recorded. Shim observations are bounded by
|
||||
the scenario protocol-event limit; exceeding it marks the whole capture
|
||||
truncated while replay continues.
|
||||
|
||||
## Run a replay machine directly
|
||||
|
||||
Use `rea run-replay-machine ./run.json` to validate a finite replay machine
|
||||
against an ordered `events` array without launching a process or opening a
|
||||
socket. MCP clients use `run_replay_machine` with the same `{ machine, events }`
|
||||
input. The result retains one decision per offered event, a transition journal
|
||||
with capture alias metadata, and one redacted action-table entry per used
|
||||
transition. It never returns request bodies, request headers, or captured
|
||||
values. It also reports the initial and final states, whether the final state is
|
||||
terminal, configured limits, and committed usage.
|
||||
|
||||
The direct boundary accepts at most 10,000 events, 4 MiB of declared action
|
||||
JSON, 1,024 action text fields, and 1,024 possible sensitive-capture operations.
|
||||
These caps bound both returned content and secret-redaction work.
|
||||
|
||||
Refused events remain data rather than aborting the run. Outcomes distinguish
|
||||
unmatched events, invalid states, failed guards, exhausted transitions, invalid
|
||||
captures, unexpected reconnects, and exhausted limits. This direct runner
|
||||
evaluates the same domain runtime used by loopback Process Capture, but performs
|
||||
no network or target execution.
|
||||
|
||||
## Compare captures
|
||||
|
||||
Compare two saved Evidence records with:
|
||||
|
||||
```bash
|
||||
```sh
|
||||
rea compare-process-captures authority.json reconstruction.json
|
||||
```
|
||||
|
||||
Pass an optional third path, `rea compare-process-captures authority.json
|
||||
reconstruction.json trace.json`, when valid executions may differ in declared
|
||||
concurrent ordering. MCP callers pass the same object as `trace_spec` to
|
||||
`compare_process_captures`. A trace specification declares exact named events
|
||||
and chooses one language:
|
||||
The comparison checks terminal, interaction, exit, filesystem, and
|
||||
process observations under a shared comparison contract. It reports observed
|
||||
differences with their locations, while residual unknowns or truncated
|
||||
observations prevent a complete-equivalence claim. Matching scenario
|
||||
commitments do not reveal whether redacted sensitive inputs were equal.
|
||||
|
||||
- `partial_order` requires every event pair to be related by a
|
||||
`happens_before` edge (including transitive edges) or an explicit
|
||||
`unordered_groups` declaration. `not_before` expresses negative ordering
|
||||
constraints without inferring causality. Optional `prefix` and `suffix`
|
||||
arrays remain exact.
|
||||
- `finite_traces` lists the complete accepted variants. An `unordered` token
|
||||
accepts permutations of exactly its declared event multiset; it does not
|
||||
drop or broadly sort events.
|
||||
For scenarios where independent scheduling can change event order, the CLI
|
||||
also accepts an optional trace-specification JSON file as the third argument.
|
||||
That specification must state the exact events and ordering constraints to
|
||||
accept; it does not discard unmatched observations or make timestamps into
|
||||
causal evidence. Trace comparison returns `unknown` when a capture lacks the
|
||||
required complete event journal or contains relevant residual unknowns.
|
||||
|
||||
Each event uses one source family (`terminal_raw`, `terminal_rendered`,
|
||||
`interaction`, `lifecycle`, `process`, `filesystem`, `http`, `websocket`,
|
||||
`shim`, or `replay_transition`), an exact JSON payload, and a discriminated
|
||||
cardinality: `required`, `optional`, `exact`, or `range`. Specifications with
|
||||
duplicate or overlapping predicates, unknown references, cycles, implicit
|
||||
ordering gaps, ordered/unordered conflicts, or finite variants that violate
|
||||
cardinality are rejected before comparison.
|
||||
|
||||
Event payloads are exact by default. To compare schedules whose recorder
|
||||
metadata necessarily changes, an event may explicitly list top-level
|
||||
`ignore_fields` from the bounded set `sequence`, `at_ms`, `scheduled_at_ms`,
|
||||
`dispatched_at_ms`, and `elapsed_ms`; its `exact` object must omit those fields.
|
||||
All predicates for one source use the same ignore set, so relaxed metadata
|
||||
cannot create declaration-order matching ambiguity. Other payload fields are
|
||||
never dropped or normalized by trace comparison.
|
||||
|
||||
Process Capture records a monotonic `event_journal` at observation time. Trace
|
||||
comparison uses only this capture order for cross-source causality; timestamps
|
||||
are retained as event data but never promoted into happens-before evidence.
|
||||
Older captures without a complete journal, truncated captures, and relevant
|
||||
residual unknowns return a trace verdict of `unknown`. A passing result retains
|
||||
each side's raw journal locations and identifies either the matched finite
|
||||
variant or the complete satisfied constraint set. A failure returns the first
|
||||
declaration-ordered predicate, cardinality, edge, prefix, suffix, or language
|
||||
violation with its relevant event locations.
|
||||
If both captures have the same invalid declared trace, the nested verdict is
|
||||
`nonconforming` while the top-level capture comparison remains unchanged. Side
|
||||
statuses and the diagnostic distinguish conformance from pairwise difference.
|
||||
|
||||
The result classifies terminal, interaction, exit, filesystem, protocol,
|
||||
process, and shim behavior. `first_divergence` points to the earliest observed
|
||||
difference. Residual unknowns prevent a claim of complete equivalence, but they
|
||||
do not hide an observed difference in another dimension.
|
||||
|
||||
Captures must have equal comparison-contract commitments, but may identify
|
||||
different scenarios and executables. MCP callers may set `max_capture_age_ms`;
|
||||
the application clock then rejects stale captures before comparison.
|
||||
|
||||
## Limits and lifecycle behavior
|
||||
|
||||
Scenarios bound output bytes, frames, files, file bytes, process observations,
|
||||
protocol/shim events, connections, filesystem depth, total runtime, idle time,
|
||||
and settlement time. Process trees are sampled rather than syscall-traced, and
|
||||
short-lived descendants may be missed. Filesystem observations are snapshots,
|
||||
not filesystem event streams.
|
||||
|
||||
REA tags launched processes with a private run identifier. During cleanup it
|
||||
revalidates that identifier before signaling sampled process groups, including
|
||||
groups detached from the original parent. Temporary HOME, replay, shim,
|
||||
terminal, checkpoint, and sampling resources are released on success, failure,
|
||||
timeout, and cancellation.
|
||||
|
||||
After root exit, REA checks token-owned process groups every 50 ms. Two
|
||||
consecutive empty observations establish `quiesced`; the settlement deadline
|
||||
otherwise produces `alive_at_deadline`, while inspection or identity failures
|
||||
produce `unverifiable`. Sampling can still miss short-lived or early-detached
|
||||
descendants, which remains a residual unknown.
|
||||
Each capture carries commitments for the selected scenario, executable,
|
||||
comparison contract, and normalization rules. Comparison rejects captures
|
||||
whose comparison contracts differ. Captures are local Evidence files; keep
|
||||
their source artifacts and invocation context available when interpreting a
|
||||
difference.
|
||||
|
||||
+222
-216
@@ -1,31 +1,34 @@
|
||||
{
|
||||
"catalog_schema_version": 1,
|
||||
"package": {
|
||||
"name": "rea-agents",
|
||||
"version": "2.7.0",
|
||||
"version": "5.0.0",
|
||||
"sdk": {
|
||||
"server": "2.0.0-beta.4",
|
||||
"client": "2.0.0-beta.4",
|
||||
"core": "2.0.0-beta.4"
|
||||
"server": "2.3.1",
|
||||
"client": "2.3.1",
|
||||
"core": "2.3.1"
|
||||
},
|
||||
"skill_version": "23"
|
||||
"skill_version": "27"
|
||||
},
|
||||
"tools": {
|
||||
"total": 111,
|
||||
"total": 133,
|
||||
"families": [
|
||||
{
|
||||
"id": "direct",
|
||||
"surface": "official-proxy",
|
||||
"count": 36,
|
||||
"count": 41,
|
||||
"tools": [
|
||||
"address_name",
|
||||
"address_to_file_offset",
|
||||
"annotate_native_function",
|
||||
"comment",
|
||||
"current_address",
|
||||
"current_document",
|
||||
"current_procedure",
|
||||
"goto_address",
|
||||
"inline_comment",
|
||||
"inspect_native_data_type",
|
||||
"inspect_native_instruction",
|
||||
"inspect_native_load_image",
|
||||
"list_bookmarks",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
@@ -44,6 +47,7 @@
|
||||
"read_bytes",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"resolve_native_call_targets",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"set_address_name",
|
||||
@@ -59,60 +63,86 @@
|
||||
{
|
||||
"id": "enhanced",
|
||||
"surface": "enhanced",
|
||||
"count": 13,
|
||||
"count": 14,
|
||||
"tools": [
|
||||
"analyze_function",
|
||||
"analyze_swift_types",
|
||||
"batch_decompile",
|
||||
"binary_overview",
|
||||
"find_code_for_string",
|
||||
"find_xrefs_to_name",
|
||||
"get_call_graph",
|
||||
"get_objc_classes",
|
||||
"get_objc_protocols",
|
||||
"inspect_native_api",
|
||||
"swift_classes",
|
||||
"inspect_native_dispatch_metadata",
|
||||
"trace_call_path",
|
||||
"trace_feature"
|
||||
"trace_feature",
|
||||
"trace_native_ui_action",
|
||||
"trace_native_values"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "native",
|
||||
"surface": "native-provider",
|
||||
"count": 5,
|
||||
"count": 7,
|
||||
"tools": [
|
||||
"capture_native_ui_scenario",
|
||||
"demangle_swift",
|
||||
"inspect_macho",
|
||||
"inspect_plist",
|
||||
"inspect_signature",
|
||||
"list_architectures"
|
||||
"list_architectures",
|
||||
"observe_native_ui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "artifact",
|
||||
"surface": "artifact-provider",
|
||||
"count": 3,
|
||||
"tools": ["extract_artifact", "inspect_artifact", "inventory_artifact"]
|
||||
"count": 5,
|
||||
"tools": [
|
||||
"decode_interface_builder",
|
||||
"extract_artifact",
|
||||
"inspect_artifact",
|
||||
"inspect_asset_catalog",
|
||||
"inspect_keyed_archive"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "managed",
|
||||
"surface": "managed",
|
||||
"count": 8,
|
||||
"count": 7,
|
||||
"tools": [
|
||||
"compare_managed_members",
|
||||
"import_managed_reconstruction",
|
||||
"inspect_managed_artifact",
|
||||
"inspect_managed_members",
|
||||
"inspect_managed_native_boundaries",
|
||||
"plan_managed_runtime_correlation",
|
||||
"project_managed_application_graph",
|
||||
"verify_managed_native_boundaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "firmware",
|
||||
"surface": "firmware-provider",
|
||||
"count": 2,
|
||||
"tools": ["extract_firmware", "inspect_firmware_regions"]
|
||||
},
|
||||
{
|
||||
"id": "android",
|
||||
"surface": "android-provider",
|
||||
"count": 5,
|
||||
"tools": [
|
||||
"inspect_android_class",
|
||||
"inspect_android_method",
|
||||
"inspect_android_package",
|
||||
"search_android_classes",
|
||||
"trace_android_references"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "browser",
|
||||
"surface": "browser-provider",
|
||||
"count": 9,
|
||||
"count": 11,
|
||||
"tools": [
|
||||
"analyze_web_bundle",
|
||||
"capture_browser_scenario",
|
||||
@@ -120,17 +150,20 @@
|
||||
"compare_web_captures",
|
||||
"compare_web_screenshots",
|
||||
"discover_webmcp_tools",
|
||||
"inspect_web_event_listeners",
|
||||
"inspect_web_page",
|
||||
"list_browser_targets",
|
||||
"observe_web_execution",
|
||||
"observe_web_session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "electron",
|
||||
"surface": "electron-provider",
|
||||
"count": 4,
|
||||
"count": 5,
|
||||
"tools": [
|
||||
"analyze_javascript_application",
|
||||
"capture_electron_scenario",
|
||||
"inspect_electron_page",
|
||||
"list_electron_targets",
|
||||
"reconcile_javascript_runtime"
|
||||
@@ -148,26 +181,27 @@
|
||||
{
|
||||
"id": "application",
|
||||
"surface": "application-workflow",
|
||||
"count": 12,
|
||||
"count": 13,
|
||||
"tools": [
|
||||
"build_reconstruction_obligation_ledger",
|
||||
"commit_reconstruction_coverage",
|
||||
"compare_application_versions",
|
||||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_readiness",
|
||||
"execute_node_characterization",
|
||||
"prepare_node_characterization",
|
||||
"query_reconstruction_coverage",
|
||||
"run_controlled_replay",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"export_web_scripts",
|
||||
"project_android_application_graph",
|
||||
"project_apple_application_graph",
|
||||
"recover_javascript_sources",
|
||||
"trace_application_feature",
|
||||
"trace_javascript_semantics"
|
||||
"trace_javascript_semantics",
|
||||
"trace_web_module_imports",
|
||||
"trace_web_source_location"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "session",
|
||||
"surface": "session",
|
||||
"count": 19,
|
||||
"count": 21,
|
||||
"tools": [
|
||||
"binary_session",
|
||||
"build_call_path",
|
||||
@@ -180,11 +214,13 @@
|
||||
"correlate_static_and_runtime",
|
||||
"export_evidence_bundle",
|
||||
"find_changed_behavior",
|
||||
"get_evidence_bundle",
|
||||
"get_navigation_context",
|
||||
"import_evidence_bundle",
|
||||
"inspect_address_context",
|
||||
"list_unknowns",
|
||||
"open_binary",
|
||||
"record_unknown",
|
||||
"run_replay_machine",
|
||||
"update_unknown",
|
||||
"verify_reconstruction",
|
||||
"verify_unknown_resolution"
|
||||
@@ -193,13 +229,24 @@
|
||||
]
|
||||
},
|
||||
"providers": [
|
||||
{
|
||||
"id": "binwalk",
|
||||
"name": "Binwalk",
|
||||
"version": "3.1.0",
|
||||
"capabilities": ["inspect_firmware_regions"]
|
||||
},
|
||||
{
|
||||
"id": "ghidra",
|
||||
"name": "Ghidra",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"address_name",
|
||||
"address_to_file_offset",
|
||||
"analyze_function",
|
||||
"annotate_native_function",
|
||||
"inspect_native_data_type",
|
||||
"inspect_native_instruction",
|
||||
"inspect_native_load_image",
|
||||
"list_documents",
|
||||
"list_names",
|
||||
"list_procedures",
|
||||
@@ -212,8 +259,10 @@
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_references",
|
||||
"read_bytes",
|
||||
"read_function_instructions",
|
||||
"resolve_containing_procedure",
|
||||
"resolve_native_call_targets",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"xrefs"
|
||||
@@ -263,26 +312,73 @@
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ida",
|
||||
"name": "IDA Pro MCP adapter",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"analyze_function",
|
||||
"list_procedures",
|
||||
"list_strings",
|
||||
"procedure_address",
|
||||
"procedure_assembly",
|
||||
"procedure_callees",
|
||||
"procedure_callers",
|
||||
"procedure_pseudo_code",
|
||||
"read_function_instructions",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
"xrefs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "jadx",
|
||||
"name": "JADX headless",
|
||||
"version": "0.7.1",
|
||||
"capabilities": [
|
||||
"inspect_android_class",
|
||||
"inspect_android_method",
|
||||
"inspect_android_package",
|
||||
"search_android_classes",
|
||||
"trace_android_references"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "native-macos",
|
||||
"name": "macOS native inspection utilities",
|
||||
"version": null,
|
||||
"capabilities": [
|
||||
"capture_native_ui_scenario",
|
||||
"demangle_swift",
|
||||
"inspect_macho",
|
||||
"inspect_plist",
|
||||
"inspect_signature",
|
||||
"list_architectures"
|
||||
"list_architectures",
|
||||
"observe_native_ui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-android-application",
|
||||
"name": "REA Android application workflows",
|
||||
"version": "1",
|
||||
"capabilities": ["project_android_application_graph"]
|
||||
},
|
||||
{
|
||||
"id": "rea-apple-application",
|
||||
"name": "REA Apple application workflows",
|
||||
"version": "1",
|
||||
"capabilities": ["project_apple_application_graph"]
|
||||
},
|
||||
{
|
||||
"id": "rea-artifact-graph",
|
||||
"name": "REA safe artifact graph provider",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"decode_interface_builder",
|
||||
"extract_artifact",
|
||||
"inspect_artifact",
|
||||
"inventory_artifact"
|
||||
"inspect_asset_catalog",
|
||||
"inspect_keyed_archive"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -295,8 +391,10 @@
|
||||
"compare_web_captures",
|
||||
"compare_web_screenshots",
|
||||
"discover_webmcp_tools",
|
||||
"inspect_web_event_listeners",
|
||||
"inspect_web_page",
|
||||
"list_browser_targets",
|
||||
"observe_web_execution",
|
||||
"observe_web_session"
|
||||
]
|
||||
},
|
||||
@@ -323,7 +421,6 @@
|
||||
"capabilities": [
|
||||
"compare_managed_members",
|
||||
"import_managed_reconstruction",
|
||||
"plan_managed_runtime_correlation",
|
||||
"project_managed_application_graph",
|
||||
"verify_managed_native_boundaries"
|
||||
]
|
||||
@@ -340,15 +437,10 @@
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"build_reconstruction_obligation_ledger",
|
||||
"commit_reconstruction_coverage",
|
||||
"compare_application_versions",
|
||||
"compare_javascript_export_shapes",
|
||||
"compare_source_to_bundle",
|
||||
"evaluate_reconstruction_readiness",
|
||||
"execute_node_characterization",
|
||||
"prepare_node_characterization",
|
||||
"query_reconstruction_coverage",
|
||||
"run_controlled_replay",
|
||||
"evaluate_reconstruction_coverage",
|
||||
"trace_application_feature",
|
||||
"trace_javascript_semantics"
|
||||
]
|
||||
@@ -365,6 +457,12 @@
|
||||
"version": "1",
|
||||
"capabilities": ["capture_browser_scenario"]
|
||||
},
|
||||
{
|
||||
"id": "rea-playwright-electron-active",
|
||||
"name": "REA Playwright active Electron observation provider",
|
||||
"version": "1",
|
||||
"capabilities": ["capture_electron_scenario"]
|
||||
},
|
||||
{
|
||||
"id": "rea-v8-inspector",
|
||||
"name": "REA passive Node/Electron V8 Inspector provider",
|
||||
@@ -373,6 +471,28 @@
|
||||
"list_javascript_runtime_targets",
|
||||
"observe_javascript_runtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rea-web-script-export",
|
||||
"name": "REA captured script export",
|
||||
"version": "1",
|
||||
"capabilities": [
|
||||
"export_web_scripts",
|
||||
"trace_web_module_imports",
|
||||
"trace_web_source_location"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "unblob",
|
||||
"name": "Unblob",
|
||||
"version": "26.6.4",
|
||||
"capabilities": ["extract_firmware"]
|
||||
},
|
||||
{
|
||||
"id": "wakaru",
|
||||
"name": "Wakaru",
|
||||
"version": "1.13.0",
|
||||
"capabilities": ["recover_javascript_sources"]
|
||||
}
|
||||
],
|
||||
"setup_clients": [
|
||||
@@ -415,184 +535,55 @@
|
||||
{
|
||||
"id": "devin",
|
||||
"display_name": "Devin",
|
||||
"format": "unsupported",
|
||||
"configuration": "detect-only"
|
||||
}
|
||||
],
|
||||
"schemas": [
|
||||
{
|
||||
"id": "analysis_snapshot",
|
||||
"version": 2
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "artifact_extraction",
|
||||
"version": 1
|
||||
"id": "opencode",
|
||||
"display_name": "OpenCode",
|
||||
"format": "opencode",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "artifact_graph",
|
||||
"version": 1
|
||||
"id": "antigravity",
|
||||
"display_name": "Antigravity",
|
||||
"format": "json",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "browser_scenario",
|
||||
"version": 1
|
||||
"id": "copilot_cli",
|
||||
"display_name": "GitHub Copilot CLI",
|
||||
"format": "copilot_cli",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "browser_scenario_capture",
|
||||
"version": 1
|
||||
"id": "commandcode",
|
||||
"display_name": "Command Code",
|
||||
"format": "commandcode",
|
||||
"configuration": "managed"
|
||||
},
|
||||
{
|
||||
"id": "browser_scenario_diff",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "browser_target_list",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "electron_page_inspection",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "electron_target_list",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "evidence",
|
||||
"version": 2
|
||||
},
|
||||
{
|
||||
"id": "evidence_bundle",
|
||||
"version": 2
|
||||
},
|
||||
{
|
||||
"id": "evidence_completion_ledger",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "evidence_completion_manifest",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "investigation_run",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "investigation_run_summary",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "investigation_workspace",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_application_analysis",
|
||||
"version": 2
|
||||
},
|
||||
{
|
||||
"id": "javascript_application_graph",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_application_version_comparison",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_feature_trace",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_runtime_observation",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_runtime_target_list",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_semantic_graph",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_semantic_query",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "javascript_semantic_trace",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "managed_artifact_inspection",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "managed_member_comparison",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "managed_member_inspection",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "managed_native_boundary_inspection",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "managed_native_verification",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "process_capture",
|
||||
"version": 4
|
||||
},
|
||||
{
|
||||
"id": "reconstruction_verification",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "residual_unknown",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "web_bundle_analysis",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "web_capture_diff",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "web_mcp_discovery",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "web_observation_session",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "web_page_inspection",
|
||||
"version": 2
|
||||
},
|
||||
{
|
||||
"id": "web_screenshot",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"id": "web_screenshot_diff",
|
||||
"version": 1
|
||||
"id": "vscode",
|
||||
"display_name": "VS Code",
|
||||
"format": "vscode",
|
||||
"configuration": "managed"
|
||||
}
|
||||
],
|
||||
"cli": {
|
||||
"primary_count": 67,
|
||||
"primary_count": 88,
|
||||
"commands": [
|
||||
"address-to-file-offset",
|
||||
"analyze",
|
||||
"analyze-javascript-application",
|
||||
"analyze-web-bundle",
|
||||
"annotate-native-function",
|
||||
"build-reconstruction-obligation-ledger",
|
||||
"capabilities",
|
||||
"capture-browser-scenario",
|
||||
"capture-electron-scenario",
|
||||
"capture-native-ui-scenario",
|
||||
"capture-process",
|
||||
"capture-web-screenshot",
|
||||
"commit-reconstruction-coverage",
|
||||
"compare",
|
||||
"compare-application-versions",
|
||||
"compare-javascript-export-shapes",
|
||||
@@ -601,54 +592,72 @@
|
||||
"compare-source-to-bundle",
|
||||
"compare-web-captures",
|
||||
"compare-web-screenshots",
|
||||
"decode-interface-builder",
|
||||
"decompile",
|
||||
"demangle-swift",
|
||||
"discover-webmcp-tools",
|
||||
"doctor",
|
||||
"evaluate-reconstruction-readiness",
|
||||
"evaluate-reconstruction-coverage",
|
||||
"evidence-export",
|
||||
"evidence-import",
|
||||
"execute-node-characterization",
|
||||
"export-web-scripts",
|
||||
"extract-artifact",
|
||||
"extract-firmware",
|
||||
"function",
|
||||
"import-managed-reconstruction",
|
||||
"import-reference-source",
|
||||
"inspect",
|
||||
"inspect-android-class",
|
||||
"inspect-android-method",
|
||||
"inspect-android-package",
|
||||
"inspect-artifact",
|
||||
"inspect-asset-catalog",
|
||||
"inspect-electron-page",
|
||||
"inspect-firmware-regions",
|
||||
"inspect-keyed-archive",
|
||||
"inspect-macho",
|
||||
"inspect-managed-artifact",
|
||||
"inspect-managed-members",
|
||||
"inspect-managed-native-boundaries",
|
||||
"inspect-native-api",
|
||||
"inspect-native-data-type",
|
||||
"inspect-native-dispatch-metadata",
|
||||
"inspect-native-instruction",
|
||||
"inspect-native-load-image",
|
||||
"inspect-plist",
|
||||
"inspect-signature",
|
||||
"inspect-web-event-listeners",
|
||||
"inspect-web-page",
|
||||
"instructions",
|
||||
"inventory-artifact",
|
||||
"investigate-versions",
|
||||
"list-architectures",
|
||||
"list-browser-targets",
|
||||
"list-electron-targets",
|
||||
"list-javascript-runtime-targets",
|
||||
"observe-javascript-runtime",
|
||||
"observe-native-ui",
|
||||
"observe-web-execution",
|
||||
"observe-web-session",
|
||||
"plan-managed-runtime-correlation",
|
||||
"policy",
|
||||
"prepare-node-characterization",
|
||||
"project-android-application-graph",
|
||||
"project-apple-application-graph",
|
||||
"project-managed-application-graph",
|
||||
"providers",
|
||||
"query-reconstruction-coverage",
|
||||
"read-bytes",
|
||||
"reconcile-javascript-runtime",
|
||||
"run-controlled-replay",
|
||||
"run-replay-machine",
|
||||
"recover-javascript-sources",
|
||||
"resolve-native-call-targets",
|
||||
"search",
|
||||
"search-android-classes",
|
||||
"setup",
|
||||
"trace",
|
||||
"trace-android-references",
|
||||
"trace-application-feature",
|
||||
"trace-javascript-semantics",
|
||||
"trace-native-ui-action",
|
||||
"trace-native-values",
|
||||
"trace-web-module-imports",
|
||||
"trace-web-source-location",
|
||||
"uninstall",
|
||||
"upgrade",
|
||||
"update",
|
||||
"verify-managed-native-boundaries",
|
||||
"xrefs"
|
||||
],
|
||||
@@ -661,18 +670,15 @@
|
||||
},
|
||||
"runtime_catalog": {
|
||||
"counts": {
|
||||
"cli_commands": 67,
|
||||
"mcp_tools": 111,
|
||||
"mcp_prompts": 6,
|
||||
"mcp_resources": 2,
|
||||
"mcp_resource_templates": 11
|
||||
"cli_commands": 88,
|
||||
"mcp_tools": 133,
|
||||
"mcp_prompts": 6
|
||||
},
|
||||
"digests": {
|
||||
"tools_sha256": "5a88bf598a1e0fe705761db6af92f1ceabaf026cb6efa425e87b9633f1d7a3de",
|
||||
"prompts_sha256": "f6da3ad38a1000c11364d1710f122ac4990551f04efd8c53ffb2e819b5cf5707",
|
||||
"resources_sha256": "339a984ea8cd8be5f234adf24395bf0295258d990492005cf6fe1f293d12cd46",
|
||||
"combined_sha256": "861cd8618e793342daa8b149bc2944360d84b077fd19cf22f6e0a57b6b249c73",
|
||||
"providers_sha256": "918ccd6b8ae442cd5844dcfde55086f458176241c8d166c1e9dd0f242ccd2bb0"
|
||||
"tools_sha256": "af7fe81ec689c2a77f04579b6f4beb4abe86aa71d49c4844d8e0f4d7ed7a3a1e",
|
||||
"prompts_sha256": "ef2de8814390b340623c32001610aa2709d90bed51537b6e562ab3abf144ab4d",
|
||||
"combined_sha256": "83b4c3445ed0ec003c0aeefa3188f123d9ae319484f8b1e2b611d2fe278c9c72",
|
||||
"providers_sha256": "aea15f517c822d80918ccaba57a4fa06557f484cf27b8acbaed4e1a660fac896"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+112
-92
@@ -1,25 +1,29 @@
|
||||
# Static-analysis provider evaluation
|
||||
|
||||
Status: the Ghidra read-only analysis provider is shipped on Linux x64 and has
|
||||
an experimental Windows x64 P0 for approved native PE applications. It
|
||||
validates an exact bring-your-own Ghidra 12.1.2/JDK 21 environment, resolves a
|
||||
provider/version/profile commitment, runs one isolated read-only headless
|
||||
import, and publishes 19 operation-level capabilities after the authenticated
|
||||
post-analysis handshake.
|
||||
This guide describes repository main. Check the [released package boundary](installation.md#released-package-and-main) when using npm.
|
||||
|
||||
The provider-neutral target, provider registry, deterministic target binding,
|
||||
analysis-profile commitment, Evidence provenance, snapshot v2, and bounded
|
||||
provider-process lifecycle foundations are implemented. The Ghidra launcher,
|
||||
packaged Java bridge, doctor/setup projection, bounded client lifecycle, and
|
||||
multi-target real Linux verifier, curated Windows CI, and controlled real
|
||||
Windows verifier are also implemented. Program identity,
|
||||
procedure/string/symbol inventory, memory blocks, address/name and
|
||||
containing-procedure resolution, bounded search, function metadata,
|
||||
decompilation, assembly, resolved calls, typed references, xrefs, CFG, and
|
||||
function dossiers are admitted. A provider is not a drop-in replacement for
|
||||
Hopper: every capability is mapped explicitly, with truthful `unavailable`,
|
||||
unknown, or degraded results where the engine cannot provide equivalent
|
||||
semantics.
|
||||
Ghidra read-only analysis is available on Linux x64 and macOS x64/arm64.
|
||||
Install a Ghidra 12.1.x release and the 64-bit full JDK that release declares,
|
||||
then configure REA to use them. Current 12.1 releases require JDK 21 or newer
|
||||
and set no maximum. The bridge is verified with Ghidra 12.1.4 and JDK 21.
|
||||
macOS installations need the matching native decompiler.
|
||||
|
||||
REA imports one target into a temporary project and exposes 25 read-only
|
||||
operations after analysis completes. They cover inventories, search,
|
||||
decompilation, assembly, function metadata, resolved calls, references, control
|
||||
flow, instructions, and recovered types. Results identify the provider version,
|
||||
analysis settings, target digest, and any unavailable or incomplete facts.
|
||||
On Linux and macOS, `annotate_native_function` additionally edits names and entry
|
||||
comments atomically, verifies readback, and returns refreshed function analysis.
|
||||
Changes live only in the session database and leave executable bytes unchanged.
|
||||
Ghidra does not expose GUI controls; Windows P0 remains read-only.
|
||||
|
||||
Windows x64 P0 supports native x86-64 PE applications on local NTFS with
|
||||
bundled Job Object ownership, protected runtime DACLs, and handle-based path
|
||||
admission. Real ordinary-user verification covers all 25 read-only operations
|
||||
through the packaged CLI and MCP, target integrity, and cleanup. This remains
|
||||
an experimental boundary; see [Windows Ghidra P0](windows-ghidra-p0.md) and
|
||||
[issue #527](https://github.com/morluto/rea/issues/527).
|
||||
|
||||
[ADR-0001](adr/0001-provider-selection-and-analysis-profiles.md) fixes the
|
||||
provider registry, deterministic selection, target binding, analysis profile,
|
||||
@@ -31,17 +35,17 @@ follow.
|
||||
- Keep the existing provider-neutral CLI and MCP tool names.
|
||||
- Bind one deep-analysis provider to a target for the target's lifetime; never
|
||||
fail over silently between Hopper and Ghidra.
|
||||
- Start with bring-your-own Ghidra and a compatible Java runtime. Setup must not
|
||||
install or upgrade Java.
|
||||
- Use an existing Ghidra installation and a compatible Java runtime. Setup must
|
||||
not install or upgrade Ghidra or Java.
|
||||
- Run Ghidra headlessly in an owned process with a private temporary project,
|
||||
bounded startup/analysis/request deadlines, cancellation, and cleanup.
|
||||
startup and cleanup deadlines, caller cancellation, and complete replies.
|
||||
- Prefer a packaged Java bridge loaded through Ghidra's script path. PyGhidra
|
||||
remains useful for prototypes but is not a mandatory production dependency.
|
||||
- Treat authenticated `ping` and `shutdown` as lifecycle proof only. Inventory,
|
||||
xref, CFG, and decompilation claims require separately admitted operation
|
||||
contracts and real-provider conformance.
|
||||
- Implement read-only inventories, assembly, decompilation, function metadata,
|
||||
calls, references, containment, and bounded search first.
|
||||
calls, references, containment, and complete inventory search first.
|
||||
- Report GUI cursor/navigation and persistent mutation operations as unavailable
|
||||
until their semantics and project ownership are explicitly designed.
|
||||
- Verify real claims on Linux with at least two distinct source-owned binaries.
|
||||
@@ -51,91 +55,107 @@ follow.
|
||||
|
||||
## Shipped foundation boundary
|
||||
|
||||
`GHIDRA_INSTALL_DIR` must identify an extracted official 12.1.2 release;
|
||||
optional `JAVA_HOME` must identify a 64-bit full JDK 21, otherwise doctor probes
|
||||
`java`/`javac` or `java.exe`/`javac.exe` from `PATH`. Linux accepts x86, x86-64,
|
||||
ARM, and ARM64 ELF, PE, and Mach-O executable targets. Windows P0 accepts only
|
||||
native x86-64 PE applications and rejects DLLs, managed images, unknown roles,
|
||||
other formats, and other architectures before launch.
|
||||
`GHIDRA_INSTALL_DIR` must identify an extracted Ghidra 12.1.x release. Optional
|
||||
`JAVA_HOME` must identify a 64-bit full JDK inside that installation's
|
||||
`application.java.min` and `application.java.max` (JDK 21 or newer, with no
|
||||
maximum, when the release leaves those at the 12.1 defaults). Otherwise doctor
|
||||
probes `java`/`javac` or `java.exe`/`javac.exe` from `PATH`. Supported Linux and macOS
|
||||
hosts accept compatible ELF, PE, and Mach-O executable targets. Host admission
|
||||
and target compatibility are separate checks. Windows x64 P0 admits only native,
|
||||
non-managed, non-DLL x86-64 PE applications on fixed local NTFS, with the packaged
|
||||
native authority available before launch.
|
||||
|
||||
The launcher creates one ephemeral runtime root with project,
|
||||
home/cache/config/data/temp, logs, descriptor, endpoint, target snapshot, and
|
||||
ownership manifest.
|
||||
It passes `-readOnly`, `-deleteProject`, a 300-second per-file analysis limit,
|
||||
two CPUs, and a 2 GiB heap; inherited Java option injection variables are
|
||||
cleared. On Linux, the mode-0600 descriptor carries the random token without
|
||||
It passes `-readOnly` and `-deleteProject`, and uses Ghidra's default analysis
|
||||
and resource settings; inherited Java option injection variables are cleared.
|
||||
On Linux, the mode-0600 descriptor carries the random token without
|
||||
exposing it in argv or environment and the Java bridge binds a mode-restricted
|
||||
Unix socket. Windows uses authenticated IPv4 loopback with a strict token-free
|
||||
endpoint record. Both transports report actual
|
||||
Ghidra/language/compiler/analysis/import-digest metadata, accept only the exact
|
||||
authenticated `ping`, `shutdown`, and ten
|
||||
inventory plus nine function-analysis methods, and deletes the socket. Close,
|
||||
cancellation, timeout,
|
||||
malformed protocol, or process exit stops the owned process resources and
|
||||
removes the entire runtime root. Windows P0 uses bounded `taskkill` tree
|
||||
termination and explicitly does not claim Job Object, private-DACL, or
|
||||
reparse-point authority; see [Windows Ghidra P0](windows-ghidra-p0.md).
|
||||
Unix socket. macOS uses the same local transport. The bridge reports actual
|
||||
Ghidra/language/compiler/analysis/import-digest metadata and accepts only
|
||||
authenticated `ping`, `shutdown`, thirteen read-only inventory/load-image methods,
|
||||
and twelve function-analysis methods. Linux/macOS also admits atomic function
|
||||
annotation changes in the ephemeral database. Close, cancellation, timeout, malformed protocol,
|
||||
or process exit stops the owned process resources, closes the socket, and
|
||||
removes the runtime root.
|
||||
|
||||
The provider catalog lists only the 19 proved Ghidra operations. GUI cursor,
|
||||
navigation, and mutation operations remain absent; the router therefore
|
||||
The experimental Windows transport uses authenticated IPv4 loopback with a
|
||||
token-free endpoint record. The bundled native authority establishes Job Object
|
||||
ownership, protected runtime DACLs, and handle-based path admission before
|
||||
launch. If the bundle or an admission constraint is unavailable, the provider
|
||||
remains unavailable; a `taskkill` fallback does not establish that authority.
|
||||
|
||||
The provider catalog lists 25 read-only Ghidra operations and the Linux/macOS
|
||||
session annotation operation. GUI cursor/navigation and persistent mutations
|
||||
remain unavailable; Windows P0 admits no database mutation. The router therefore
|
||||
reports them unavailable instead of borrowing Hopper semantics or inferring
|
||||
capability from a successful import.
|
||||
|
||||
## Admitted inventory semantics
|
||||
|
||||
| Concern | Ghidra contract |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Program identity | One `analyzeHeadless` import produces exactly one Program; `list_documents` therefore returns exactly one name. |
|
||||
| Addresses | Default memory uses lowercase `0x` hexadecimal. Non-default and external spaces use `<percent-encoded-space>:0x<hex>` and remain round-trippable. The handshake commits image base and default address-space name. |
|
||||
| Symbols | `list_names` includes address-bearing memory and external symbols, including dynamic symbols, while excluding variable and no-address namespace records. Each item reports primary, dynamic, external, symbol type, and source facts. |
|
||||
| Procedures | Both non-external and external functions are listed. A local thunk remains distinct from its resolved target; exact and qualified name lookup fails on ambiguity rather than guessing. |
|
||||
| Strings | Only Ghidra-defined string `Data` is observed. Items report charset, byte length, and whether a required null terminator is missing. The API cannot distinguish a present terminator from fixed/Pascal layouts when no terminator is missing, so that state is named `present_or_not_required`. |
|
||||
| Memory | Memory-block end addresses are exclusive. Read/write/execute, initialization, overlay, address space, and image base are direct Ghidra observations. |
|
||||
| Pagination | List limits are 500 and search limits are 100. Pages commit exact totals and advancing offsets; an inventory above one million items fails rather than returning a partial result labeled exhaustive. |
|
||||
| Search | Literal search scans the immutable inventory with a 1,000,000-unit cumulative work budget. Regex uses a conservative finite Java-regex subset with 10,000 static paths, 4,096 UTF-16 code units per candidate, and the same cumulative budget. Exceeding a budget fails explicitly; returned values identify truncation. |
|
||||
| Analysis state | The socket is exposed only after default auto-analysis. A 300-second analysis timeout fails target opening; ordinary established requests have a 10-second client deadline and every response has a 1 MiB ceiling. |
|
||||
| Concern | Ghidra contract |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Program identity | One `analyzeHeadless` import produces exactly one Program; `list_documents` therefore returns exactly one name. |
|
||||
| Addresses | Default memory uses lowercase `0x` hexadecimal. Non-default and external spaces use `<percent-encoded-space>:0x<hex>` and remain round-trippable. The handshake commits image base and default address-space name. |
|
||||
| Symbols | `list_names` includes address-bearing memory and external symbols, including dynamic symbols, while excluding variable and no-address namespace records. Each item reports primary, dynamic, external, symbol type, and source facts. |
|
||||
| Procedures | Both non-external and external functions are listed. A local thunk remains distinct from its resolved target; exact and qualified name lookup fails on ambiguity rather than guessing. |
|
||||
| Strings | Only Ghidra-defined string `Data` is observed. Items report charset, byte length, and whether a required null terminator is missing. The API cannot distinguish a present terminator from fixed/Pascal layouts when no terminator is missing, so that state is named `present_or_not_required`. |
|
||||
| Memory | Memory-block end addresses are exclusive. Read/write/execute, initialization, overlay, address space, and image base are direct Ghidra observations. |
|
||||
| Inventory | Procedure, symbol, and string listings plus searches return the complete matching collection in one response; callers do not provide offsets or result-count limits. |
|
||||
| Search | Literal and Java-regex searches scan the complete immutable inventory and return all matching entries inline. Search waits for a reply or caller cancellation; there are no caller-supplied offsets or result-count limits. |
|
||||
| Analysis state | The socket is exposed only after default auto-analysis completes. Established operations wait for their reply or caller cancellation; there is no fixed response-size ceiling. |
|
||||
|
||||
## Admitted function-analysis semantics
|
||||
|
||||
| Concern | Ghidra contract |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Decompiler lifetime | One persistent `DecompInterface` is opened for the imported Program and disposed during bridge shutdown. Each native decompile has a 30-second deadline; the socket leaves a bounded 35-second projection window. External functions or functions without bodies return `null`; timeout, cancellation, and native failure remain distinct. |
|
||||
| Serialization | A bounded FIFO admits at most 32 active-plus-queued requests and sends one Program request at a time. Queue wait counts against the caller's deadline, and queued cancellation is prompt. 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 bounded Ghidra Listing text; pseudocode is bounded Ghidra decompiler output. Neither is original source, and cross-provider comparison never treats Hopper and Ghidra text as equal or unequal semantic facts. |
|
||||
| Instruction fast path | `read_function_instructions` returns one offset-paginated raw Listing window without invoking the decompiler or whole-program name/string inventories. It reports scan work, exact-or-unknown totals, truncation, and continuation metadata. |
|
||||
| Calls and references | Callers/callees contain only resolved functions. Reference edges preserve exact ReferenceManager type and call/jump/data/read/write/indirect/computed/conditional/terminal/external facts. Targetless computed flow remains unknown. Synthetic entry-point references without actionable memory sources are omitted explicitly. |
|
||||
| CFG | Dossiers use `BasicBlockModel` and retain only non-call successors inside the function body. CFG topology is address-normalized for comparison; provider-specific block construction remains a declared difference. |
|
||||
| Bounds | Direct assembly fails above 100,000 instructions or the wire ceiling. Dossiers scan at most 5,000 requested instructions, return bounded independent pages, mark unknown totals after an incomplete scan, and paginate pseudocode by Unicode code points. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| Instruction fast path | `read_function_instructions` returns every raw Listing instruction for the requested function without invoking the decompiler or whole-program name/string inventories. |
|
||||
| Calls and references | Callers/callees contain only resolved functions. Reference edges preserve exact ReferenceManager type and call/jump/data/read/write/indirect/computed/conditional/terminal/external facts. Targetless computed flow remains unknown. Synthetic entry-point references without actionable memory sources are omitted explicitly. |
|
||||
| CFG | Dossiers use `BasicBlockModel` and retain only non-call successors inside the function body. CFG topology is address-normalized for comparison; provider-specific block construction remains a declared difference. |
|
||||
| P-code value flow | Function dossiers include up to 3,000 high-p-code operations, at most 64 inputs per operation, and 12,000 def-use edges. Results identify memory reads/writes, branches, and calls by p-code opcode, and mark decompiler-dead operations. Large results report truncation, an omitted-operation lower bound, and known omitted-input/edge counts. The dossier is an intra-function graph; `trace_native_values` composes derived parameter/argument and return/output dependencies across resolved calls. It does not resolve memory aliasing, call side effects, runtime behavior, persistent state, or business meaning. |
|
||||
| Result extent | Function instruction scans and native API boundary observations remain complete. P-code flow is the explicit bounded exception; its count fields distinguish exact retained counts from lower-bound or known-omitted counts. Caller cancellation and provider failures remain distinct. |
|
||||
|
||||
`npm run verify:ghidra` compiles the versioned C oracles into x86-64 debug and
|
||||
stripped ELF, AArch64 ELF, x86-64 PE, and x86-64 Mach-O targets. It proves all
|
||||
19 operations, external functions, resolved thunks, exports, stripped-name
|
||||
`npm run verify:ghidra` compiles the versioned C oracle into debug and stripped
|
||||
host-native targets (x86-64 ELF on Linux x64 or Mach-O on macOS), plus a native
|
||||
DWARF 4 type-layout object. It proves the admitted operations, external functions, resolved thunks, exports, stripped-name
|
||||
behavior, direct and targetless indirect calls, typed references, strings/xrefs,
|
||||
multi-block CFG, semantic enhanced workflows, cancellation, deadlines,
|
||||
multi-block CFG, bounded p-code def-use/effect links, semantic enhanced workflows, cancellation, startup deadlines,
|
||||
serialized concurrency, malformed-target rejection, profile identity, and
|
||||
process/project cleanup against real Ghidra 12.1.2. Unit fixtures separately
|
||||
cover analysis/decompile timeouts, process exit, queue saturation, and malformed
|
||||
wire output.
|
||||
process/project cleanup against real Ghidra 12.1.4. Unit fixtures separately
|
||||
cover startup deadlines, process exit, queued cancellation, and malformed wire
|
||||
output.
|
||||
|
||||
`npm run verify:ghidra:windows` generates a deterministic source-owned native
|
||||
x86-64 PE application, proves all 19 operations through the production Windows
|
||||
launcher and loopback transport, checks target/snapshot/import SHA-256 linkage,
|
||||
and requires project, endpoint, process, and runtime cleanup. Passing that lane
|
||||
proves only the documented P0 boundary, not the remaining Windows security
|
||||
gates.
|
||||
`npm run verify:ghidra:cross-format` adds AArch64 ELF, x86-64 PE, and x86-64
|
||||
Mach-O target coverage. It requires `clang`, LLD, and `lld-link`; set
|
||||
`REA_CLANG`, `REA_LLD`, or `REA_LLD_LINK` to select them. The verifier checks
|
||||
these tools before compilation. Keeping this matrix separate lets Linux
|
||||
host/provider acceptance run with only the host compiler.
|
||||
|
||||
`npm run verify:ghidra:windows` checks the source-owned native x86-64 PE
|
||||
fixture, all 25 read-only operations, target/snapshot/import SHA-256 linkage,
|
||||
and project, endpoint, process, and runtime cleanup. The independent native
|
||||
lane checks DACLs, handle admission, cancellation, and Job Object lifecycle.
|
||||
`npm run verify:ghidra:windows:package` packs and installs REA into an isolated
|
||||
prefix, then checks ordinary-user CLI/MCP operations against canonical contracts.
|
||||
All lanes require matching native controls; package startup alone establishes
|
||||
only package compatibility.
|
||||
|
||||
## Shared provider-process foundation
|
||||
|
||||
`src/process/` now provides the mechanisms that a long-lived Hopper or Ghidra
|
||||
adapter genuinely shares: POSIX run-token-authenticated process-group ownership,
|
||||
adapter shares: POSIX run-token-authenticated process-group ownership,
|
||||
ephemeral temporary runtime roots, one absolute startup deadline, correlated
|
||||
request timeout/cancellation cleanup, bounded stdout and stderr retention with
|
||||
request cancellation and lifecycle-deadline cleanup, bounded stdout and stderr retention with
|
||||
exact byte counts, process-exit diagnostics, and bounded TERM-to-KILL shutdown.
|
||||
Reusable fixtures exercise exit, timeout, cancellation, graceful termination,
|
||||
forced termination, double-close, spawn failure, and resource release. Windows
|
||||
P0 adds bounded process-tree termination as an explicitly weaker host backend;
|
||||
Job Object ownership remains unimplemented.
|
||||
Ghidra uses its native Job Object, private runtime, and path-admission boundary;
|
||||
generic process-tree termination alone cannot establish provider availability.
|
||||
|
||||
The foundation does not define a bridge schema, socket framing, health payload,
|
||||
analysis model, or shutdown acknowledgement. Hopper keeps its authenticated
|
||||
@@ -145,12 +165,12 @@ only the generic process mechanisms.
|
||||
|
||||
## Shortlist
|
||||
|
||||
| Provider | License / automation surface | What it brings | REA fit and blockers |
|
||||
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Ghidra](https://github.com/NationalSecurityAgency/ghidra) | Apache-2.0 source license; `analyzeHeadless`, Java APIs, and PyGhidra | Broad static analysis, many processors and formats, scripting, and project/database workflows | Read-only analysis shipped: exact BYO checks, isolated state, bounded process and queue, packaged bridge, 19 admitted inventory/function operations, and real ELF/PE/Mach-O x86-64 plus AArch64 conformance. GUI and mutation semantics remain intentionally unavailable. |
|
||||
| [Rizin](https://github.com/rizinorg/rizin) / [rz-pipe](https://github.com/rizinorg/rz-pipe) | Rizin repository contains LGPL-3.0 and GPL-3.0 components; `rizin`, `rz-bin`, and language bridges through `rzpipe` | Portable CLI analysis, disassembly/debugging, many architectures and file formats, JSON command output | Good candidate for a process-backed Linux provider and fast metadata fallback. License/component inventory must be preserved; command output needs version-pinned parsers and semantic conformance before evidence is trusted. |
|
||||
| [LIEF](https://github.com/lief-project/LIEF) | Apache-2.0; C++, Python, and other bindings | Deterministic parsing and modification of ELF, PE, Mach-O, COFF, and related executable formats; headers, sections, symbols, relocations, and functions | Best near-term complement, not a decompiler replacement. It can cover format metadata and artifact evidence without a long-lived analysis process; function semantics, pseudocode, CFG, and cross-reference parity remain out of scope unless separately demonstrated. |
|
||||
| [Binary Ninja](https://docs.binary.ninja/dev/index.html) | API/documentation components are MIT, while the analysis product is licensed by edition; commercial, Ultimate, or Headless license is required for headless automation | Python/Core/C++/Rust APIs, headless loading, IL layers, function analysis, plugins, and configurable analysis | Strong technical fit for a native provider, especially function dossiers. Commercial licensing, license-secret handling, native runtime packaging, and multithreaded lifecycle rules are material deployment blockers. |
|
||||
| Provider | License / automation surface | What it brings | REA fit and blockers |
|
||||
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Ghidra](https://github.com/NationalSecurityAgency/ghidra) | Apache-2.0 source license; `analyzeHeadless`, Java APIs, and PyGhidra | Static analysis, multiple processors and formats, scripting, and project/database workflows | 25 read-only operations shipped on Linux/macOS with installation checks, temporary projects, a serial API queue, and real ELF/PE/Mach-O conformance. Experimental Windows x64 P0 supports native x86-64 PE on local NTFS with bundled native controls and ordinary-user CLI/MCP verification; Linux/macOS additionally support atomic session function annotations; Windows mutation and all Ghidra GUI operations are unavailable. |
|
||||
| [Rizin](https://github.com/rizinorg/rizin) / [rz-pipe](https://github.com/rizinorg/rz-pipe) | Rizin repository contains LGPL-3.0 and GPL-3.0 components; `rizin`, `rz-bin`, and language bridges through `rzpipe` | Portable CLI analysis, disassembly/debugging, many architectures and file formats, JSON command output | Good candidate for a process-backed Linux provider and fast metadata fallback. License/component inventory must be preserved; command output needs version-pinned parsers and semantic conformance before evidence is trusted. |
|
||||
| [LIEF](https://github.com/lief-project/LIEF) | Apache-2.0; C++, Python, and other bindings | Deterministic parsing and modification of ELF, PE, Mach-O, COFF, and related executable formats; headers, sections, symbols, relocations, and functions | Best near-term complement, not a decompiler replacement. It can cover format metadata and artifact evidence without a long-lived analysis process; function semantics, pseudocode, CFG, and cross-reference parity remain out of scope unless separately demonstrated. |
|
||||
| [Binary Ninja](https://docs.binary.ninja/dev/index.html) | API/documentation components are MIT, while the analysis product is licensed by edition; commercial, Ultimate, or Headless license is required for headless automation | Python/Core/C++/Rust APIs, headless loading, IL layers, function analysis, plugins, and configurable analysis | Strong technical fit for a native provider, especially function dossiers. Commercial licensing, license-secret handling, native runtime packaging, and multithreaded lifecycle rules are material deployment blockers. |
|
||||
|
||||
## Recommended order
|
||||
|
||||
@@ -170,7 +190,7 @@ Before implementation, an adapter proposal must provide:
|
||||
|
||||
- a capability matrix covering supported, unsupported, and degraded results;
|
||||
- provider identity, version, analysis profile, target digest, authority,
|
||||
limitations, and deterministic locations in every Evidence v2 record;
|
||||
limitations, and deterministic locations in every Evidence record;
|
||||
- bounded subprocess or library lifetime, cancellation, timeouts, and cleanup;
|
||||
- actionable local diagnostics that retain paths, digests, mismatch locations,
|
||||
and provider metadata while redacting credentials, authorization headers,
|
||||
@@ -184,9 +204,9 @@ prematurely claiming equivalence or redistribution support.
|
||||
|
||||
## Primary-source notes
|
||||
|
||||
- Ghidra 12.1.2 documents headless batch mode and its Java runtime requirements
|
||||
in its [release-specific Getting Started guide](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.2_build/GhidraDocs/GettingStarted.md);
|
||||
the [official release](https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_12.1.2_build)
|
||||
- Ghidra 12.1.4 documents headless batch mode and its Java runtime requirements
|
||||
in its [release-specific Getting Started guide](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/GhidraDocs/GettingStarted.md);
|
||||
the [official release](https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_12.1.4_build)
|
||||
supplies the corresponding distribution and checksums.
|
||||
- Rizin lists supported formats, architectures, tools, and `rzpipe` bridges in
|
||||
its [repository README](https://github.com/rizinorg/rizin); `rz-pipe` documents
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
# Reconstruction obligation ledgers
|
||||
|
||||
`build_reconstruction_obligation_ledger` turns authenticated Evidence v2 records
|
||||
into a versioned, deterministic list of reconstruction claims. The same workflow
|
||||
`build_reconstruction_obligation_ledger` turns authenticated Evidence records
|
||||
into a deterministic list of reconstruction claims. The same workflow
|
||||
is available as:
|
||||
|
||||
- MCP tool: `build_reconstruction_obligation_ledger`
|
||||
- CLI command: `rea build-reconstruction-obligation-ledger`
|
||||
- MCP resource:
|
||||
`rea://evidence/{evidenceId}/reconstruction-obligation-ledger`
|
||||
|
||||
The ledger is conservative by design. Static Application Graph facts create
|
||||
candidate obligations; they do not prove runtime or process behavior. A required
|
||||
@@ -29,7 +27,6 @@ never claims closure:
|
||||
rea build-reconstruction-obligation-ledger \
|
||||
'{
|
||||
"evidence_bundle": {
|
||||
"bundle_version": 2,
|
||||
"artifacts": [],
|
||||
"providers": [],
|
||||
"environments": [],
|
||||
@@ -40,18 +37,15 @@ rea build-reconstruction-obligation-ledger \
|
||||
},
|
||||
"reviewed_obligations": [],
|
||||
"manifest": {
|
||||
"schema_version": 1,
|
||||
"bindings": [],
|
||||
"contradictions": []
|
||||
},
|
||||
"limits": { "max_obligations": 100 },
|
||||
"page": { "offset": 0, "limit": 50 }
|
||||
}
|
||||
}' \
|
||||
--json
|
||||
```
|
||||
|
||||
For a real investigation, export an Evidence bundle from the session and replace
|
||||
the empty bundle. Application Graph and Process Capture v4 records generate
|
||||
the empty bundle. Application Graph and process capture records generate
|
||||
conservative candidates automatically. Add reviewed obligations when a required
|
||||
claim is not represented by those sources.
|
||||
|
||||
@@ -326,8 +320,6 @@ close a `packaged-process` obligation. A static-only source cannot close a
|
||||
runtime or process claim. Marking a claim `blocked` or `out-of-scope` records the
|
||||
disposition but does not silently count it as verified.
|
||||
|
||||
Pagination changes only `page` and the returned `obligations` slice. Every page
|
||||
retains the same `ledger_id`, `closure_digest`, full-ledger summaries, reports,
|
||||
ownership graph, dependency graph, Evidence links, and limitations. Consumers
|
||||
should follow `next_offset` until it is `null`, and should reject pages whose
|
||||
identity or closure digest changes mid-read.
|
||||
The tool returns every obligation inline. Its `ledger_id` and `closure_digest`
|
||||
identify the complete result, including summaries, reports, ownership and
|
||||
dependency graphs, Evidence links, and limitations.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Reconstruction readiness conformance
|
||||
|
||||
Run the complete source-owned native, JavaScript CLI, Electron, Evidence,
|
||||
comparison, obligation, replay, CLI, and MCP journey from a clean checkout:
|
||||
Run the source-owned native, JavaScript CLI, Electron, Evidence, comparison,
|
||||
obligation, replay, and cleanup conformance verifier from a clean checkout:
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
@@ -10,10 +10,10 @@ npm run verify:readiness
|
||||
|
||||
The command builds the redistributable native conformance fixtures, inventories
|
||||
the native executable through the public CLI, analyzes the generated JavaScript
|
||||
CLI and Electron fixture through both CLI and MCP, verifies exact advertised
|
||||
limits, evaluates the same nine-stage report through both surfaces, reads the
|
||||
full MCP report resource, and proves stale/tampered replay rejection. It prints
|
||||
one compact JSON summary.
|
||||
CLI and Electron fixtures through both CLI and MCP, evaluates an internal
|
||||
nine-stage conformance report, and proves stale/tampered replay rejection. The
|
||||
report evaluator is part of the repository verifier, not a public REA tool. It
|
||||
prints one compact JSON summary.
|
||||
|
||||
Set `REA_READINESS_REPORT_PATH` to retain the complete machine-readable report:
|
||||
|
||||
@@ -24,17 +24,11 @@ REA_READINESS_REPORT_PATH=readiness-report.json npm run verify:readiness
|
||||
CI runs this command on Linux, publishes the compact summary, and retains the
|
||||
full report as the `reconstruction-readiness` artifact.
|
||||
|
||||
## Public contracts
|
||||
## Project conformance contract
|
||||
|
||||
- CLI: `rea evaluate-reconstruction-readiness <json-or-file> --json`
|
||||
- MCP tool: `evaluate_reconstruction_readiness`
|
||||
- Full MCP resource:
|
||||
`rea://evidence/{evidenceId}/reconstruction-readiness-report`
|
||||
|
||||
The MCP tool result contains only report identity, status, summary, metrics, and
|
||||
the resource URI. The full Evidence bundle, stage records, comparisons,
|
||||
contradictions, obligation ledger, and replay inputs stay in the retained
|
||||
resource instead of consuming the tool-response context.
|
||||
The verifier runs as `npm run verify:readiness`; it is not exposed as a public
|
||||
CLI command or MCP tool. The synthetic report input and evaluator exist to
|
||||
check project-owned conformance fixtures and fail-closed report behavior.
|
||||
|
||||
## Required stages
|
||||
|
||||
@@ -42,7 +36,7 @@ resource instead of consuming the tool-response context.
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| `discover-classify` | target-free startup, artifact inventory, compatible workflow selection, and exact visible limits |
|
||||
| `diagnose-environment` | broken runtime separated from analysis failure, version skew detected, and revision changed after recovery |
|
||||
| `acquire-authority` | exact missing scope, narrow grant, expansion re-prompt, and no launch after denial/cancellation |
|
||||
| `declare-request` | exact target, operation, and lifecycle are carried by the request before launch |
|
||||
| `static-analysis` | native and JavaScript routing plus preserved ambiguity, truncation, unsupported syntax, and residual unknowns |
|
||||
| `reactive-scenarios` | predicate-driven interaction, descendant settlement, Electron correlation, and cancellation cleanup Evidence |
|
||||
| `compare-authority-candidate` | explicit partial-order/finite-trace semantics, primary divergence localization, and unknown for incomplete comparison |
|
||||
@@ -51,7 +45,7 @@ resource instead of consuming the tool-response context.
|
||||
| `export-replay` | CLI/MCP parity, process/structured failure parity, deterministic replay, tamper rejection, and stale-input rejection |
|
||||
|
||||
Every stage and required check must occur exactly once and link to Evidence in
|
||||
the embedded Evidence v2 bundle. `fail`, `unknown`, `unsupported`, `truncated`,
|
||||
the embedded Evidence bundle. `fail`, `unknown`, `unsupported`, `truncated`,
|
||||
and `skipped` never aggregate to `pass`.
|
||||
|
||||
## Fail-closed rules
|
||||
@@ -69,7 +63,7 @@ The evaluator rejects or withholds readiness when:
|
||||
- contradiction Evidence or its affected comparison is missing;
|
||||
- the reconstruction obligation ledger is not `ready`, has no required
|
||||
obligation, or retains an unowned/unverified required obligation;
|
||||
- denied authority launches a process, cancellation loses diagnostics, or owned
|
||||
- a process exceeds its declared target/lifecycle, cancellation loses diagnostics, or owned
|
||||
resources remain;
|
||||
- replay is nondeterministic, tampering/staleness is not detected, or the
|
||||
expected source digest differs.
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# Releasing from a checkpoint
|
||||
|
||||
REA releases use a frozen source checkpoint. Main can continue accepting
|
||||
changes while a release is tested and published. The Release workflow runs
|
||||
on `release/*` branches; main pushes do not refresh release PRs.
|
||||
|
||||
## 1. Cut the release branch
|
||||
|
||||
Choose the next version from the unreleased Conventional Commits, including
|
||||
breaking changes. Record the full source SHA and create `release/VERSION` at
|
||||
that commit. The version is a maintainer choice; Release Please's proposed
|
||||
version must agree before publication.
|
||||
|
||||
For example, after selecting a reviewed commit for 5.0.0:
|
||||
|
||||
```bash
|
||||
git fetch origin main
|
||||
git branch release/5.0.0 SOURCE_SHA
|
||||
git push origin release/5.0.0
|
||||
```
|
||||
|
||||
Replace `SOURCE_SHA` with the selected full commit SHA. The checkpoint must
|
||||
include this release workflow and CI support for release branches. For an
|
||||
older checkpoint, backport only the release infrastructure first and record
|
||||
that additional commit. Do not merge later implementation changes into the
|
||||
release branch or rebase the candidate onto a moving main.
|
||||
|
||||
The branch push runs Release Please against that branch and opens a PR for
|
||||
the version, changelog, and registry metadata. The generation step normalizes
|
||||
the product catalog. To retry preparation after a failed run, select the same
|
||||
frozen branch explicitly:
|
||||
|
||||
```bash
|
||||
gh workflow run release.yml --ref release/5.0.0
|
||||
```
|
||||
|
||||
## 2. Review and merge the bot PR
|
||||
|
||||
Record the final bot PR head after normalization. Approve GitHub-blocked bot
|
||||
workflow runs for that head when needed. Review the candidate's version,
|
||||
notes, generated metadata, and package contents. Wait for the candidate's CI
|
||||
and relevant real-provider checks. Routine local iterations need focused
|
||||
checks; CI owns full deterministic coverage and platform lanes. If an artifact
|
||||
or real-provider check is unavailable, report that limit before deciding to
|
||||
publish.
|
||||
|
||||
Merge the reviewed PR into `release/5.0.0` with its head SHA matched. **This
|
||||
merge authorizes and triggers publication.** Further main commits do not change
|
||||
this candidate. Do not push or merge anything else into the release branch
|
||||
while publication is running. A necessary release fix must be reviewed and
|
||||
tested before the bot PR merges and establishes a new recorded checkpoint.
|
||||
|
||||
## 3. Verify publication
|
||||
|
||||
The merge push runs Release Please again, creates the tag and GitHub release,
|
||||
and publishes npm followed by MCP Registry metadata. Both publishers check
|
||||
out Release Please's exact release SHA. The triggering workflow also runs
|
||||
from that release commit so npm's provenance records the actual source.
|
||||
|
||||
Before invoking Release Please, the workflow checks that the release branch
|
||||
still equals the triggering SHA and rejects main or tag refs. Before either
|
||||
registry publish, it rejects a mismatch between the tagged release SHA and
|
||||
the triggering SHA. These checks detect moved branches; maintainers must also
|
||||
keep the release branch frozen throughout publication.
|
||||
See [npm's provenance implementation](https://github.com/npm/cli/blob/v11.16.0/workspaces/libnpmpublish/lib/provenance.js)
|
||||
for the use of GitHub's workflow ref and commit SHA.
|
||||
|
||||
The workflow builds the bundled Windows controls and verifies the packaged
|
||||
artifact before npm publication. It then verifies the published CLI, the
|
||||
capability-scoped MCP catalog, and the isolated package update path before
|
||||
publishing MCP Registry metadata.
|
||||
|
||||
Record these outcomes separately:
|
||||
|
||||
- GitHub release and tag, including the resolved commit SHA.
|
||||
- npm's exact version and integrity, with the published-package canary passed.
|
||||
- MCP Registry's exact server version and matching npm package version.
|
||||
|
||||
A GitHub tag alone does not establish npm or MCP Registry publication.
|
||||
|
||||
## Sync metadata back to main
|
||||
|
||||
After publication, open a PR from the release branch back to main. Preserve
|
||||
main's later implementation changes and resolve generated-file conflicts by
|
||||
regenerating from the combined contracts with the released package version.
|
||||
Review and test this synchronization PR, then use a merge commit so the release
|
||||
tag remains in main's ancestry. Keep the released tag unchanged.
|
||||
|
||||
Close any superseded rolling release PR. Future releases repeat the checkpoint
|
||||
procedure from main; never resume automatic release-PR refreshes on main pushes.
|
||||
|
||||
## Partial publication and retries
|
||||
|
||||
Inspect the failed job and public registry state before retrying. When npm is
|
||||
already published, the npm job verifies that exact version instead of
|
||||
publishing it again. Re-run failed jobs in the original publication run so its
|
||||
release SHA and outputs stay fixed. If only MCP publication failed, retry that
|
||||
job after checking that the npm canary succeeded.
|
||||
|
||||
Do not start a fresh workflow run to repair an already-created release:
|
||||
Release Please will not create the same release again. Do not move the tag,
|
||||
delete the release, or unpublish npm as a retry. A defective public package
|
||||
requires a reviewed correction and a new version.
|
||||
+71
-27
@@ -1,27 +1,71 @@
|
||||
# Installation roadmap
|
||||
# REA roadmap
|
||||
|
||||
## Tool direction
|
||||
|
||||
REA provides inspection, capture, and comparison tools that return evidence
|
||||
directly. Agents choose commands, paths, browser actions, scripts, and local
|
||||
fixture servers, then compose experiments from those results. CLI and MCP must
|
||||
expose equivalent behavior and preserve observations, inferences, and unknowns.
|
||||
|
||||
[#555](https://github.com/morluto/rea/pull/555) removed REA permission grants,
|
||||
scope ceilings, elicitation, and repeated approval fields.
|
||||
[#572](https://github.com/morluto/rea/pull/572) removed the replay engines,
|
||||
Node characterization prepare/execute flow, and plan-only managed runtime
|
||||
correlation tool. Those systems are retired roadmap items. Existing evidence
|
||||
authority labels remain readable as provenance; they do not imply a current
|
||||
replay executor.
|
||||
|
||||
Local operations use the current user's OS permissions. Input and protocol
|
||||
validation, provider prerequisites, target identity, cancellation, and owned
|
||||
resource cleanup remain part of each tool's contract. Setup still discloses
|
||||
installation changes and requires approval before writing or installing.
|
||||
|
||||
## Remaining evidence and provider work
|
||||
|
||||
The [platform roadmap](https://github.com/morluto/rea/issues/32),
|
||||
[process and Hopper fidelity tracker](https://github.com/morluto/rea/issues/48),
|
||||
and [browser tracker](https://github.com/morluto/rea/issues/39) track remaining
|
||||
capabilities and proof. Their scope follows direct tools and agent-composed
|
||||
experiments; custom replay languages and grant systems are not completion gates.
|
||||
|
||||
[Inline Evidence composition](https://github.com/morluto/rea/pull/585) now returns
|
||||
the complete record accepted by comparison tools. Managed inspection uses an
|
||||
explicit PE/CLI target independently of the native provider selection. Comparisons
|
||||
preserve unknowns when observation is incomplete, and PTY diagnostics retain the
|
||||
actual loader or startup failure. Browser cleanup reports incomplete teardown
|
||||
alongside the primary failure; transient documents invalidate only the affected
|
||||
frame's WebMCP registrations. Real-provider and platform claims still require
|
||||
their corresponding verification lanes.
|
||||
|
||||
## Shipped behavior
|
||||
|
||||
REA setup currently configures the capabilities that exist today: its bundled
|
||||
skill, detected agent integrations, optional Hopper provider, and validated
|
||||
bring-your-own Ghidra paths. It detects
|
||||
Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, and Devin. The
|
||||
first six have documented local MCP configuration boundaries and can be updated
|
||||
additively; Devin is reported but left unchanged.
|
||||
REA setup lets you select agent integration and optional Hopper installation.
|
||||
It installs the bundled workflow, configures detected agents, and can save
|
||||
verified paths for an existing Ghidra installation. It configures Claude Code,
|
||||
Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity,
|
||||
GitHub Copilot CLI, Command Code, and VS Code using each client's configuration format.
|
||||
|
||||
The Ghidra foundation supports Linux x64 with exact Ghidra 12.1.2 and a 64-bit
|
||||
full JDK 21. An experimental Windows x64 P0 admits approved native x86-64 PE
|
||||
applications through the same 19-operation Java bridge. Doctor validates those coordinates; approved Linux/macOS setup propagates them
|
||||
to MCP registrations without installing or modifying either dependency. REA's
|
||||
packaged Java bridge then proves an isolated read-only headless import,
|
||||
post-analysis handshake, complete cleanup, ten admitted read-only inventory
|
||||
operations, and nine function-analysis operations. Real source-owned x86-64
|
||||
debug/stripped ELF, AArch64 ELF, PE, and Mach-O fixtures cover program,
|
||||
procedure, string, symbol, external/thunk, memory, resolution, search,
|
||||
decompilation, assembly, calls, typed references, xrefs, and CFG semantics.
|
||||
Hosted Windows CI covers build, package, target admission, transport, and
|
||||
lifecycle seams; a controlled self-hosted workflow covers the real Ghidra P0
|
||||
claim.
|
||||
Ghidra analysis supports Linux x64 and macOS x64/arm64 with Ghidra 12.1.x and the
|
||||
64-bit full JDK that installation declares. Current 12.1 releases require JDK 21
|
||||
or newer and set no maximum; the bridge is verified with Ghidra 12.1.4 and JDK 21.
|
||||
macOS also requires the matching native decompiler. The
|
||||
adapter exposes thirteen inventory operations and twelve function-analysis
|
||||
operations, for 25 read-only operations total. Linux and macOS additionally support
|
||||
atomic function-name and entry-comment edits with refreshed analysis; metadata
|
||||
is discarded on close and executable bytes stay unchanged. Approved setup saves verified
|
||||
installation paths in agent configurations without installing or changing
|
||||
Ghidra or Java.
|
||||
|
||||
Real-provider verification uses host-native debug and stripped fixtures plus
|
||||
a native type-layout object. A separate lane covers AArch64 ELF, PE, and
|
||||
Mach-O cross-target fixtures. See [testing](testing.md) for the prerequisites
|
||||
and exact commands.
|
||||
|
||||
Experimental Windows x64 P0 now supplies bundled Job Object ownership,
|
||||
protected runtime DACLs, and handle-based admission for native x86-64 PE
|
||||
applications on local NTFS. Real ordinary-user CLI/MCP verification covers
|
||||
all 25 read-only operations and cleanup. The [Windows P0 guide](windows-ghidra-p0.md)
|
||||
describes this boundary and unverified broader coverage.
|
||||
|
||||
## Ghidra maintenance boundary
|
||||
|
||||
@@ -31,16 +75,16 @@ not expected to match; unresolved targetless flow remains unknown. Automatic
|
||||
Ghidra acquisition, if ever added, remains a separately planned and approved
|
||||
related-tool change; setup must never install Java.
|
||||
|
||||
Before Windows advances beyond P0, add current-user-only DACL creation and
|
||||
readback, handle-based reparse-point-safe path authority, a DACL-protected IPC
|
||||
backend, and Job Object assignment before provider execution. Process capture,
|
||||
controlled replay, Hopper, and broad filesystem-sensitive workflows remain
|
||||
separate Windows projects rather than implied parity.
|
||||
Maintain Windows P0 with independent DACL readback, handle-based admission,
|
||||
private authenticated IPC, and Job Object assignment before provider execution.
|
||||
Process capture, Hopper, and broad filesystem-sensitive workflows remain
|
||||
separate Windows projects; P0 does not imply their parity.
|
||||
|
||||
## Capability-selective setup
|
||||
|
||||
After REA ships at least two real optional toolchains, setup can evolve into a
|
||||
capability-oriented wizard. It may ask whether the operator wants to investigate
|
||||
Setup already lets users select agent integration and Hopper installation.
|
||||
Once REA supports installing additional analysis tools, it can expand these
|
||||
choices to match the work a user wants to do. It may ask whether the operator wants to investigate
|
||||
native binaries, websites, mobile applications, firmware, or runtime protocols,
|
||||
then propose only the tools needed for the selected work.
|
||||
|
||||
|
||||
+569
@@ -0,0 +1,569 @@
|
||||
# Testing REA
|
||||
|
||||
Prefer evidence in this order: full end-to-end workflows with real production
|
||||
providers, integration tests across data/API boundaries, then golden regressions
|
||||
from real captured inputs. Keep focused module tests for distinct failure or
|
||||
semantic cases that these workflows cannot reliably reproduce. A test's path
|
||||
or suite name does not establish its behavioral depth.
|
||||
|
||||
Tests are grouped into Vitest projects so ownership, allowed dependencies and
|
||||
runtime cost are visible from their paths. Before pruning a test, identify the
|
||||
replacement scenario and its assertions; passing success journeys do not
|
||||
replace malformed input, cancellation, permission or cleanup coverage.
|
||||
|
||||
## Behavioral depths
|
||||
|
||||
| Depth | Location | What it proves |
|
||||
| ------------ | ----------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| Module | `src/**/*.test.ts` | One domain, service, or adapter through its narrow public surface |
|
||||
| Composition | `tests/composition/**` | Provider-neutral session and registry wiring; filesystem access is limited to fixture materialization |
|
||||
| Boundary | `tests/boundary/**` | Exactly one production filesystem, process, network, browser, CLI, or provider boundary |
|
||||
| MCP boundary | `tests/boundary/mcp/**` | MCP transport and tool-contract boundaries with isolated sessions and explicit cleanup |
|
||||
| Acceptance | `tests/acceptance/**` | A compiled CLI or MCP journey; injected providers still make it integration |
|
||||
| Conformance | `tests/conformance/**` | Shared provider contracts, parameterized by declared capabilities and explicit opt-outs |
|
||||
| Evaluation | `tests/evaluation/**` | Deterministic evaluator parsing, scoring, and report generation |
|
||||
|
||||
Colocated domain tests may import only their owning domain and inward
|
||||
dependencies. They do not construct sessions or servers and do not start
|
||||
subprocesses or network listeners. Colocated application tests exercise one
|
||||
service through explicit ports and recording adapters, never a CLI or MCP
|
||||
entrypoint. Composition tests may assemble provider-neutral sessions and
|
||||
registries but do not cross production filesystem, process, socket, or browser boundaries.
|
||||
Boundary tests cross one production boundary. Only acceptance tests assemble
|
||||
the complete runtime or invoke the compiled product surface.
|
||||
|
||||
Binary session, registry, lazy-client and snapshot module cases live under
|
||||
`src/application/binary/`. Their composition cases stay in
|
||||
`tests/composition/analysis-sessions/`; snapshot persistence and actual SDK calls
|
||||
stay in the filesystem and MCP boundary lanes. The shared injected session fixture
|
||||
serves several capabilities and remains under `tests/fixtures/`.
|
||||
|
||||
Focused immutable builders and recording ports shared by one test family live
|
||||
beside their production owner as `src/**/*.fixture.ts`. They are typechecked
|
||||
with the suite and excluded from package builds; broader runtime and provider
|
||||
fixtures remain under `tests/fixtures/**`.
|
||||
|
||||
`tests/process-global/**` is reserved for cases with a demonstrated dependency
|
||||
on process-global state. Those tests use isolated forks so environment and
|
||||
exit-status changes cannot leak between files. Serialize a case only when it
|
||||
demonstrably shares an external resource that cannot be isolated. Reusable,
|
||||
test-scoped fixtures live under `tests/support/**`; immutable source artifacts
|
||||
remain under `tests/fixtures/**`.
|
||||
The process-global Vitest configuration contract rejects new direct temporary-root
|
||||
creation outside the workspace seam and its narrowly documented boundary/package
|
||||
exceptions.
|
||||
|
||||
Real Hopper, Ghidra, IDA, browser, package, and managed-code claims belong to their
|
||||
explicit `npm run verify:*` lanes. The reconstruction-readiness lane also
|
||||
checks deterministic rerun, tamper, and stale-input handling; those checks do
|
||||
not execute extracted JavaScript modules. When application runtime behavior is
|
||||
needed, exercise the actual target through browser, Electron, or process
|
||||
capture. Real model trials are manual; Vitest covers deterministic evaluator
|
||||
logic.
|
||||
|
||||
`verify:managed` runs the portable PE byte-fixture conformance entrypoint under
|
||||
`scripts/verify/managed/`, with its byte builder under
|
||||
`scripts/fixtures/managed/`. It checks static classification, members,
|
||||
reconstruction, native-boundary relationships and application graphs without
|
||||
executing fixture PE files. Operator-local manifests and actual ILSpy oracles
|
||||
remain optional, separately reported checks; the real Ghidra NativeAOT lane has
|
||||
its own toolchain prerequisites. See [the managed guide](managed-code-analysis.md)
|
||||
for those configurations. Generated completion-ledger checks use the same owning
|
||||
entrypoint and include its verifier/fixture files in their cache inputs.
|
||||
|
||||
## End-to-end, integration and golden evidence
|
||||
|
||||
Full E2E tests invoke the production command dispatcher and real providers,
|
||||
without fake launchers, runners or responses. `verify:keyed-archive` writes an
|
||||
actual Foundation binary archive, runs the CLI and a separate stdio MCP
|
||||
subprocess, checks parity and pagination, rejects an escaping path and checks
|
||||
an XML graph golden. `verify:asset-catalog` compiles source-owned colors with
|
||||
`actool`, invokes real `assetutil`, then checks CLI/MCP results, exact catalog
|
||||
digest, every raw metadata field, pagination and malformed input rejection.
|
||||
Neither artifact workflow requires Hopper or launches it. Both run in macOS CI.
|
||||
`verify:macos-bundle` needs only macOS with Command Line Tools. It compiles a
|
||||
source-owned app with `clang`: a versioned framework, XPC services, an app
|
||||
extension, a login item, a privileged helper, launchd plists, and a helper tool,
|
||||
signed ad hoc. It packs the app as a directory, a `ditto` ZIP, and an APFS DMG,
|
||||
then checks that `inspect-artifact` plus `project-apple-application-graph`
|
||||
report the same bundle anatomy for all three through the CLI, with stdio MCP
|
||||
parity. It also checks that the DMG is detached afterwards. It runs in macOS CI.
|
||||
|
||||
Apple artifact verifiers live in `scripts/verify/apple/`, with the macOS bundle
|
||||
builder under `scripts/fixtures/apple/` and NIB byte fixtures beside the decoder
|
||||
in `src/artifacts/apple/`. The npm entrypoints are unchanged. Format-specific
|
||||
Swift/XIB/asset sources and goldens retain their locations; real Apple workflows
|
||||
resolve them from the verifier file URL and run in the macOS CI lane.
|
||||
|
||||
Portable native semantics and their tests live in `src/domain/native/`; shared
|
||||
analyst workflows and service-lane tests live in `src/application/native/`.
|
||||
Named native contracts live in `src/contracts/native/`. Provider protocol and
|
||||
host UI tests retain their adapter/boundary lanes, and real verifier command
|
||||
names remain unchanged.
|
||||
The existing Apple CI job also runs the host Swift-demangling CLI/MCP regression
|
||||
suites, including option-like symbols, carriage returns and multiline rejection.
|
||||
|
||||
MCP SDK transport tests with recording providers remain integration tests.
|
||||
They are useful for schema drift and failure projection but do not prove that
|
||||
Hopper, Ghidra or another substituted engine works. `verify:package` proves
|
||||
packaging/install behavior and fake-provider integration; use the corresponding
|
||||
real-provider lanes for engine claims. Real Apple dispatch and Interface
|
||||
Builder verifiers currently prove format integration through production readers.
|
||||
|
||||
Golden tests use immutable captured text inputs with producer/source provenance
|
||||
under `tests/fixtures/golden/`. Expected results are reviewed for the semantic
|
||||
claim; capture commands do not automatically approve new expected outputs.
|
||||
Do not call handcrafted utility output or synthetic binary builders real-data
|
||||
goldens. Keep unsupported binary layouts and malformed boundaries as targeted
|
||||
regressions until a real fixture establishes equivalent coverage.
|
||||
|
||||
`verify:browser` also captures a source-owned noise canvas as a real PNG above
|
||||
8 MiB through the CLI and stdio MCP, with complete byte/digest parity and real PNG
|
||||
decoding. Its SDK client explicitly permits the larger inline JSON response;
|
||||
this lane does not establish large image-comparison request transport coverage.
|
||||
|
||||
`verify:browser:network` is a focused real-browser lane for transaction identity,
|
||||
selected request/response bytes, binary and compressed responses, duplicate
|
||||
headers, credential and declared-secret redaction, redirects, streaming cutoff,
|
||||
CLI/MCP parity, and owned-profile cleanup. Set `REA_BROWSER_EXECUTABLE` to an
|
||||
installed Chrome-family browser. An optional script argument selects an already
|
||||
installed package's `scripts/rea.mjs` entry point for packaged-artifact checks.
|
||||
The complete `verify:browser` lane includes these same checks.
|
||||
|
||||
After building, `verify:browser:dom` checks empty and HTML-whitespace form
|
||||
destinations against a native Chrome DOM-property oracle through CLI and stdio
|
||||
MCP. It requires `REA_BROWSER_EXECUTABLE` and accepts an optional installed REA
|
||||
entrypoint. The full browser lane includes the same public-adapter assertions
|
||||
before other fixtures navigate the selected page.
|
||||
|
||||
`verify:browser:scripts` checks active script capture → exact-byte export →
|
||||
existing static JavaScript analysis through CLI and stdio MCP, including
|
||||
manifest readback, competing query variants, and resolved relative imports.
|
||||
It uses an installed browser and accepts an optional installed REA entrypoint.
|
||||
The complete `verify:browser` lane also exercises passive script export through
|
||||
both public adapters. See [website script export](website-script-export.md).
|
||||
|
||||
`verify:browser:modules` compares CLI and stdio MCP traces against an independent
|
||||
real Chromium module-loading fixture: import-map scopes, package prefixes,
|
||||
null/backtracking rejection, query/fragment identities, repeated module instances,
|
||||
lazy and computed unknowns, exact source/map identities and no implicit refetch.
|
||||
Set `REA_BROWSER_EXECUTABLE`; the optional script argument selects an installed
|
||||
package entrypoint. The complete `verify:browser` lane and existing conditional
|
||||
Chrome CI include this verifier. See [module relationships](website-module-trace.md).
|
||||
|
||||
## Real-toolchain verification lanes
|
||||
|
||||
Each real-toolchain command must require only the host tools needed to prove
|
||||
its stated claim. Use a host-native fixture for host/provider acceptance, and
|
||||
place optional cross-target formats or platform-specific runners in separate
|
||||
commands. Check prerequisites before starting expensive work and name the
|
||||
missing command, target, and lane in any failure message. A lane must not imply
|
||||
that a host or target is covered when it was skipped.
|
||||
|
||||
Provider admission accepts Ghidra 12.1.x and the JDK range declared by that
|
||||
installation (`application.java.min` through `application.java.max`). Current
|
||||
12.1 releases require JDK 21 or newer and set no maximum. The lanes below still
|
||||
prove behavior on the verified Ghidra 12.1.4 and JDK 21 build.
|
||||
|
||||
| Ghidra lane | Supported runner/target | Additional local tools |
|
||||
| ------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| `npm run verify:ghidra` | Linux x64 ELF or macOS x64/arm64 Mach-O | Host C compiler, Ghidra 12.1.4, and full JDK 21 |
|
||||
| `npm run verify:ghidra:switch` | Linux x64 ELF; GCC/Clang optimized and stripped switch fixtures | GCC, Clang, GNU nm/objdump/strip, Ghidra 12.1.4, and full JDK 21 |
|
||||
| `npm run verify:ghidra:aarch64-jump-table` | Any supported Ghidra host; AArch64 ELF; byte/halfword tables; host ARM64 Mach-O | Clang with AArch64 target support, Ghidra 12.1.4, and full JDK 21 |
|
||||
| `npm run verify:ghidra:cross-format` | Any supported Ghidra host; also analyzes AArch64 ELF, x86-64 PE, and x86-64 Mach-O | `clang`, LLD, and `lld-link` in addition to host-lane prerequisites |
|
||||
| `npm run verify:ghidra:windows` | Controlled Windows x64 with native x86-64 PE | Ghidra 12.1.4, full JDK 21, and the matching native artifact |
|
||||
|
||||
Windows native conformance runs with `npm run verify:windows-native` and does
|
||||
not require Ghidra or Java. An optional independently compiled Windows fixture
|
||||
adds in-place reparse, breakaway, environment, and token observations.
|
||||
`npm run verify:ghidra:windows:package` additionally packs and installs REA into
|
||||
an isolated prefix and runs ordinary-user CLI/MCP operations against their
|
||||
canonical schemas. Run the controlled fixture generator before that lane, or
|
||||
supply an installed package root and an explicit fixture as arguments.
|
||||
The host-native Ghidra lane also verifies native value tracing through the
|
||||
production CLI and a separate stdio MCP process. It compares complete dependency
|
||||
graphs, validates Evidence and upstream/workflow profiles, checks capability
|
||||
discovery, and closes the MCP session. No provider or transport is mocked.
|
||||
|
||||
The Linux switch lane checks dense, sparse-with-holes, shared-body, nonzero,
|
||||
negative, and nonexact JSON integer labels plus a comparison-only control.
|
||||
Independent source labels, ELF file bytes, table slots, and bounds branches
|
||||
define expected case/default destinations. Production CLI and MCP must agree;
|
||||
debug labels must retain their signed values. For stripped negative fixtures,
|
||||
the independently checked 32-bit dispatch permits equivalent unsigned labels
|
||||
only alongside the reported low-confidence `undefined4` parameter; original
|
||||
source signedness remains unknown and the ABI residual must remain visible.
|
||||
unsafe labels remain unresolved and every recovered destination is retained.
|
||||
The compiler oracle also injects malformed records to check that its assertions
|
||||
reject missing/default-confused labels, wrong destinations, and numeric guesses.
|
||||
It also invokes the actual bridge methods on detached Ghidra model objects to
|
||||
check ambiguous dispatches, signed literals, precision bounds, shared targets,
|
||||
and conflicting labels. This reflection fixture depends on the pinned Ghidra
|
||||
model and does not claim a compiler produced those synthetic graph shapes.
|
||||
Pass `--entrypoint /path/to/installed/rea-agents/scripts/rea.mjs` directly to
|
||||
`scripts/verify-real-ghidra-switch.mjs` to verify an installed package through
|
||||
the same compiler oracles and CLI/MCP checks.
|
||||
|
||||
The cross-format Ghidra lane also analyzes an optimized AArch64 ELF switch
|
||||
fixture. It checks the recovered case values against the source cases and
|
||||
requires unresolved table bounds or case mappings to remain visible as
|
||||
residual unknowns.
|
||||
|
||||
`npm run verify:inspector` requires the supported Node.js runtime and installed
|
||||
REA dependencies. CI runs it on Linux and macOS x64/arm64 and Windows x64. It starts owned loopback
|
||||
Node Inspector fixtures and verifies discovery and passive observation through
|
||||
the CLI and stdio MCP, including special filenames, unresolved discovery
|
||||
locations, and independently resolved loaded scripts. Double-quote filenames
|
||||
are tested on POSIX only because Windows does not support them.
|
||||
|
||||
Inspector adapters live in `src/inspector/`, with loopback fixtures under
|
||||
`tests/fixtures/inspector/` and forked producer boundaries under
|
||||
`tests/boundary/inspector/`. The real lane is owned by
|
||||
`scripts/verify/inspector/runtime-observation.mjs` and
|
||||
`tests/conformance/inspector/`. Browser and Inspector deliberately share the
|
||||
existing browser CDP transport/value and file-location helpers. Node fixture
|
||||
success proves this Inspector workflow on the tested host; it does not prove
|
||||
Electron GUI behavior or another engine.
|
||||
|
||||
## Android APK analysis
|
||||
|
||||
`npm run verify:android` requires an existing Java 17+ and an explicit
|
||||
`REA_JADX_MCP_JAR` for jadx-headless-mcp 0.7.1. Set `REA_ANDROID_TEST_APK` to the
|
||||
fixed public ApiDemos v6.0.18 fixture. Obtain both with the explicit
|
||||
`npm run fixtures:android` command; files are SHA-256 verified and
|
||||
kept under ignored `_reference/`. No Gradle build, Android SDK, emulator or
|
||||
application execution is required. The lane compares real CLI/MCP package,
|
||||
class search, class inventory, method decompilation and incoming references.
|
||||
See [Android analysis](android-analysis.md) for boundaries and resource budgets.
|
||||
Authenticated IPA and macOS application inventory projection is documented in
|
||||
[Apple application analysis](apple-application-analysis.md).
|
||||
|
||||
The lane and owned-process cancellation helper live in `scripts/verify/android/`.
|
||||
The explicit downloader and fixed manifest live in `scripts/fixtures/android/`;
|
||||
their default remains repository-root `_reference/apk-integration/`. Producer
|
||||
fixtures live in `tests/fixtures/android/`, boundary cases in
|
||||
`tests/boundary/android/`, and inventory projection composition cases in
|
||||
`tests/composition/android/`. Real engine success and synthetic protocol success
|
||||
are separate proof levels.
|
||||
|
||||
Synthetic producer regressions run independently:
|
||||
|
||||
```sh
|
||||
npm run test:focused -- tests/boundary/android/jadxIntegration.test.ts tests/boundary/mcp/androidAnalysisMcp.test.ts
|
||||
```
|
||||
|
||||
## Optional NativeAOT Ghidra analysis
|
||||
|
||||
This lane is separate from the default native lane. `build:fixtures:nativeaot`
|
||||
requires an existing .NET SDK 8.0.416, the platform NativeAOT compiler/linker and
|
||||
runtime pack 8.0.22. It builds benign sources into ignored `_reference/` and
|
||||
records independent symbol/directory/SHA oracles; it never executes the target.
|
||||
Linux also builds stripped, ordinary-native and small malformed/ambiguous/layout
|
||||
negative inputs. The optional Windows fixture workflow builds a PE on a Windows
|
||||
runner; analyzing that PE on Linux does not verify a Windows Ghidra host.
|
||||
|
||||
Build the clean pinned upstream adapter with `build:ghidra:nativeaot`, then set
|
||||
`REA_GHIDRA_NATIVEAOT_JAR`. Run `verify:ghidra:nativeaot -- symbols`, `-- stripped`,
|
||||
`-- ordinary`, `-- unsupported`, `-- malformed`, `-- ambiguous`, and
|
||||
`-- loader-failure`, and `-- default-native` separately. The loader-failure mode source-builds
|
||||
a deliberately failing JDK 21 initializer and checks the actual loader cause and
|
||||
cleanup. The default-native mode verifies ordinary analysis with the optional
|
||||
extension disabled.
|
||||
The real MCP lane checks source identity, inline format discovery, metadata
|
||||
relationships/slots against independent compiler symbols, frozen strings,
|
||||
pseudocode and owned cleanup. Set `REA_NATIVEAOT_PROOF_CLI=1` for one equivalent
|
||||
CLI type inspection; this costs an additional full import. Select an unpacked
|
||||
installed package with `REA_NATIVEAOT_PROOF_PACKAGE_ROOT`, a fixture directory
|
||||
with `REA_NATIVEAOT_PROOF_FIXTURE_ROOT`, and optional evidence capture directory
|
||||
with `REA_NATIVEAOT_PROOF_CAPTURE_DIR` (absolute paths).
|
||||
|
||||
Keep builds/imports sequential on small hosts; scope `GHIDRA_HEADLESS_MAXMEM`
|
||||
(e.g. `768M`) to this command and use CPU affinity if needed. REA does not install
|
||||
or upgrade Java, Ghidra, .NET or native toolchains. See
|
||||
[the supported layout and provenance](ghidra-nativeaot.md).
|
||||
|
||||
## IDA MCP adapter
|
||||
|
||||
`npm run verify:ida -- --target /absolute/path/to/program --procedure main`
|
||||
uses the existing `REA_IDA_MCP_CONFIG` registration. It installs no engine,
|
||||
Python package, or compiler. The target must already be open in the GUI for
|
||||
the legacy attached profile; the database-supervisor headless profile opens
|
||||
a digest-verified private copy. A caller-supplied fixture keeps prerequisites
|
||||
limited to the selected engine and host. `tests/conformance/ida/inventory.c`
|
||||
provides an optional small native fixture source with an exported
|
||||
`rea_fixture_add` function.
|
||||
|
||||
The lane invokes the production CLI dispatcher and connects the pinned MCP
|
||||
client SDK to the production REA server. It verifies function Evidence and
|
||||
CLI/MCP parity, inventory/search, pseudocode, instructions, xrefs, malformed
|
||||
input, original-input preservation, and lifecycle cleanup. For headless
|
||||
analysis it confirms that the owned database IDs disappear from upstream
|
||||
discovery and private workspaces are removed. For attached analysis it confirms
|
||||
the existing GUI target remains reachable with the same input identity.
|
||||
`--package-root` selects an installed/extracted REA artifact. `--report` writes
|
||||
private local observations with mode `0600`; the console summary contains no
|
||||
target paths or upstream output.
|
||||
|
||||
Adapter and composition tests cover producer parsing, pagination, canonical
|
||||
entries, external callees, target switches, cancellation draining, snapshot
|
||||
replay exclusion, ownership failures, and incomplete cleanup. They do not
|
||||
establish real IDA operation. The initial real workflows cover legacy upstream
|
||||
1.4.0 on a Windows GUI and the modern supervisor at upstream commit
|
||||
`c133c3853faa111a9b00ee615c013b720d0c4acd` with Windows x64 IDA 9.3.
|
||||
Linux/macOS headless, modern attached GUI tools, other engine versions and
|
||||
architectures remain unverified; see the [provider guide](ida-provider.md).
|
||||
|
||||
## DOS Ghidra analysis
|
||||
|
||||
`npm run verify:ghidra:dos` requires the supported Ghidra and JDK installation
|
||||
on Linux x64 or macOS x64/arm64. It generates a source-owned MZ fixture without
|
||||
a DOS emulator or compiler, then checks real 16-bit decoding, segment
|
||||
relocation, near/far calls, decompilation, disjoint function body ranges,
|
||||
stable CLI/MCP observations, unchanged source bytes, and owned process/project
|
||||
cleanup. Raw p-code address-space selector tokens are reported separately from
|
||||
the stable observation comparison. Linux x64 is verified; macOS DOS remains
|
||||
unverified. This lane is separate from host-native and optional cross-format
|
||||
verification. See [DOS analysis](ghidra-dos.md).
|
||||
|
||||
`npm run verify:ghidra:com` uses a generated headerless fixture with no compiler,
|
||||
DOS emulator or game data. It exercises explicit admission, BinaryLoader entry
|
||||
preparation, measured register context, whole-file byte readback, source offsets,
|
||||
unmapped PSP/partial reads, actual decompilation, CLI/MCP parity and owned cleanup.
|
||||
It has the same Ghidra/JDK prerequisites as the MZ lane. Neither lane claims DOS
|
||||
runtime or PC-98 device execution.
|
||||
|
||||
## Apple Interface Builder archives
|
||||
|
||||
`npm run verify:interface-builder` compiles the source-owned AppKit XIB into a
|
||||
real `.nib` with Xcode `ibtool`, wraps it in a temporary app bundle, and checks
|
||||
the decoded view hierarchy, outlet, action, evidence coverage, and truncation
|
||||
status. Storyboard compilation additionally requires an installed iOS platform.
|
||||
|
||||
Keep the provider-specific acceptance path independent from optional
|
||||
cross-compilers. Cross-format failures belong to the cross-format lane and must
|
||||
not make native host acceptance unavailable.
|
||||
|
||||
## Native platform baseline in CI
|
||||
|
||||
CI exercises the pinned Node.js runtime on native hosted runners:
|
||||
|
||||
| Host | Runner | Baseline checks |
|
||||
| --------------------- | ------------------ | ---------------------------------------------------------------------------------------- |
|
||||
| Linux x64 | `ubuntu-latest` | Installed package and real Node Inspector CLI/MCP |
|
||||
| Linux arm64 (aarch64) | `ubuntu-24.04-arm` | Installed package and real Node Inspector CLI/MCP |
|
||||
| macOS 15 arm64 | `macos-15` | Installed package and real Node Inspector CLI/MCP |
|
||||
| macOS 15 x64 | `macos-15-intel` | Installed package and real Node Inspector CLI/MCP |
|
||||
| Windows x64 | `windows-latest` | Curated capabilities, native controls, installed package and real Node Inspector CLI/MCP |
|
||||
|
||||
Package and Inspector matrices assert the actual Node platform/architecture
|
||||
before verification and record those values with the Node version. Each matrix
|
||||
runs at most two jobs concurrently with explicit timeouts and Node heap/thread
|
||||
limits. Package checks cover installation, CLI/MCP discovery, target-free
|
||||
analysis, configuration backups/recovery, Evidence and owned lifecycle; Inspector
|
||||
checks execute source-owned loopback targets and special filename cases.
|
||||
|
||||
macOS uses one OS version with one native baseline job per architecture. Each job
|
||||
runs package and Inspector checks after a single dependency installation. The
|
||||
separate Inspector matrix covers Linux and Windows; Apple artifact checks retain
|
||||
their own macOS 15 arm64 job for Xcode-dependent workflows.
|
||||
|
||||
These native baseline checks complement the Linux source-test shards and the
|
||||
separate Apple-artifact and real-provider lanes. Actual Hopper, Ghidra, IDA,
|
||||
browser and managed-tool claims require their corresponding verification lanes.
|
||||
Runner labels follow the [GitHub hosted-runner reference](https://docs.github.com/en/actions/reference/runners/github-hosted-runners).
|
||||
|
||||
## Developer commands
|
||||
|
||||
Use source feedback while editing, explicit boundary checks for the changed
|
||||
behavior, and complete CI evidence before merging.
|
||||
|
||||
| Command | Scope |
|
||||
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `npm run test:local` | Dirty source tests via the import graph, without build; explicit source paths run regardless of Git status |
|
||||
| `npm run test:focused -- PATH...` | Exact existing test files; compiled boundaries build first, and unmatched paths fail |
|
||||
| `npm run test:changed` | Source tests affected since the merge base with `origin/main`, including committed and dirty changes |
|
||||
| `npm run test:fast` | All domain, service, adapter, composition, conformance, and evaluation tests without build |
|
||||
| `npm run test:boundary` | Boundary, MCP boundary, process boundary, and process-global projects |
|
||||
| `npm run test:mcp` | MCP boundary project |
|
||||
| `npm run test:acceptance` | Complete compiled CLI and MCP acceptance workflows |
|
||||
| `npm run test:watch` | Dirty source tests in watch mode, without build |
|
||||
| `npm run test:watch:all` | Changed tests from every project; builds at startup, so rebuild after production edits before relying on compiled tests |
|
||||
| `npm run check:changed` | Cached typecheck/lint and branch-related source feedback |
|
||||
| `npm run check:pr` | Opt-in complete local deterministic gate and generated-file checks |
|
||||
| `npm run docs:check` | Committed generated-document freshness |
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
npm run test:local -- src/config.test.ts
|
||||
npm run test:focused -- tests/acceptance/applications/runtime.test.ts
|
||||
npm run test:changed -- --base origin/main
|
||||
npm run test:changed -- --dry-run
|
||||
```
|
||||
|
||||
`test:focused` accepts exact repository-relative test file paths. Source-only
|
||||
paths do not build; boundary, acceptance, process-global, or unfamiliar `tests/`
|
||||
paths build conservatively. Tests use Vitest concurrency and isolated
|
||||
workspaces; commands do not hold a broad test lock. Build and documentation
|
||||
writers retain checkout-local locks for their shared output files. Explicit
|
||||
selections do not use `--changed` or permit zero-test success.
|
||||
The dry-run option reports the chosen merge base, scope and build prerequisite
|
||||
without executing tests or building. A missing Git base reports how to fetch
|
||||
it or select another revision.
|
||||
|
||||
Changed selection can miss runtime registration, generated data, shell
|
||||
entrypoints, bridges, or other relationships absent from the import graph.
|
||||
An empty changed selection means no tests were selected, not verified
|
||||
correctness. Select relevant boundary files and real-provider lanes explicitly.
|
||||
Source projects also contain large capacity regressions; `test:fast` promises
|
||||
no compiled-runtime prerequisite, not a fixed time budget.
|
||||
|
||||
Routine iterations and rebases need focused regressions and relevant checks.
|
||||
Before handing off a PR, run `npm run check` and generated-document checks when
|
||||
applicable; CI owns the full suite and coverage. Use the full local gate for
|
||||
broad changes or diagnosing CI, rather than after every edit. Package/install
|
||||
changes additionally need package verification; provider changes need actual
|
||||
provider evidence.
|
||||
|
||||
Local full-suite Vitest runs use up to two workers and schedule projects one
|
||||
at a time. Process, acceptance and process-global projects serialize their
|
||||
files to prevent competing lifecycle observations. CI retains its two-worker
|
||||
budget.
|
||||
The pure domain/contracts and recording-port service projects share one worker
|
||||
module context because their tests own no mutable runtime resources. MCP
|
||||
boundary files also share the immutable server module graph while creating and
|
||||
closing independent in-memory sessions. Adapter, composition, acceptance,
|
||||
process-global, and other boundary projects retain per-file isolation.
|
||||
`npm test`, `npm run docs:check`, and `npm run docs:generate` share
|
||||
repository-local locks and fail fast when the same class of command is already
|
||||
running. The `npm test` build is inside that lock. `check:pr` runs its test task
|
||||
before starting generated-document validation.
|
||||
|
||||
Vitest and Node persistent compile caches are deliberately not enabled by
|
||||
default. To evaluate repeated local runs, opt in for both cold and warm
|
||||
measurements with an isolated cache:
|
||||
|
||||
```bash
|
||||
NODE_COMPILE_CACHE=.cache/node-compile npm test
|
||||
```
|
||||
|
||||
Do not report the warm result as a cold-suite improvement, and do not enable
|
||||
the cache in coverage or benchmark CI without first showing that its
|
||||
instrumentation remains equivalent.
|
||||
|
||||
## Coverage and timing
|
||||
|
||||
CI owns coverage. The aggregate floors are 65% statements, 60% branches, 60%
|
||||
functions, and 68% lines. `src/domain/**` must reach 80% statements, 75%
|
||||
branches, 75% functions, and 80% lines. `src/contracts/**` must reach 85%
|
||||
statements, 80% branches, 80% functions, and 85% lines. Thresholds are
|
||||
glob-specific rather than per-file and are never updated automatically.
|
||||
Coverage does not replace named boundary, acceptance, or real-provider scenario
|
||||
matrices.
|
||||
|
||||
CI runs four native Vitest shards without retries. Each shard emits a blob
|
||||
report; the merge job produces aggregate coverage plus JUnit and JSON timing
|
||||
reports, uploads them together, and writes the slowest files to the workflow
|
||||
summary. Static checks, documentation, build, package verification, and
|
||||
real-system lanes remain separate jobs so one kind of evidence cannot stand in
|
||||
for another.
|
||||
|
||||
The PR acceptance target is a median `npm run check:pr` wall time below three
|
||||
minutes across three warm-build runs on the benchmark host. Keep Vitest caches
|
||||
cold unless separately identified. A PR that touches packaging or real-system
|
||||
behavior requires the applicable `verify:*` lanes; packaging and installation
|
||||
changes also require `npm run verify:package`. The full-gate benchmark measures
|
||||
that explicit lane, not the routine iteration requirement.
|
||||
|
||||
## Apple native metadata and UI
|
||||
|
||||
`npm run verify:apple-dispatch` compiles the Objective-C fixture (classes,
|
||||
protocols, a property and an `NSString` category) and the Swift
|
||||
conformance/vtable fixture.
|
||||
|
||||
- Each fixture is linked with legacy `LC_DYLD_INFO` binds and with chained
|
||||
fixups; on Apple silicon the ObjC fixture is also built as arm64e, which uses
|
||||
authenticated pointers.
|
||||
- The lane inspects the bytes and repeats after stripping local symbols.
|
||||
- It requires the bound `NSObject` superclass, the external category, and the
|
||||
matching `pointer_fixups` coverage. It requires macOS and the host Xcode toolchain; targets are not
|
||||
executed. `npm run verify:native-ui` launches exactly one source-owned fixture
|
||||
window and requires successful selected-window capture and selected actions.
|
||||
An OS permission denial fails the positive lane. `npm run verify:native-ui:permissions`
|
||||
allows a host-permission-boundary-only result and explicitly reports
|
||||
`positive_e2e: false`; it must not be reported as capture/action proof.
|
||||
Both commands reject a changed executable digest and clean up the fixture
|
||||
process and helper. These lanes require an interactive macOS desktop. See [native investigation](native-investigation.md)
|
||||
for the exact ABI, authority, graph and observation boundaries.
|
||||
|
||||
### Firmware adapters
|
||||
|
||||
`npm run fixtures:firmware` uses existing Python 3 and a host C compiler to make
|
||||
an ignored gzip/USTAR firmware fixture and independent offset/hash oracle.
|
||||
`npm run verify:firmware` requires caller-supplied Binwalk 3.1.0, Unblob 26.6.4
|
||||
and util-linux prlimit on Linux. The provider also accepts other 3.1.x and
|
||||
26.6.x builds and reports them as unverified; this lane proves the audited
|
||||
releases. It verifies CLI/MCP parity, selected ranges,
|
||||
unknown chunks, depth limits and extracted child digests. The optional
|
||||
`REA_FIRMWARE_VERIFY_EXT4=1` lane requires existing mke2fs/debugfs; the separate
|
||||
`REA_FIRMWARE_VERIFY_GHIDRA=1` lane checks a selected host ELF through real Ghidra.
|
||||
Neither optional toolchain is a base-lane prerequisite. See
|
||||
[firmware analysis](firmware-analysis.md) for limits and unverified formats.
|
||||
|
||||
The real entrypoint is `scripts/verify/firmware/analysis.mjs`; the fixture runner
|
||||
and unchanged Python producer belong together in `scripts/fixtures/firmware/`.
|
||||
Producer fixtures and boundaries remain in `tests/fixtures/firmware/` and
|
||||
`tests/boundary/firmware/`. Source-fixture generation proves the runner and
|
||||
independent oracle; actual Binwalk/Unblob CLI/MCP proof requires selected tools.
|
||||
|
||||
### JavaScript source recovery
|
||||
|
||||
Build once with `npm run build:cached`, then run `npm run verify:javascript:recovery`.
|
||||
This focused lane requires Linux x64, util-linux `prlimit`,
|
||||
`REA_WAKARU_COMMAND` pointing to the official Wakaru 1.13.0 Linux x64 binary,
|
||||
and `REA_JAVASCRIPT_FIXTURE_TOOLS` pointing to an isolated npm prefix containing
|
||||
esbuild 0.25.10 and webpack 5.101.3. No global installation is required.
|
||||
The lane compiles source-owned fixtures, exercises CLI and stdio MCP, verifies
|
||||
published bytes and provenance, feeds recovered modules into existing analysis,
|
||||
and compares a finite set of known fixture results. It does not establish
|
||||
arbitrary recovered-application equivalence. CI installs these prerequisites only
|
||||
in `.github/workflows/real-javascript-recovery.yml`; the existing `real-browser`
|
||||
lane uses real Chrome for browser capture and website workflows.
|
||||
|
||||
### Captured website source-map lane
|
||||
|
||||
`npm run verify:browser:source-maps` checks actual Chromium capture/export and
|
||||
source-map point tracing through CLI and stdio MCP. It requires absolute
|
||||
`REA_BROWSER_EXECUTABLE` and `REA_WEB_SOURCE_MAP_COMPILER` pointing to esbuild
|
||||
0.25.10's `lib/main.js` in a caller-owned isolated installation. Preflight reports
|
||||
missing prerequisites for this lane. The compiler is used only to generate the
|
||||
source-owned fixture. No Hopper, Ghidra or application dependency installation
|
||||
is required.
|
||||
|
||||
```bash
|
||||
REA_BROWSER_EXECUTABLE=/absolute/path/to/chromium \
|
||||
REA_WEB_SOURCE_MAP_COMPILER=/absolute/path/to/fixture-tools/node_modules/esbuild/lib/main.js \
|
||||
npm run verify:browser:source-maps
|
||||
```
|
||||
|
||||
`scripts/verify-browser-source-maps.mjs /absolute/path/to/installed/rea.mjs`
|
||||
checks an installed package after building the verifier dependencies. The separate
|
||||
conditional `real-web-source-map` CI job supplies Chrome and an isolated pinned
|
||||
fixture compiler; static/unit checks do not acquire a browser. See
|
||||
[the source location guide](web-source-location.md) for the verified decoder profile.
|
||||
|
||||
### Website runtime attribution lane
|
||||
|
||||
`npm run verify:browser:runtime` uses caller-supplied
|
||||
`REA_BROWSER_EXECUTABLE` and an owned synthetic site/profile. It exercises public
|
||||
CLI and stdio MCP for precise execution and native listener source locations,
|
||||
including actual armed progress, Unicode/CRLF digests, repeated source URLs with
|
||||
distinct script IDs, zero branches and function-only unknowns on repeated
|
||||
coverage, request initiators and an externally owned page that remains open.
|
||||
|
||||
An optional entrypoint argument to `scripts/verify-browser-runtime.mjs` runs the
|
||||
same checks through an isolated installed package. The conditional
|
||||
`real-web-runtime` CI job runs only for relevant changes and needs no fixture
|
||||
compiler. Ordinary unit/static gates acquire no browser. See
|
||||
[website runtime attribution](web-runtime.md) for effects, resource bounds and
|
||||
coverage limits.
|
||||
@@ -0,0 +1,95 @@
|
||||
# MCP tool design
|
||||
|
||||
Design tools around analyst tasks. A provider API is an implementation detail;
|
||||
expose it directly when it gives agents a useful, reusable capability that
|
||||
cannot be expressed through an existing contract. Do not impose caller-facing
|
||||
limits merely to make work fit an assumed agent budget. Keep real input-format,
|
||||
protocol, authority, and resource-safety constraints, and report their effects.
|
||||
|
||||
## Choose the tool shape
|
||||
|
||||
| Shape | Use it for | Contract should return |
|
||||
| --------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `inspect` | Facts about one explicit target, address, object, or resource | Relevant fields, source locations, and facet-level availability |
|
||||
| `search` / `list` | Finding candidate targets or entities | Stable ordering and useful result context; expose pagination when the result format or caller's query needs it |
|
||||
| `trace` | Relationships across code, metadata, UI resources, or observations | Typed edges, supporting evidence, and unresolved paths; identify any genuine traversal boundary |
|
||||
| `compare` | Two explicitly identified artifacts, versions, or evidence sets | Paired identity, comparable coverage, and deltas with evidence |
|
||||
| `workflow` | A distinct analyst outcome that benefits from composition inside REA | A useful inline result, contributing evidence, and partial/unavailable facets |
|
||||
| `observe` / `capture` | A question that requires runtime behavior | Required authority, launch/attach behavior, real operational constraints, lifecycle, and cleanup status |
|
||||
|
||||
These are task shapes, not required prefixes. Name a tool for the action and
|
||||
object agents reason about; keep the name distinct from nearby alternatives.
|
||||
|
||||
## Prefer primitives; compose workflows
|
||||
|
||||
Start with a primitive when one call can report a reusable fact about one
|
||||
identified object or relationship, such as an instruction decode, a type
|
||||
layout, a reference, a dispatch target, or a resource graph. The same primitive
|
||||
should be useful across different applications or analyst questions. A
|
||||
format-specific decoder can still be a primitive when it describes a reusable
|
||||
format rather than one application's product behavior.
|
||||
|
||||
Add a workflow when repeated analysis shows that callers need the same
|
||||
multi-source result and REA can join the evidence without hiding important
|
||||
choices or uncertainty. A useful check is whether the workflow remains
|
||||
meaningful for different applications that share the relevant evidence types.
|
||||
If its purpose depends on one application's business rules or product concepts,
|
||||
keep that interpretation outside the general tool contract and expose the
|
||||
underlying evidence through reusable primitives.
|
||||
|
||||
## Decide whether to add a tool
|
||||
|
||||
1. State the user intent and the smallest result that answers it.
|
||||
2. Inspect the canonical tool contracts, current provider capabilities, and
|
||||
representative CLI/MCP traces. Record the nearest competing tool.
|
||||
3. Extend an existing contract when the intent and result are the same. Add a
|
||||
batch or composed workflow when it materially improves a demonstrated task;
|
||||
do not force agents through extra calls or collapse distinct tasks into one
|
||||
tool. Add a tool when it answers a distinct analyst question or has a
|
||||
materially different authority or result contract.
|
||||
4. Keep public tool names and result semantics provider-neutral. Put engine-
|
||||
specific parsing and protocol handling in adapters. Advertise exact support
|
||||
per provider; do not create parallel tools just because engines differ.
|
||||
5. Keep prompts optional and concise. They may point out useful tools, but must
|
||||
not prescribe a call sequence when the task can be answered directly.
|
||||
|
||||
Avoid opaque mode flags and mega-tools that combine unrelated discovery,
|
||||
execution, and mutation. Prefer a useful direct tool call over a tool sequence
|
||||
or model-authored loop when one call can answer the question.
|
||||
|
||||
## Define the contract
|
||||
|
||||
- Use strict object inputs with explicit required fields and enums. Add numeric
|
||||
bounds only when the format, protocol, authority, or a measured resource
|
||||
constraint requires them.
|
||||
- Return complete results by default. Make real pagination, truncation, partial
|
||||
failure, cancellation, and unavailable states explicit. Missing evidence is
|
||||
unknown, not empty or false.
|
||||
- Return task-oriented results inline with artifact identity, provider/version,
|
||||
addresses or resource paths, Evidence references, confidence, and relevant
|
||||
limitations. Do not require agents to fetch a resource or dereference an
|
||||
opaque Evidence link to understand a tool result.
|
||||
- Separate observed facts from derived and inferred edges. Cite the evidence
|
||||
supporting every important relationship; preserve unresolved edges.
|
||||
- Declare read-only, mutation, process, filesystem, network, and UI effects
|
||||
truthfully in the contract and enforce the target and lifecycle named in the
|
||||
request.
|
||||
- Keep provider-specific types out of provider-neutral domain and application
|
||||
layers. Normalize supported provider results without implying equal coverage.
|
||||
|
||||
## Implement and evaluate
|
||||
|
||||
- Put shared user workflows in the application layer; keep MCP translation in
|
||||
the server adapter and CLI behavior aligned with the same workflow.
|
||||
- Update the canonical contract inventory, output schemas, examples, generated
|
||||
catalog, and relevant docs together.
|
||||
- Verify valid, malformed, boundary, unavailable, cancellation, and partial
|
||||
results at the owning boundaries. Use real-provider checks for claims that
|
||||
depend on external analysis engines.
|
||||
- Evaluate representative broad and direct task prompts on the intended MCP
|
||||
host when tool selection is the question. Record the host, model-facing tool
|
||||
catalog, selected tool, arguments, errors, retries, and task outcome.
|
||||
Schema validity or word-overlap heuristics do not prove discoverability.
|
||||
|
||||
See [mcp-contracts.md](mcp-contracts.md) for shipped runtime behavior and
|
||||
[testing.md](testing.md) for test boundaries and verification lanes.
|
||||
@@ -7,17 +7,16 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
6,src/domain/errors.ts:AnalysisProtocolError,Application protocol fails,Raw constructor message,Can expose protocol internals,"Analysis returned an unreadable result. Retry once; if it continues, run `rea doctor`.",implemented,focused assertions passed; full `npm run check` passed
|
||||
7,src/domain/errors.ts:HopperProcessError,Analysis subprocess stops,"Hopper bridge stopped unexpectedly with code {exitCode}",Exposes provider bridge and process code,"Analysis could not start or stopped unexpectedly. Run `rea doctor`, then try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
8,src/domain/errors.ts:HopperStartError,Analysis subprocess cannot start,Hopper application bridge could not be started,Exposes provider and bridge implementation,"Analysis could not start or stopped unexpectedly. Run `rea doctor`, then try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
9,src/domain/errors.ts:BinaryTargetError,Target cannot open,"Cannot open artifact: {reason}",May expose raw reason and lacks checks,"REA could not open that app or binary. Check that the path exists, is readable, and points to a supported file.",implemented,focused assertions passed; full `npm run check` passed
|
||||
9,src/domain/errors.ts:BinaryTargetError,Target cannot open,"Cannot open artifact: {reason}",May expose raw reason and lacks checks,"Preserve the target admission reason; directories name analyze_javascript_application and rea analyze as available alternatives.",implemented,focused assertions passed; full `npm run check` passed
|
||||
10,src/domain/errors.ts:ConfigurationError,Environment configuration invalid,Raw constructor message,May expose provider-specific variables and lacks recovery,"REA configuration is invalid. Run `rea doctor` and fix the reported setting.",implemented,focused assertions passed; full `npm run check` passed
|
||||
11,src/domain/errors.ts:ArtifactOperationError,Artifact inventory or extraction fails,"Artifact {operation} failed: {reason} at {logicalPath} (declared_sha256=...)",Exposes paths hashes and internal reason,"Reason-specific plain message with safe recovery step.",implemented,focused assertions passed; full `npm run check` passed
|
||||
12,src/domain/errors.ts:EvidenceFileError,Evidence file operation fails,"Evidence bundle {operation} failed: {reason}",Internal reason token gives weak recovery,"Reason-specific plain message with safe recovery step.",implemented,focused assertions passed; full `npm run check` passed
|
||||
13,src/domain/errors.ts:EvidenceIntegrityError,Evidence validation fails,Raw constructor message,May expose validation internals and lacks recovery,"Evidence is invalid or has changed. Recreate or re-import it, then try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
14,src/domain/errors.ts:UnknownRegistryError,Evidence-state mutation fails,"Residual unknown registry mutation failed: {reason}",Exposes internal subsystem and state token,"Evidence state changed before the update completed. Refresh the current state and try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
14,src/domain/errors.ts:UnknownRegistryError,Evidence-state mutation fails,"Residual unknown registry mutation failed: {reason}",Exposes internal subsystem and state token,"Missing IDs identify the session and retry; concurrent revision conflicts refresh current state.",implemented,focused assertions passed; full `npm run check` passed
|
||||
15,src/domain/errors.ts:ProcessCaptureError,Process capture fails,Raw constructor message,May expose process and cleanup internals,"Process capture could not complete. Check command access and limits, then try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
16,src/domain/errors.ts:AnalysisTimeoutError,Analysis deadline expires,"Analysis operation timed out after {timeoutMs}ms: {operation}",Exposes internal deadline and operation; weak recovery,"Analysis took too long. Try a smaller request, then run `rea doctor` if it continues.",implemented,focused assertions passed; full `npm run check` passed
|
||||
17,src/domain/errors.ts:HopperTimeoutError,Provider deadline expires,"Hopper did not respond within {timeoutMs}ms",Exposes provider and internal deadline,"Analysis took too long. Try a smaller request, then run `rea doctor` if it continues.",implemented,focused assertions passed; full `npm run check` passed
|
||||
18,src/domain/errors.ts:ProviderSelectionError,Deep-provider selection fails,"Analysis provider selection failed ({reason}) for {requestedProviderId}; candidates: {candidateIds}",Retains non-secret local selection context that needs a safe structured projection,Reason-specific guidance with stable candidate IDs and actionable rejection diagnostics,implemented,focused registry/CLI/MCP assertions passed; full `npm run check` passed
|
||||
19,src/domain/errors.ts:EvidenceLimitError,Evidence ledger limit reached,"Evidence ledger {limit} limit exceeded ({maximum})",Exposes internal storage model,"Evidence is too large for this session. Reduce the evidence set and try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
20,src/domain/errors.ts:AnalysisInputError,Parsed analysis input invalid,"Invalid analysis input for {operation}",Exposes operation identifier and lacks correction,"Analysis input is invalid. Check the arguments and try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
21,src/domain/errors.ts:AnalysisCancelledError,Caller cancels analysis,"Analysis operation was cancelled: {operation}",Exposes operation identifier,"Analysis was cancelled. Start it again when ready.",implemented,focused assertions passed; full `npm run check` passed
|
||||
22,src/domain/errors.ts:HopperCancelledError,Caller cancels provider request,Hopper operation was cancelled,Exposes provider name,"Analysis was cancelled. Start it again when ready.",implemented,focused assertions passed; full `npm run check` passed
|
||||
@@ -25,7 +24,7 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
24,src/server/toolResult.ts:successResult,Successful value violates public output schema,AnalysisOutputError: Analysis output does not match the tool contract,Exposes internal error tag and contract implementation,"Analysis returned an unreadable result. Retry once; if it continues, run `rea doctor`.",implemented,focused assertions passed; full `npm run check` passed
|
||||
24.5,src/server/registerOfficialTools.ts:capability unknown,Approved unknown tracking records an unavailable analysis,"TOOL_NAME is unavailable: PROVIDER_REASON",Exposes an internal tool identifier and provider diagnostic in persistent user-visible evidence,"The requested analysis is unavailable for the current target.",implemented,verified by focused MCP integration test
|
||||
24.6,src/application/ReferenceSourceImportEntries.ts:failed entry,A partial reference import contains an unreadable entry,Low-level reader diagnostic stored in entry limitations,Can persist filesystem or reader implementation details,"Code- and entry-specific safe guidance with a retry, limit, exclusion, or platform recovery step.",implemented,verified by exhaustive focused projection test
|
||||
24.7,src/server/registerSessionTools.ts:process residual,Capture records an approved residual unknown,SCOPE: RAW_REASON,Persists adapter diagnostic and implementation wording,"Was SCOPE behavior fully observed during capture?",implemented,verified by focused session MCP test
|
||||
24.7,src/server/registerSessionTools.ts:process residual,Capture records a residual unknown linked to capture Evidence,SCOPE: RAW_REASON,Persists adapter diagnostic and implementation wording,"Was SCOPE behavior fully observed during capture?",implemented,verified by focused session MCP test
|
||||
24.8,src/server/registerInvestigationTools.ts:changed behavior unknown,Changed-behavior workflow remains incomplete,"Changed behavior result EVIDENCE_ID remains STATUS",Persists internal evidence identifier,"Did both versions behave the same under a complete controlled replay?",implemented,verified by focused investigation MCP test
|
||||
24.9,src/server/registerInvestigationTools.ts:call path unknown,Call-path workflow remains incomplete,Call path START_ADDRESS → GOAL_ADDRESS is STATUS,Persists binary addresses and internal status wording,Can the requested call path be established from complete analysis?,implemented,verified by focused unresolved call-path MCP test
|
||||
24.91,src/server/registerInvestigationTools.ts:static runtime unknown,Static/runtime correlation remains incomplete,Static/runtime correlation result EVIDENCE_ID is STATUS,Persists internal evidence identifier,Does runtime behavior match the available static analysis?,implemented,verified by focused unresolved static/runtime MCP test
|
||||
@@ -33,10 +32,10 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
24.93,src/server/registerFunctionComparisonTool.ts:probe rationale,Function comparison is incomplete,Capture complete dossiers with equal providers and analysis limits.,Exposes provider architecture and gives ambiguous equality guidance,"Capture complete dossiers for both functions under the same target context and analysis limits.",implemented,verified by focused function-comparison MCP test
|
||||
24.95,incur:CLI argument validation,Caller omits or mistypes a command argument,VALIDATION_ERROR with raw Zod Details and fieldErrors including code/path/expected/received,Exposes validator representation and buries the useful correction,"REA could not read the command arguments. Check the named argument and its expected type, then try again.",implemented,verified by sanitizer unit tests and real dispatcher missing-argument test
|
||||
25,src/application/ReferenceSourceImport.ts:cancelled,Reference-source import is cancelled,Reference source import cancelled,No recovery timing guidance,"Reference source import was cancelled. Start it again when ready.",implemented,focused assertions passed
|
||||
26,src/application/ReferenceSourceImportPolicy.ts:invalid-limits,Configured import limits are not positive integers,Import limits must be positive integers,Does not tell user where recovery lies,"Reference source limits are invalid. Use positive integer limits, then try again.",implemented,focused assertions passed
|
||||
26,src/application/ReferenceSourceImportPolicy.ts:invalid-limits,Configured import limits are not positive integers,Import limits must be positive integers,Fixed importer ceilings prevented full source imports,Removed fixed byte, entry, depth, and path limits.,removed,verified by greater-than-10000 entry and greater-than-16-MiB regressions
|
||||
27,src/application/ReferenceSourceImportPolicy.ts:invalid-root,Source path cannot resolve or is not a directory,Reference source root could not be resolved / Reference source root is not a directory,Multiple low-level copies and no complete path check,"Reference source directory could not be opened. Check that the path exists, is readable, and points to a directory.",implemented,focused assertions passed
|
||||
28,src/application/ReferenceSourceImportPolicy.ts:policy,No approved root exists or requested root is outside policy,No approved reference source roots / Reference source root is outside approved roots,Does not identify safe configuration recovery,"Reference source directory is not approved. Add its directory to `REA_REFERENCE_ROOTS_JSON`, restart REA, then try again.",implemented,focused assertions passed
|
||||
29,src/application/ReferenceSourceImport.ts:io,Reader fails during bounded traversal,Raw reader error message,May expose filesystem details and inconsistent recovery,"Reference source files could not be read. Check directory permissions and try again.",implemented,focused assertions passed
|
||||
28,src/application/ReferenceSourceImportPolicy.ts:policy,No approved root exists or requested root is outside policy,No approved reference source roots / Reference source root is outside approved roots,Approval friction duplicates direct caller path selection,Removed the configured root allowlist and permission gate; caller-selected directories are validated and traversed directly.,removed,verified by source search and focused import tests
|
||||
29,src/application/ReferenceSourceImport.ts:io,Reader fails during traversal,Raw reader error message,May expose filesystem details and inconsistent recovery,"Reference source files could not be read. Check directory permissions and try again.",implemented,focused assertions passed
|
||||
30,src/application/ReferenceSourceImport.ts:parse,Historical graph validation fails,Raw thrown graph validation message,Can expose internal graph invariants,"Reference source could not be indexed. Check that the source tree is readable, then try again.",implemented,focused assertions passed
|
||||
31,src/application/ReferenceSourceImportTypes.ts:limit,Import reports terminal limit failure,No reachable constructor found,Declared state may be unreachable; false verification risk,Remove unreachable top-level limit variant; per-entry limit guidance remains caller-visible.,removed,"verified by typecheck, exhaustive projection test, and source search"
|
||||
32,src/application/Setup.ts:configureDetectedClients(backup),Agent configuration backup fails,"{clientName} configuration backup verification failed; no successful configuration was reported.",Exposes machine client key and obscures no-write guarantee,"Agent configuration could not be backed up, so no change was made. Check file permissions, then rerun setup.",implemented,focused assertions passed
|
||||
@@ -56,8 +55,8 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
45.5,src/application/Uninstall.ts:removeClient restore,Configuration update fails and restoring the original also fails,"Configuration could not be updated or restored. Restore its `.rea.backup` manually, then rerun uninstall.","High risk; persistent configuration may be damaged, but retained backup is authoritative",Keep new recovery copy,implemented,verified by injected restoration failure test
|
||||
46,src/application/Uninstall.ts:removeManagedPath symlink,Managed cache/state/skill path is a symbolic link,Refused to follow symlink {path}.,Exposes local path and does not explain manual safety check,"REA did not remove this item because its managed path is a symbolic link. Verify the link target before removing it manually.",implemented,focused assertion passed
|
||||
47,src/application/Uninstall.ts:removeManagedPath failure,Managed item removal fails,{path} could not be removed.,Exposes local path and gives no recovery,"This item could not be removed. Check file permissions, then rerun uninstall.",implemented,verified by injected managed-path removal failure test
|
||||
48,src/application/Doctor.ts:node,Runtime version is outside supported ranges,Install Node.js 22.19+ or 24.11+.,Low risk; exact dependency recovery,Keep current copy,unchanged,verified by doctor unit test
|
||||
49,src/application/Doctor.ts:host,Host OS/distribution is unsupported,"REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux.",Low risk; clearly states supported choices,Keep current copy,unchanged,verified by doctor unit tests
|
||||
48,src/application/DoctorDiagnostics.ts:node,Runtime version is outside supported ranges,"Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact dependency recovery,Keep current copy,unchanged,verified by doctor unit test
|
||||
49,src/application/Doctor.ts:host,Host OS/distribution is unsupported,"REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, 64-bit Arch Linux, and CachyOS.",Low risk; clearly states supported choices,Keep current copy,unchanged,verified by doctor unit tests
|
||||
50,src/application/Doctor.ts:analysis engine,Optional deep-analysis executable cannot be found,"Run rea setup to install Hopper, or set HOPPER_LAUNCHER_PATH.",Low risk in setup-specific diagnostic; exact recovery paths,Keep current copy,unchanged,verified by doctor unit tests
|
||||
51,src/application/Doctor.ts:target,Optional target path fails shared validation,Supply a readable local app or program path.,Low risk; tells caller valid target class,Keep current copy,unchanged,verified by doctor unit test
|
||||
52,src/application/Setup.ts:approval,Setup plan lacks approval,"Review the setup plan, then rerun interactively or with --yes.",Low risk; exact authorization recovery,Keep current copy,unchanged,verified by setup unit tests
|
||||
@@ -66,15 +65,14 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
55,src/application/Setup.ts:optional engine,Setup completes without optional deep-analysis engine,Hopper is optional for non-Hopper providers. Rerun with --yes --install-hopper for deep native analysis.,Exposes provider terminology but this is an explanatory setup status rather than an error; recovery command is exact,Keep current copy,unchanged,verified by setup unit test
|
||||
56,src/application/Setup.ts:doctor fallback,Post-setup diagnostics remain unhealthy,Run rea doctor and apply each reported remediation.,Low risk; delegates to structured per-check recovery,Keep current copy,unchanged,verified by exact setup unit test
|
||||
57,src/application/Setup.ts:unsupported platform,Platform is neither macOS nor Linux,REA supports Hopper on macOS and selected 64-bit Linux distributions.,Low risk; states support boundary,Keep current copy,unchanged,verified by setup unit test
|
||||
58,src/application/Setup.ts:unsupported Node,Runtime version cannot run setup,Install Node.js 22.19+ or 24.11+ and rerun setup.,Low risk; exact dependency and retry,Keep current copy,unchanged,verified by setup host tests
|
||||
58,src/application/SetupHost.ts:unsupported Node,Runtime version cannot run setup,"Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ and rerun setup.",Low risk; exact dependency and retry,Keep current copy,unchanged,verified by setup host tests
|
||||
59,src/application/Setup.ts:unsupported macOS,macOS version is missing or older than 12,Upgrade to macOS 12 or newer.,Low risk; exact support floor,Keep current copy,unchanged,verified by setup host tests
|
||||
60,src/application/Setup.ts:unsupported Linux,Linux distribution is outside supported set,"REA supports Hopper on Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux.",Low risk; exact supported alternatives,Keep current copy,unchanged,verified by setup unit test
|
||||
60,src/application/Setup.ts:unsupported Linux,Linux distribution is outside supported set,"REA supports Hopper on Ubuntu 24.04+, Fedora 41+, 64-bit Arch Linux, and CachyOS.",Low risk; exact supported alternatives,Keep current copy,unchanged,verified by setup unit test
|
||||
61,src/application/ProcessCli.ts:readJson read,Process scenario or evidence file cannot be read,Raw filesystem exception through CLI framework,Can expose local paths/codes and no recovery,"Process input file could not be read. Check that the path exists and is readable.",implemented,verified by focused CLI test
|
||||
62,src/application/ProcessCli.ts:readJson size,Process scenario or evidence file exceeds 64 MiB,Process JSON input exceeds byte limit,Does not state limit or recovery,"Process input file is too large. Reduce it below 64 MiB, then try again.",implemented,verified by focused sparse-file CLI test
|
||||
63,src/application/ProcessCli.ts:readJson parse,Process scenario or evidence file is malformed JSON,Raw JSON parser exception through CLI framework,Can expose parser implementation and lacks recovery,"Process input file is not valid JSON. Repair the file, then try again.",implemented,verified by focused CLI test
|
||||
64,src/application/ProcessCli.ts:capture scenario parse,Scenario fails Process Capture schema,Raw Zod validation exception through CLI framework,Exposes schema internals and field paths without stable recovery,"Process scenario is invalid. Check its required fields and limits, then try again.",implemented,verified by focused CLI test
|
||||
65,src/application/ProcessCli.ts:legacy evidence,Comparison receives Process Capture v3 Evidence,"Process Capture v3 is unsupported. Re-run the scenario with this REA version using capture_process_scenario to produce Process Capture v4; captures cannot be upgraded because v4 requires new manifest and settlement evidence.",Long but accurately explains why recapture is required and gives exact tool recovery,Keep current copy,unchanged,verified by focused CLI test
|
||||
66,src/application/ProcessCli.ts:wrong evidence,Comparison receives valid Evidence from another workflow,Expected REA Process Capture v4 Evidence,Uses internal schema name and does not explain recovery,"Capture evidence is not from the current process-capture workflow. Create new capture evidence, then try again.",implemented,verified by focused CLI test
|
||||
66,src/application/ProcessCli.ts:wrong evidence,Comparison receives valid Evidence from another workflow,Expected REA process capture Evidence,Uses internal schema name and does not explain recovery,"Capture evidence is not from the current process-capture workflow. Create new capture evidence, then try again.",implemented,verified by focused CLI test
|
||||
67,src/application/ProcessCli.ts:unexpected,Unexpected parser/comparison/canonicalization failure escapes expected classifications,Raw thrown exception through CLI framework,Can expose stack or internal invariant,"Process command could not complete. Check the input files and run `rea doctor`, then try again.",implemented,verified by direct unexpected-cause projection test
|
||||
68,src/server/registerEvidenceTools.ts:object guard,Validated provider-tool input is unexpectedly not an object,Provider tool input was not an object,Exposes provider architecture and implies caller can fix impossible state,Remove the impossible duplicate guard and parse through the declared JSON-object schema.,removed,verified by typecheck and MCP integration tests
|
||||
69,src/server/registerEnhancedTools.ts:object guard,Validated enhanced-tool input is unexpectedly not an object,Validated enhanced input was not a JSON object,Exposes validation/JSON implementation detail,Remove the impossible duplicate guard and parse through the declared JSON-object schema.,removed,verified by typecheck and enhanced MCP integration tests
|
||||
@@ -85,11 +83,11 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
74,install.sh:unknown option,Installer receives an unsupported flag,unknown option with caller-provided flag,Low risk; identifies offending input,Keep current copy,unchanged,verified by shell branch inspection
|
||||
75,install.sh:platform,Installer runs outside macOS or supported Linux,REA supports macOS and selected 64-bit Linux distributions.,Low risk; states support boundary,Keep current copy,unchanged,verified by shell branch inspection
|
||||
76,install.sh:curl missing,curl is absent,curl is required.,No recovery instruction,"curl is required. Install curl, then rerun this installer.",implemented,verified by exact missing-curl installer test
|
||||
77,install.sh:node missing,node is absent,Node.js 22.19+ or 24.11+ is required; install it from nodejs.org and retry.,Low risk; exact versions and recovery,Keep current copy,unchanged,verified by shell branch inspection
|
||||
77,install.sh:node missing,node is absent,"Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ is required; install it from nodejs.org and retry.",Low risk; exact versions and recovery,Keep current copy,unchanged,verified by shell branch inspection
|
||||
78,install.sh:npm missing,npm is absent,npm is required; install it with Node.js and retry.,Low risk; exact recovery,Keep current copy,unchanged,verified by shell branch inspection
|
||||
79,install.sh:node unreadable,node exists but does not report a parseable version,could not determine the active Node.js version.,No cause-local recovery,"The active Node.js version could not be read. Check that node works and is on PATH, then retry.",implemented,verified by exact unreadable-Node-version installer test
|
||||
80,install.sh:Node 22 floor,Node 22 minor version is below 19,Node.js VERSION is unsupported; use Node.js 22.19+ or 24.11+.,Low risk; exact detected version and supported alternatives,Keep current copy,unchanged,verified by exact installer test using Node 20 and branch inspection
|
||||
81,install.sh:Node 24 floor,Node 24 minor version is below 11,Node.js VERSION is unsupported; use Node.js 24.11 or newer.,Low risk; exact detected version and floor,Keep current copy,unchanged,verified by shell branch inspection
|
||||
80,install.sh:Node 22 floor,Node 22 minor version is below 19,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact detected version and supported alternatives,Keep current copy,unchanged,verified by exact installer test using Node 20 and branch inspection
|
||||
81,install.sh:Node 24 floor,Node 24 minor version is below 11,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact detected version and floor,Keep current copy,unchanged,verified by shell branch inspection
|
||||
82,install.sh:version syntax,Explicit version is not exact semantic version,version must be an exact semantic version.,Low risk; exact correction,Keep current copy,unchanged,verified by shell branch inspection
|
||||
83,install.sh:release lookup,Latest GitHub release request fails,could not resolve the latest GitHub release.,No fallback despite supporting pinned versions,"The latest REA release could not be resolved. Check network access or pass --version VERSION, then retry.",implemented,verified by exact release-lookup installer test
|
||||
84,install.sh:release response,Latest release response is not valid expected JSON,Node JSON parser stack followed by the safe release-response message,Leaks a parser stack and buries recovery guidance,The release response was invalid. Retry later or pass --version VERSION.,implemented,verified by exact malformed-release test with parser stderr suppressed
|
||||
|
||||
|
@@ -1,109 +1,101 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"ledger_id": "ecl_a56a75b91375c9c9501d1545fee9977e74f57dbd1bf413a370f9cd1ed26987bd",
|
||||
"ledger_id": "ecl_132e857a8a93f0b366cd72f9cfa1c338ffd4100460a7834ef69abff936c84472",
|
||||
"records": [
|
||||
{
|
||||
"claim_id": "managed.application-graph-projection",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_4d6821cdd212a77507dfbdf995fc8b086cd63b3f1d39dee4a2e12f62a6f0609d"
|
||||
"ev_b061a5a559acb3748cb9784c637f6693f74faeb564ba2b11d6fc32619e757459"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.decompiler-reconstruction-import",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_cd9015f39a60251bee1b01baf5189de7427e5810f130aed39770f17ae69cee44"
|
||||
"ev_13f3401a3aaddc3f6d087ad160fa928374e142cdb9861254359b46865b8c992e"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.dotnet-framework-x86-pinvoke",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_af02c0218a01138bd1f0f5ca052f95d348dfacb540040ab72d6ddc9889e3a7e4"
|
||||
"ev_6f026216826ca06e080b77ff608f02634fa945fbd2e151b4cd261c1444f8bbb1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.ilspy-reconstruction-oracle",
|
||||
"status": "unsupported",
|
||||
"evidence_ids": [
|
||||
"ev_5df12074aeb26d67bc954fd9230076bd0c3ce9969600e19c59ba02090a9988f2"
|
||||
"ev_65a94c4de03f9c6ebdaff3f381cca377821f349d3ca6dfad47a31cc586fae939"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.malformed-metadata",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_5d6aadf199ae090e0ddebc10a51aab8a410fa778a6ae8454fedfb10d240cdac2"
|
||||
"ev_deae4259ea4395623176de40d06c2abcbc2b3a77f0bfb068d76e568d801f45a8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.managed-native-verification",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_9f1862276fe8869b86105eb91154603922eeb95fec33a10c354b2d6c9c3fe077"
|
||||
"ev_0edcbbcaa1191a4e5324a437f739957b77f9ac45e7897724337f2d6d06b3f9d1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.modern-dotnet-anycpu",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_c9aef74724cf555bcedce8a9dbf46327b2f92b95b03eb363f966885d429e6882"
|
||||
"ev_d1af50e8dc04cd589385769efd8758e82095425f6e9c1cc4bf2f530c6b434c93"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.mvid-and-token-drift",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_55808bf3ee5ec9d87cee751462077a6bce88701c4fcf66822ba1d7d33706706b"
|
||||
"ev_5493f40bc875535488d789a490361abcc1f76bfbdf52536d09ff127442a8e854"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.not-managed",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_a8166506ca91a4595b825f355836bb4becf35852c8547c55408c09698f4953a6"
|
||||
"ev_03ce0cc0183ee0710f8120e837565129559ab3bc41927df326066370c2341699"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-local-manifest",
|
||||
"status": "unsupported",
|
||||
"evidence_ids": [
|
||||
"ev_03d1a151446834fbee7c093e82121b714a0cc1e6167ad84f47a79ffb9e2602ce"
|
||||
"ev_9d3e50da1d372c55f46649e69eae702cbe8f0ba6a81d1e46afc3e1a284c81757"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-manifest-self-test",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_74d7f9c75a4476196664542d790e2532435f2a1c54349b481967e51e2ccc9453"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.runtime-correlation-admission-plan",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_5a80df0b92dc2280361e900ce590bc88ea52eba3dc8e216a0ce282f503e3cb05"
|
||||
"ev_f974f4b9d039cda841894c6173cae86427ecef48f4e7a4d6c73edf7b23af2b2a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.unicode-obfuscated-identifiers",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_3b61c79ec152c70693278a70ad2ca6528ce0a9c9ab389d3ec9e4520de79082a7"
|
||||
"ev_c4f38ce76d8359d92ee8e9442503e47b7913550442349a0df688bd274a3def67"
|
||||
]
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.x64-ready-to-run-native-body",
|
||||
"status": "pass",
|
||||
"evidence_ids": [
|
||||
"ev_81f41b1b3809c2fe1964f7abbcbd2faeb14c723938639c2e0bb16cd58746aa1a"
|
||||
"ev_3e05ef1d6a796d3d4cb3372fe8e549efec759c59bf09d8ff660059a2e448cbc0"
|
||||
]
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"total": 14,
|
||||
"pass": 12,
|
||||
"total": 13,
|
||||
"pass": 11,
|
||||
"fail": 0,
|
||||
"unsupported": 2,
|
||||
"truncated": 0,
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"manifest_id": "ecm_24026e05aeb579814a3d44d04eb8914b1d4ce301d916536bc68a745418c5d892",
|
||||
"manifest_id": "ecm_880356f1ceceb9e29b43e3e89f7a326aa83132e1e1fae0573245848ba714f404",
|
||||
"verifier": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"environment": {
|
||||
"platform": "linux",
|
||||
"architecture": "x64",
|
||||
"platform": "portable",
|
||||
"architecture": "portable",
|
||||
"runtime": "node",
|
||||
"runtime_version": "24"
|
||||
"runtime_version": "portable"
|
||||
},
|
||||
"skill_digests": [
|
||||
{
|
||||
"skill_id": "reverse-engineer-anything",
|
||||
"sha256": "9bb090837e251fc7ff1c1e621b77230f54c88650efb6520aff74d33877b023d8"
|
||||
"sha256": "97dc167b6e0d41ac69953501c96a918319fa6e4b3be0affe2fc2119294e7101d"
|
||||
}
|
||||
],
|
||||
"claims": [
|
||||
@@ -30,8 +29,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.decompiler-reconstruction-import",
|
||||
@@ -45,8 +43,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.dotnet-framework-x86-pinvoke",
|
||||
@@ -60,8 +57,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.ilspy-reconstruction-oracle",
|
||||
@@ -73,8 +69,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.malformed-metadata",
|
||||
@@ -88,8 +83,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.managed-native-verification",
|
||||
@@ -104,8 +98,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.modern-dotnet-anycpu",
|
||||
@@ -119,8 +112,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.mvid-and-token-drift",
|
||||
@@ -135,8 +127,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.not-managed",
|
||||
@@ -150,8 +141,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-local-manifest",
|
||||
@@ -163,8 +153,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.operator-manifest-self-test",
|
||||
@@ -178,23 +167,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.runtime-correlation-admission-plan",
|
||||
"scenario": {
|
||||
"id": "runtime-correlation-admission-plan",
|
||||
"version": 1
|
||||
},
|
||||
"artifact_sha256s": [
|
||||
"cead4cc99733e4e56a5fd0cec8a0813bbc24e249aa72a10eca4c47ee3b0e129d"
|
||||
],
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.unicode-obfuscated-identifiers",
|
||||
@@ -208,8 +181,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"claim_id": "managed.x64-ready-to-run-native-body",
|
||||
@@ -223,8 +195,7 @@
|
||||
"provider": {
|
||||
"id": "rea-managed-conformance",
|
||||
"version": "1"
|
||||
},
|
||||
"result_schema_version": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Website runtime source attribution
|
||||
|
||||
REA integrates native Chrome DevTools Protocol inspection and precise V8 coverage through two composable tools. Supply an existing loopback CDP endpoint and a page target from `list_browser_targets`. With omitted origins, REA selects the page's current HTTP(S) origin. The browser and page remain externally owned.
|
||||
|
||||
## Observe execution
|
||||
|
||||
```bash
|
||||
rea observe-web-execution http://127.0.0.1:9222 TARGET_ID \
|
||||
--observation-ms 10000 --json
|
||||
```
|
||||
|
||||
MCP: `observe_web_execution` with `cdp_endpoint`, `target_id`, optional `allowed_origins` and optional `observation_ms` (default 10000). The operation itself selects instrumentation; no extra approval flag is needed.
|
||||
|
||||
Starting precise coverage resets counters and prevents optimized execution until REA stops it. This changes timing/JIT behavior. REA requests accurate call counts and detailed ranges, takes one resetting sample, stops coverage, disables enabled domains and detaches its own connection. It does not navigate, click, evaluate selected application code or close the page.
|
||||
|
||||
Wait for the actual armed progress event before performing an action. MCP emits negotiated progress notifications; CLI emits `rea_progress` JSON on stderr while the Evidence result stays on stdout. Use ordinary commands, scripts or user actions to trigger behavior. CLI SIGINT/SIGTERM and MCP cancellation await instrumentation cleanup; unconfirmed cleanup is an error with the target, failed commands and previous failure.
|
||||
|
||||
Results retain:
|
||||
|
||||
- The selected target, frame and document loader identity; local armed/end timestamps and separate backend monotonic coverage timestamps.
|
||||
- Every admitted producer function/block range, exclusive end offset and execution count in UTF-16 code units, with verified bounds when exact text is available. Nested/overlapping counts are preserved and must not be summed.
|
||||
- Main-document script inventory, exact session script IDs, resource start coordinates, sourceURL/source-map declarations, producer hash and independently computed UTF-8 text digest.
|
||||
- Armed-window requests from the selected main frame and selected origins, complete reported initiator objects (including stack hierarchy/descriptions), derived callsites, exact script-ID associations and unresolved async parent IDs. Source association does not establish UI causality.
|
||||
|
||||
The coverage counter interval starts at backend acceptance before the armed receipt and ends at the resetting sample after the local timer. Counts may include execution during these command intervals; request events are restricted to the locally armed window. The result retains both backend timestamps and the distinct local clock.
|
||||
|
||||
Source metadata remains collected through receipt of the resetting sample, including scripts parsed during the final command. REA then freezes this inventory for source joins. Fatal protocol parsing failures terminate the observer even when no command is outstanding; listener inspection checks producer failures after its final document assertion.
|
||||
|
||||
A document replacement ends the window; final coverage then remains unavailable. Missing coverage is unknown: an already loaded script may be absent from a sample. A detailed request also does not guarantee block granularity for already compiled functions. If `is_block_coverage` is false, the function-only ranges are observed and branch execution remains unknown. Native Chrome verification reproduced block coverage in the first window and function-only coverage in a subsequent window, including without REA.
|
||||
|
||||
Same-document navigation such as `history.pushState()` remains valid when the frame and known loader identity stay stable and the current origin is allowed. Unknown loader identity retains a conservative URL check; newly reported or replaced loaders are rejected. Malformed producer events end an armed window immediately and trigger cleanup, independently of the selected duration.
|
||||
|
||||
Target detachment ends the window even when the browser's root connection stays open. When the closed session cannot acknowledge cleanup, the operation returns a failure with the selected target, failed commands and original producer reason; it does not claim successful cleanup.
|
||||
|
||||
Child frames, workers, service workers, WebAssembly, chronological execution order and framework-specific causal interpretation are outside this operation. Script ownership comes from frame/context metadata, never URL equality. Repeated URLs and synthetic names therefore retain separate identities.
|
||||
|
||||
Observed script metadata remains available when ownership is unknown; source text and source association remain excluded or unknown. Request/initiator resource URLs omit transport userinfo. Stack and script URLs can also be inert `sourceURL` names: REA preserves declared names and unknown representations, and only removes script URL userinfo when stable producer metadata explicitly reports `hasSourceURL: false`. This policy also applies when metadata arrives after a request. Unknown producer extension fields and source-map declarations are preserved.
|
||||
|
||||
## Inspect a node's listeners
|
||||
|
||||
```bash
|
||||
rea inspect-web-event-listeners http://127.0.0.1:9222 TARGET_ID '#submit' --json
|
||||
```
|
||||
|
||||
MCP: `inspect_web_event_listeners` with the same scope and an explicit CSS `selector`. Native DOM selection inspects the first matching node in the main document. A missing match is an explicit empty result.
|
||||
|
||||
The result contains directly registered listener types/flags, callback coordinates, source identity/text/digest and execution unknowns. REA uses native DOM/DOMDebugger operations and releases its own remote object group. It never invokes a listener or dispatches an event. Delegated ancestor listeners, descendants, closed shadow roots and framework registries are not enumerated.
|
||||
|
||||
## Bounds and verification
|
||||
|
||||
Each protocol message has a 64 MiB budget; script/context metadata and request metadata each have an 8 MiB retained budget. Complete retained source text has a 32 MiB budget. Oversized or malformed evidence returns no partial successful result. Each command and the complete inspection/source-join phase have a 20-second deadline; cleanup has a separate 5-second command deadline plus transport closure. The selected observation duration must fit the host timer's positive 32-bit millisecond range. REA does not control the externally owned browser's CPU or memory.
|
||||
|
||||
`npm run verify:browser:runtime` uses caller-supplied `REA_BROWSER_EXECUTABLE` and the native `ps` command on Linux/macOS against an owned synthetic fixture, through real public CLI and stdio MCP. It checks exact Unicode/CRLF text/digests, same-URL identities, same-document navigation, inert declared names, executed and zero-count branches, function-only unknowns on repeated observation, listener selection, initiator association, actual arming and preservation of the external page. A separate fixture page closes during an armed MCP window to verify prompt termination and inline cleanup diagnostics. The optional positional entrypoint to `scripts/verify-browser-runtime.mjs` permits the same checks against an isolated installed tarball.
|
||||
|
||||
Real workflow coverage is Linux x64 / Chrome 153 / Node 24. Other browser engines and host workflows remain unverified. The conditional `real-web-runtime` CI lane acquires Chrome only for relevant changes; ordinary unit/static checks need no browser.
|
||||
|
||||
Primary protocol references: [Profiler and Debugger](https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/master/pdl/js_protocol.pdl), [DOMDebugger](https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/master/pdl/domains/DOMDebugger.pdl).
|
||||
|
||||
Source ownership requires both the selected main frame and producer evidence for its default execution world. Extension/automation isolated worlds, worker worlds and unknown worlds cannot establish website source joins. Contradictory world metadata stays unowned. Referenced source records retain the reported execution context and script auxiliary context metadata; source text is read only for proven main-document default worlds.
|
||||
|
||||
Known script/context identities are monitored through asynchronous source completion after the sample cutoff. A changed identity excludes its retained text, frame attribution, coverage and request/source joins. Chromium's specific native CSS syntax rejection returns an input error for `selector`, including the selected target; unrelated node, command and transport failures retain their original category.
|
||||
@@ -0,0 +1,97 @@
|
||||
# Captured website source locations
|
||||
|
||||
`trace_web_source_location` maps one selected position in a script retained by
|
||||
`export_web_scripts` through one explicitly paired local source map. The shared
|
||||
CLI/MCP workflow verifies actual manifest, script and map byte identities and
|
||||
returns the original position and embedded source inline. It acquires no browser,
|
||||
fetches no URL and executes no application code.
|
||||
|
||||
```bash
|
||||
rea trace-web-source-location \
|
||||
/analysis/scripts/manifest.json 0 \
|
||||
/analysis/app.js.map 'https://example.test/maps/app.js.map?v=7#context' \
|
||||
1 25 --json
|
||||
```
|
||||
|
||||
The six positional arguments select the manifest, zero-based script index,
|
||||
absolute map path, absolute map URL context, one-based generated line and
|
||||
zero-based UTF-16 column. The MCP arguments are:
|
||||
|
||||
```json
|
||||
{
|
||||
"manifest_path": "/analysis/scripts/manifest.json",
|
||||
"script_index": 0,
|
||||
"source_map": {
|
||||
"path": "/analysis/app.js.map",
|
||||
"url": "https://example.test/maps/app.js.map?v=7#context"
|
||||
},
|
||||
"generated_position": { "line": 1, "column": 25 }
|
||||
}
|
||||
```
|
||||
|
||||
The URL supplies the context for resolving `sources` and `sourceRoot`; it is not
|
||||
an instruction to fetch. Absolute virtual or file contexts can be selected as
|
||||
well. The map/script association is caller-selected: a declaration or local map
|
||||
does not prove deployment authenticity or execution. Use
|
||||
[import tracing](website-module-trace.md) to inspect outgoing native module edges.
|
||||
|
||||
## Meaning of the evidence
|
||||
|
||||
Lookup follows the greatest generated position less than or equal to the
|
||||
selected point, across lines, and returns **every** segment at that position.
|
||||
It includes generated-only mappings. This follows
|
||||
[ECMA-426 GetOriginalPositions](https://tc39.es/ecma426/#sec-getoriginalpositions),
|
||||
with one-based lines in REA's public contract. A point result does not establish
|
||||
coverage of a source range, execution or UI causality.
|
||||
|
||||
Mapped entries preserve the raw source name/root, section path, section-local
|
||||
source index and flattened index. Duplicate URLs remain distinct, including
|
||||
entries with different embedded contents. Missing source names retain a null
|
||||
resolved URL. Original coordinates outside embedded text retain the reported
|
||||
coordinates and a null offset.
|
||||
|
||||
Embedded content hashes identify the UTF-8 encoding of the decoded
|
||||
`sourcesContent` string, including its line endings. They do not identify an
|
||||
independently captured original file. Raw map text is retained in Evidence's
|
||||
`raw_result`, including unknown extensions; these extensions are not interpreted.
|
||||
Capture context and source Evidence links accompany independently verified
|
||||
selected file identities.
|
||||
|
||||
## Adapter and resource boundaries
|
||||
|
||||
The adapter uses the locked upstream
|
||||
[`@jridgewell/trace-mapping` 0.3.31](https://github.com/jridgewell/trace-mapping)
|
||||
without modifying its code. It validates map/VLQ structure before allocation,
|
||||
normalizes nested indexed offsets before upstream flattening, checks section
|
||||
boundaries and indexes, and retains raw declaration identity. It supports regular
|
||||
maps and inline indexed sections. External section URLs return an actionable
|
||||
unsupported-profile error. This is a defined decoder profile, not a claim to
|
||||
implement every current source-map extension.
|
||||
|
||||
One owned Node subprocess decodes the map. Its private request directory is
|
||||
removed before success is returned. Only this child's inherited `NODE_OPTIONS`
|
||||
is removed; explicit V8 flags set a 192 MiB old-generation and 8 MiB semi-space
|
||||
budget, one V8 worker and a 20-second deadline. Reported actual V8 heap limits
|
||||
are checked. These are heap limits, not an aggregate process RSS limit. Cleanup
|
||||
may extend the deadline and an unconfirmed cleanup overrides success with
|
||||
`cleanup_incomplete` and the owning operation/resources.
|
||||
|
||||
Complete evidence is bounded by a 4 MiB map, 32 MiB codec reply, 262144 decoded
|
||||
rows/segments and 64 nested section levels. Exceeding a budget returns an explicit
|
||||
failure, without partial results. The existing 32 MiB manifest and 16 MiB selected
|
||||
script boundaries also apply. No global toolchain or browser configuration changes
|
||||
are made.
|
||||
|
||||
## Verification
|
||||
|
||||
Focused tests cover duplicate mappings, indexed offsets/overlap/local indexes,
|
||||
32-bit VLQ overflow, nullable content, UTF-16/CRLF positions, immutable artifact
|
||||
identity, cancellation, late output closure, cleanup failures, SDK schemas and
|
||||
public CLI behavior. The actual codec process is exercised on the test host.
|
||||
|
||||
The optional `verify:browser:source-maps` lane compiles a source-owned TypeScript
|
||||
fixture with isolated esbuild 0.25.10, serves it to real Chromium, captures and
|
||||
exports the actual response, then checks the original compiler point and exact
|
||||
retained content through CLI and stdio MCP. It can target an installed package.
|
||||
Only this lane requires Chromium and the fixture compiler. Linux x64 is the real
|
||||
workflow covered by the dedicated CI job; other host workflows remain unverified.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Captured website module relationships
|
||||
|
||||
`trace_web_module_imports` / `rea trace-web-module-imports` traces one script's
|
||||
outgoing native ES imports and re-exports from an
|
||||
[exported capture](website-script-export.md). It verifies the selected source's
|
||||
SHA-256 and byte count, preserves browser URL identities, and returns every
|
||||
matching capture candidate inline. It does not refetch website assets.
|
||||
|
||||
## Select the source and context
|
||||
|
||||
Provide a caller-supplied Chromium executable supporting `import.meta.resolve`.
|
||||
REA does not install or upgrade a browser. Real verification covers Linux x64;
|
||||
other hosts have not received this feature's real workflow verification.
|
||||
|
||||
```bash
|
||||
REA_BROWSER_EXECUTABLE=/absolute/path/to/chrome \
|
||||
rea trace-web-module-imports /analysis/export/manifest.json 0 \
|
||||
--importer-url 'https://example.test/app/main.js?build=2#entry' \
|
||||
--import-map-path /analysis/import-map.json \
|
||||
--import-map-base-url https://example.test/maps/ \
|
||||
--json
|
||||
```
|
||||
|
||||
The MCP input expresses the same choices:
|
||||
|
||||
```json
|
||||
{
|
||||
"manifest_path": "/analysis/export/manifest.json",
|
||||
"script_index": 0,
|
||||
"importer_url": "https://example.test/app/main.js?build=2#entry",
|
||||
"import_map": {
|
||||
"path": "/analysis/import-map.json",
|
||||
"base_url": "https://example.test/maps/"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`script_index` is zero-based in `manifest.scripts`. If `importer_url` is omitted,
|
||||
the trace selects the reported source URL. An inline script's document base
|
||||
cannot be established from that URL alone; choose the context explicitly when
|
||||
known. `import_map` is optional and reads local JSON only. Its exact bytes and
|
||||
base URL are retained; selecting it does not prove the map was installed in the
|
||||
captured page. HTTP(S) importer contexts without URL userinfo are supported initially.
|
||||
|
||||
## Interpret the evidence
|
||||
|
||||
- Static imports, re-exports and literal dynamic imports receive the native
|
||||
engine's resolved URL or exact rejection message. Empty strings are retained.
|
||||
- Computed dynamic imports retain their expression and source positions with
|
||||
`computed-specifier` unknown resolution. Erased type-only imports/exports and
|
||||
inferred bundler IDs are excluded from native import claims.
|
||||
- Locations use UTF-16 offsets and columns, with one-based lines. Parse recovery
|
||||
diagnostics and unparseable input are explicit.
|
||||
- Query variants and fragments remain distinct. An exact reported URL is an
|
||||
`exact-reported-url` candidate. A response URL stripped of its fragment is a
|
||||
weaker `response-url-without-fragment` content candidate. Multiple captured
|
||||
versions are all returned; the trace selects none as canonical.
|
||||
- Candidate metadata remains reported capture evidence; only the selected
|
||||
source's bytes are reverified. Missing/lazy targets can resolve successfully
|
||||
with no captured candidate. Every relationship reports execution as unknown.
|
||||
- Source-map declarations remain declarations; this operation does not read or
|
||||
invent map bytes, original-source mappings, worker ownership or UI causality.
|
||||
|
||||
The selected source is loaded from `files/` under the manifest's current
|
||||
directory. Relocation retains the manifest's originally reported paths and the
|
||||
actual current source path separately. Unsafe paths, symlink source directories,
|
||||
nonregular files, wrong digests/sizes and malformed UTF-8 fail explicitly.
|
||||
|
||||
## Engine and lifecycle
|
||||
|
||||
An owned temporary browser context runs REA's trusted resolver stub. The selected
|
||||
application is parsed as data and is never executed by this trace. Page requests
|
||||
are fulfilled with REA's synthetic document/stub or blocked; no website asset is
|
||||
downloaded. This does not establish an OS sandbox or browser-wide network
|
||||
containment. The engine version and native report are retained in Evidence.
|
||||
|
||||
Sources without literal imports do not acquire a browser. The manifest, source
|
||||
and import map have 32 MiB, 16 MiB and 4 MiB input limits. Native resolution has
|
||||
one 20-second deadline; cleanup may extend that deadline. One renderer is
|
||||
requested, and there is no aggregate host memory-limit claim. Cancellation
|
||||
closes only the owned browser; cleanup failure cannot return a successful trace.
|
||||
`REA_BROWSER_NO_SANDBOX=true` passes Chromium's corresponding launch option when
|
||||
the host requires it; it does not enable an REA security boundary.
|
||||
|
||||
See [testing](testing.md) for the native loading oracle and CLI/MCP real lane.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Captured website scripts and static JavaScript analysis
|
||||
|
||||
`export_web_scripts` bridges retained browser sources into the existing local
|
||||
JavaScript analyzer. Both CLI and MCP use the same workflow. It reads a saved
|
||||
`inspect_web_page` or `capture_browser_scenario` capture and writes scripts plus a
|
||||
verified manifest into one caller-selected, absent directory.
|
||||
|
||||
## Capture and export
|
||||
|
||||
For passive inspection, select `include_script_sources: true` (CLI:
|
||||
`--include-script-sources`). For scenarios, select
|
||||
`capture.network.response_body: true`. Save either the complete Evidence record
|
||||
or its normalized result as UTF-8 JSON. The compact MCP wrapper's `evidence`
|
||||
field contains the complete Evidence record.
|
||||
|
||||
```sh
|
||||
rea export-web-scripts /analysis/capture.json /analysis/exported-scripts --json
|
||||
rea analyze-javascript-application /analysis/exported-scripts/files --json
|
||||
```
|
||||
|
||||
MCP uses `export_web_scripts` with `capture_path` and `output_directory` absolute
|
||||
filesystem paths. Its inline result includes `analysis_input`, which can be
|
||||
passed directly to `analyze_javascript_application`. When no script bytes are
|
||||
exportable, `analysis_input` is null and the manifest retains the unavailable
|
||||
sources and their reasons. Capture again with the corresponding content option.
|
||||
|
||||
The destination must not exist. Use a new directory for each capture; an
|
||||
existing directory or symlink is rejected without overwriting its contents.
|
||||
REA creates private output, verifies every written file by digest and durable
|
||||
readback, and removes its own partial output on failure or cancellation.
|
||||
A failed rollback reports residual paths.
|
||||
|
||||
## Sources and mappings
|
||||
|
||||
Every source is returned inline with its URL and its script/frame identity or
|
||||
request transaction and event sequence references. The result also contains
|
||||
the input capture's byte digest, original coverage, source Evidence link when
|
||||
provided, and the persisted manifest's digest and byte count. A saved Evidence
|
||||
envelope must have a valid semantic identifier and matching operation.
|
||||
|
||||
Exported `relative_path` values are relative to `analysis_input.input_path`.
|
||||
The persisted `manifest.json` contains the same inline manifest fields, excluding
|
||||
its own descriptor. It does not duplicate script bytes.
|
||||
|
||||
- Passive sources are the Debugger-exposed text encoded as UTF-8, with the
|
||||
verified source digest and source-map declaration retained. Unavailable or
|
||||
explicitly non-JavaScript sources remain unavailable.
|
||||
- Scenario sources use observed requests classified as `script`, matched to
|
||||
completed response bytes by transaction and event identity. URLs never select
|
||||
the response. Binary and empty retained bytes are written exactly.
|
||||
- Ordinary query-free `.js`, `.mjs`, and `.cjs` URL paths are arranged under a
|
||||
directory for each origin, preserving their relative module layout when it
|
||||
is unambiguous.
|
||||
- Competing versions, query/fragment variants, case and file/directory prefix
|
||||
collisions, inline sources, and unrepresentable paths use unique isolated
|
||||
names with a stated reason. Missing competing sources also prevent selection
|
||||
of an apparently canonical version.
|
||||
|
||||
## Interpretation and limits
|
||||
|
||||
Export does not fetch URLs or execute JavaScript. Retained Debugger sources and
|
||||
response bytes do not establish execution or a complete website. Response bytes
|
||||
are browser-decoded bytes; they do not represent transfer encoding. Declared
|
||||
redaction is preserved and can change JavaScript syntax; passive captures do
|
||||
not report a byte-redaction flag, so that value remains unknown.
|
||||
|
||||
The existing analyzer inventories and parses the actual exported files. It can
|
||||
resolve ordinary captured relative modules, but does not implement browser
|
||||
import maps, root-relative URL loading, remote imports, or runtime behavior.
|
||||
Its local resolver may strip import queries/fragments; isolated variants are
|
||||
kept away from canonical paths to prevent those from selecting a different
|
||||
observed version. Missing dependencies remain unknown. Source maps are not
|
||||
fetched or reconstructed during export.
|
||||
|
||||
The export Evidence describes a derivation from the saved capture. Subsequent
|
||||
application-analysis Evidence describes the local exported artifact. Their
|
||||
identities and authorities remain separate.
|
||||
|
||||
## Verification
|
||||
|
||||
`npm run verify:browser:scripts` exercises active capture, exact source export,
|
||||
manifest readback, query variants, and relative-module analysis through a real
|
||||
browser, CLI, and stdio MCP. Set `REA_BROWSER_EXECUTABLE` to an installed
|
||||
Chrome-family browser. The verifier accepts an optional installed REA entrypoint
|
||||
argument for package verification. `npm run verify:browser` additionally covers
|
||||
passive `inspect_web_page` export through both public adapters.
|
||||
+121
-104
@@ -1,156 +1,173 @@
|
||||
# Windows Ghidra P0
|
||||
|
||||
Status: experimental. This boundary is useful for approved local fixtures, but
|
||||
it is not general Windows REA support and must not be used for hostile,
|
||||
sensitive, security-decision, or concurrently mutable targets.
|
||||
This guide describes repository main. npm 4.1.0 includes the Windows
|
||||
native bundle; check the [release boundary](installation.md#released-package-and-main)
|
||||
before applying these instructions to an older published package.
|
||||
|
||||
Status: experimental Windows x64 support for the read-only P0 boundary. Windows
|
||||
package builds bundle native process, filesystem, and DACL controls. REA
|
||||
uses them automatically when the analyst selects a Ghidra operation; no
|
||||
additional permission flag or degraded mode is required.
|
||||
|
||||
## Supported boundary
|
||||
|
||||
The P0 accepts exactly:
|
||||
|
||||
- a Windows x64 host;
|
||||
- Node.js 22.19+ or 24.11+;
|
||||
- an operator-installed official Ghidra 12.1.2 distribution;
|
||||
- a 64-bit full JDK 21;
|
||||
- Windows 10+ x64 with fixed local NTFS target and temporary volumes;
|
||||
- Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+;
|
||||
- an operator-installed Ghidra 12.1.x distribution (verified with 12.1.4);
|
||||
- a 64-bit full JDK inside that installation's declared Java range (JDK 21 or newer, with no maximum, for current 12.1 releases);
|
||||
- an explicit native, non-managed, non-DLL x86-64 PE application; and
|
||||
- the existing 19 read-only Ghidra inventory and function-analysis operations.
|
||||
- the 25 read-only Ghidra inventory, memory, and function-analysis operations.
|
||||
|
||||
Hopper, Ghidra GUI state, mutation, controlled JavaScript replay, process
|
||||
capture, browser/Electron observation, artifact extraction, and general
|
||||
Windows feature parity are not implied by this boundary. Managed PE/CLI
|
||||
inspection remains a separate execution-free provider and is not routed
|
||||
through Windows Ghidra P0.
|
||||
Loaded memory reads and file offsets preserve Ghidra's observed source mappings.
|
||||
PE load-image inspection returns measurements with an explicit unsupported
|
||||
attestation status; independent format-specific verification currently supports
|
||||
DOS MZ only.
|
||||
|
||||
REA does not install or upgrade Ghidra, Java, Python, npm, Node.js, Hopper, or
|
||||
another package on Windows. The production adapter uses the packaged Java
|
||||
`HeadlessScript`; Python and PyGhidra are not prerequisites.
|
||||
This boundary does not establish general Windows feature parity. Ghidra GUI
|
||||
state, annotations, other target architectures and formats, and Hopper-only
|
||||
operations remain unavailable through this provider. Managed PE/CLI inspection
|
||||
uses its separate execution-free provider.
|
||||
|
||||
REA does not install or upgrade Ghidra, Java, Python, npm, Node.js, Hopper, or a
|
||||
compiler. The adapter uses the packaged Java `HeadlessScript`; Python and
|
||||
PyGhidra are not prerequisites. Users do not build the native addon.
|
||||
|
||||
REA selects its packaged bridge scripts by explicit path. Same-named files or
|
||||
directories in the caller's working directory do not select a different
|
||||
script or require users to clean that directory before analysis.
|
||||
|
||||
## Configuration
|
||||
|
||||
Install REA, Ghidra, and JDK 21 separately. In PowerShell:
|
||||
|
||||
```powershell
|
||||
npm install --global rea-agents
|
||||
$env:GHIDRA_INSTALL_DIR = "C:\tools\ghidra_12.1.2_PUBLIC"
|
||||
$env:JAVA_HOME = "C:\Program Files\Java\jdk-21"
|
||||
$env:GHIDRA_INSTALL_DIR = "C:\tools\ghidra_12.1.4_PUBLIC"
|
||||
$env:JAVA_HOME = "C:\tools\jdk-21"
|
||||
$env:REA_ANALYSIS_PROVIDER = "ghidra"
|
||||
|
||||
rea doctor --json
|
||||
rea providers --json
|
||||
rea inspect "C:\approved-fixtures\sample.exe" --provider ghidra --format json
|
||||
rea inspect "C:\fixtures\sample.exe" --provider ghidra --format json
|
||||
```
|
||||
|
||||
`rea doctor` checks the x64 host, exact Ghidra release,
|
||||
`support\analyzeHeadless.bat`, `java.exe`, `javac.exe`, JDK bitness, and JDK
|
||||
major version. `rea setup` remains unavailable on Windows and makes no changes.
|
||||
`rea doctor` distinguishes installation prerequisites from native artifact or
|
||||
OS failures. It checks the Ghidra release, headless launcher, full JDK, x64
|
||||
host, and native controls. A missing or mismatched addon requires the matching
|
||||
Windows x64 package; changing MCP registration cannot supply that artifact.
|
||||
A filesystem or access failure identifies the failed native constraint.
|
||||
Other doctor checks, including unconfigured agent integrations, can remain
|
||||
unready while Ghidra itself is available.
|
||||
|
||||
For an MCP client, register the resolved Node entry point and preserve the
|
||||
three environment variables above. A representative configuration is:
|
||||
`rea setup` can register supported agent integrations and the bundled skill.
|
||||
Its existing preview and approval apply to configuration writes. Setup
|
||||
preserves valid Ghidra and Java settings even when a separate runtime
|
||||
prerequisite is unavailable.
|
||||
|
||||
For MCP, register the resolved Node entry point and the same provider settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea-windows-ghidra-p0": {
|
||||
"command": "C:\\Program Files\\nodejs\\node.exe",
|
||||
"args": [
|
||||
"C:\\Users\\analyst\\AppData\\Roaming\\npm\\node_modules\\rea-agents\\scripts\\rea.mjs",
|
||||
"mcp"
|
||||
],
|
||||
"rea": {
|
||||
"command": "C:\\tools\\node\\node.exe",
|
||||
"args": ["C:\\tools\\rea\\scripts\\rea.mjs", "mcp"],
|
||||
"env": {
|
||||
"REA_ANALYSIS_PROVIDER": "ghidra",
|
||||
"GHIDRA_INSTALL_DIR": "C:\\tools\\ghidra_12.1.2_PUBLIC",
|
||||
"JAVA_HOME": "C:\\Program Files\\Java\\jdk-21"
|
||||
"GHIDRA_INSTALL_DIR": "C:\\tools\\ghidra_12.1.4_PUBLIC",
|
||||
"JAVA_HOME": "C:\\tools\\jdk-21"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Resolve the actual global package path on the intended machine; do not copy the
|
||||
example path blindly. Restart the MCP client after changing its registration.
|
||||
These paths are examples. Use the actual package location and restart the MCP
|
||||
client after registration changes. Cold Ghidra import can exceed a client's
|
||||
short default request timeout; the real verification lane allows the existing
|
||||
330-second provider startup deadline to complete.
|
||||
|
||||
## Target and import identity
|
||||
## Native authority and target identity
|
||||
|
||||
PE parsing records architecture, COFF executable role, and whether the CLI
|
||||
header data-directory entry is non-empty. Header classification and SHA-256
|
||||
are read from the same open file handle. Windows admission then rejects:
|
||||
The verified boundary excludes SUBST aliases and changing drive-letter/DOS-device
|
||||
namespaces. REA does not claim a separate SUBST detector. Mounted-folder entries
|
||||
that expose reparse tags fail component admission; broader namespace variants
|
||||
have not been established by this lane. Requested paths, final handle paths,
|
||||
volume serials, and file IDs remain separate observations.
|
||||
|
||||
- non-PE formats;
|
||||
- x86, ARM, ARM64, or unknown architectures;
|
||||
- DLL/shared-library and non-executable image roles;
|
||||
- managed or incompletely classified PE targets; and
|
||||
- malformed optional headers or data-directory declarations.
|
||||
The loader validates package version, ABI, Node-API compatibility, PE machine,
|
||||
and artifact SHA-256 before loading the package-owned addon. Native failure
|
||||
reasons remain distinct from a valid installation and an unsupported target.
|
||||
|
||||
After admission, REA copies the target into the ephemeral Ghidra runtime and
|
||||
requires the snapshot digest to match the admitted SHA-256. Ghidra imports only
|
||||
that snapshot. Before serving a request, the Java bridge requires Ghidra's
|
||||
`Program.getExecutableSHA256()` observation to match the same digest. A path
|
||||
replacement that changes bytes therefore fails before Evidence is created.
|
||||
PE header classification and SHA-256 come from the same open file. The Windows
|
||||
provider admits native x86-64 PE applications and rejects unsupported roles,
|
||||
architectures, managed images, and malformed headers. The original selected
|
||||
source coordinate is preserved for native admission.
|
||||
|
||||
This linkage does not establish Windows reparse-point or hostile-path safety.
|
||||
The current implementation has no handle-based `CreateFileW` authority backend
|
||||
and no verified no-follow equivalent for every path component.
|
||||
Admission opens and checks every path component without following a reparse
|
||||
point. It retains handles, verifies local NTFS semantics, and observes volume
|
||||
serial, 128-bit file ID, final handle path, requested path, and size. Reparse
|
||||
paths, alternate streams, ambiguous coordinates, UNC paths, and other
|
||||
filesystems are rejected with the actual constraint.
|
||||
|
||||
## Local transport and lifecycle
|
||||
The snapshot streams from the admitted source handle on a background worker.
|
||||
Its digest must match the parsed target digest. Ghidra imports that private,
|
||||
immutable snapshot, and the bridge requires the imported program SHA-256 to
|
||||
match before serving operations. Source and snapshot identities remain
|
||||
separate diagnostic observations. Snapshot cancellation is checked between
|
||||
64 KiB reads and cleanup waits for worker settlement.
|
||||
|
||||
Linux continues to use a mode-restricted Unix socket. Windows uses a Java
|
||||
listener bound to `127.0.0.1` on an ephemeral port. The bridge atomically
|
||||
publishes a bounded endpoint record containing only schema version, literal
|
||||
loopback host, and port. The random 256-bit request token remains only in the
|
||||
ephemeral session descriptor and every request is authenticated.
|
||||
## Private runtime, transport, and lifecycle
|
||||
|
||||
`analyzeHeadless.bat` is invoked through an absolute `cmd.exe` with a fixed
|
||||
argument sequence, command extensions enabled for the Ghidra script, delayed
|
||||
expansion disabled, and Node shell execution disabled. Every batch token is
|
||||
quoted. Paths containing command-interpreter metacharacters are rejected
|
||||
instead of escaped heuristically.
|
||||
Runtime creation installs a protected DACL permitting exactly the current user
|
||||
and SYSTEM. Readback checks the actual owner and ACEs by handle. Descriptors
|
||||
and snapshots remain locked until owner cleanup. Child files inherit the
|
||||
private boundary. Analysis runs with the caller's authority; these controls
|
||||
are not a sandbox for the Ghidra engine or other processes using the same user.
|
||||
|
||||
Close, cancellation, timeout, protocol failure, or provider exit removes the
|
||||
project and runtime root. Windows P0 uses bounded `taskkill /T /F` process-tree
|
||||
termination. It does not assign Ghidra to a Job Object and cannot make the same
|
||||
PID-reuse and descendant-ownership claim as the POSIX process-group backend.
|
||||
Windows uses an authenticated listener bound to `127.0.0.1` on an ephemeral
|
||||
port. The endpoint record contains only the literal loopback host and port.
|
||||
The bridge writes a new record and closes its writer before the native reader
|
||||
can admit it. The reader refuses concurrent writers and verifies its opened
|
||||
object's private DACL. The random 256-bit bearer token stays in the private,
|
||||
immutable session descriptor and is redacted from diagnostic output.
|
||||
|
||||
## Known security gates
|
||||
The batch launcher uses an absolute `cmd.exe`, fixed switches, delayed
|
||||
expansion disabled, and quoted tokens. Command-interpreter metacharacters in
|
||||
launch paths are unsupported. JVM home and temporary properties use quoted
|
||||
`JDK_JAVA_OPTIONS`, preserving spaces. `APPDATA` and `LOCALAPPDATA` point into
|
||||
the private runtime, isolating Ghidra preferences, logs, and caches.
|
||||
|
||||
The following remain explicitly unavailable:
|
||||
|
||||
- verified current-user-only DACLs for runtime directories, descriptors, logs,
|
||||
snapshots, and endpoint records;
|
||||
- a DACL-protected Windows named pipe or equivalent OS-identity-restricted IPC;
|
||||
- reparse-point-safe, handle-based input and output authority;
|
||||
- Job Object assignment before child execution and kill-on-close proof;
|
||||
- general extraction containment and filesystem-sensitive workflows; and
|
||||
- independent security acceptance for hostile or mutable targets.
|
||||
|
||||
Successful symlink creation or ConPTY availability does not satisfy any of
|
||||
these gates. Inspect the machine-readable report with:
|
||||
|
||||
```powershell
|
||||
npm run probe:windows-capabilities
|
||||
```
|
||||
Process creation assigns a suspended child atomically to an owned Job Object.
|
||||
Membership and kill-on-owner-close policy are verified before resume.
|
||||
Breakaway is disabled. Only the child's default file owner is normalized;
|
||||
the caller token stays unchanged. Caller CPU affinity and priority are
|
||||
preserved. Close, cancellation, timeout, startup failure, and provider failure
|
||||
terminate the job and verify all members have settled before runtime cleanup.
|
||||
Cleanup removes admitted objects by handle and unlinks reparse entries without
|
||||
following their targets. Abrupt owner death kills the job but can leave
|
||||
private runtime files; crash-time transactional deletion is not claimed.
|
||||
|
||||
## Verification
|
||||
|
||||
Hosted CI builds, type-checks, runs the curated Windows tests, records the named
|
||||
host capability report, packs the real npm artifact, installs it in isolation,
|
||||
and compares the packaged MCP catalog with `TOOL_CONTRACTS`.
|
||||
|
||||
The controlled real-engine lane is intentionally separate:
|
||||
See [native controls](https://github.com/morluto/rea/blob/main/native/windows/README.md) for the separate artifact
|
||||
build lane and native boundary checks. Development tarballs without the built
|
||||
artifact report native controls unavailable; publishing requires the verified
|
||||
artifact and manifest.
|
||||
|
||||
```powershell
|
||||
$env:GHIDRA_INSTALL_DIR = "C:\tools\ghidra_12.1.2_PUBLIC"
|
||||
$env:JAVA_HOME = "C:\Program Files\Java\jdk-21"
|
||||
npm ci
|
||||
npm run verify:windows-native
|
||||
npm run verify:ghidra:windows
|
||||
npm run verify:ghidra:windows:package -- C:\fixtures\installed-rea C:\fixtures\sample.exe
|
||||
```
|
||||
|
||||
The verifier creates an ignored deterministic x86-64 PE fixture, checks its
|
||||
fixed SHA-256, opens Ghidra 12.1.2 through the production Java bridge, exercises
|
||||
all 19 admitted operations, verifies the target/snapshot/import digest chain,
|
||||
and checks endpoint, project, process, and runtime cleanup. The GitHub workflow
|
||||
accepts only the fixed `real-ghidra-windows` repository-dispatch event against
|
||||
default-branch `main`, a protected `real-ghidra-windows` environment, and a
|
||||
self-hosted runner labelled `Windows`, `x64`, and `ghidra-12-1-2`.
|
||||
The package lane must run with an ordinary, non-elevated user token. It exercises
|
||||
CLI inspection and every admitted MCP operation, validates named canonical
|
||||
input/output contracts, checks the source digest stays unchanged, and verifies
|
||||
runtime cleanup. Use a source-owned fixture, one analysis process at a time,
|
||||
and a bounded heap for real-provider verification.
|
||||
|
||||
Passing this verifier proves the bounded P0 operation claim. It does not close
|
||||
the DACL, reparse-point, named-pipe, or Job Object gates above.
|
||||
The controlled GitHub workflow runs only default-branch code on its protected
|
||||
self-hosted Windows environment. A separate Linux job builds the matching
|
||||
native artifact. Hosted Windows CI verifies native controls and packaged
|
||||
startup separately from real Ghidra acceptance. Broader hostile-target,
|
||||
concurrently mutable-path, extraction, and IPC-security claims require their
|
||||
own acceptance and are not implied by this P0 result.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import eslint from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ["dist/**", "node_modules/**"] },
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.strictTypeChecked,
|
||||
{
|
||||
files: ["**/*.ts"],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
"@typescript-eslint/no-confusing-void-expression": "error",
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-unnecessary-condition": "error",
|
||||
"@typescript-eslint/no-unsafe-type-assertion": "error",
|
||||
"@typescript-eslint/only-throw-error": "error",
|
||||
"@typescript-eslint/require-await": "error",
|
||||
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
||||
},
|
||||
},
|
||||
);
|
||||
+17
-9
@@ -3,6 +3,10 @@ set -euo pipefail
|
||||
|
||||
PACKAGE="rea-agents"
|
||||
REPOSITORY="morluto/rea"
|
||||
numeric_identifier='(0|[1-9][0-9]*)'
|
||||
prerelease_identifier="(${numeric_identifier}|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)"
|
||||
build_identifier='[0-9A-Za-z-]+'
|
||||
version_pattern="${numeric_identifier}\\.${numeric_identifier}\\.${numeric_identifier}(-${prerelease_identifier}(\\.${prerelease_identifier})*)?(\\+${build_identifier}(\\.${build_identifier})*)?"
|
||||
version="${REA_VERSION:-}"
|
||||
dry_run=false
|
||||
start_setup=true
|
||||
@@ -55,28 +59,32 @@ fi
|
||||
platform="$(uname -s)"
|
||||
[[ "$platform" == "Darwin" || "$platform" == "Linux" ]] || fail "REA supports macOS and selected 64-bit Linux distributions."
|
||||
command -v curl >/dev/null 2>&1 || fail "curl is required. Install curl, then rerun this installer."
|
||||
command -v node >/dev/null 2>&1 || fail "Node.js 22.19+ or 24.11+ is required; install it from https://nodejs.org and retry."
|
||||
command -v node >/dev/null 2>&1 || fail "Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ is required; install it from https://nodejs.org and retry."
|
||||
command -v npm >/dev/null 2>&1 || fail "npm is required; install it with Node.js and retry."
|
||||
|
||||
node_version="$(node -p 'process.versions.node' 2>/dev/null || true)"
|
||||
IFS=. read -r node_major node_minor _node_patch <<<"$node_version"
|
||||
[[ "$node_major" =~ ^[0-9]+$ && "$node_minor" =~ ^[0-9]+$ ]] || fail "the active Node.js version could not be read. Check that node works and is on PATH, then retry."
|
||||
if ! ((node_major == 22 && node_minor >= 19 || node_major >= 24)); then
|
||||
fail "Node.js $node_version is unsupported; use Node.js 22.19+ or 24.11+."
|
||||
fi
|
||||
if ((node_major == 24 && node_minor < 11)); then
|
||||
fail "Node.js $node_version is unsupported; use Node.js 24.11 or newer."
|
||||
if ! [[ "$_node_patch" =~ ^[0-9]+(\+[0-9A-Za-z.-]+)?$ ]] ||
|
||||
! ((node_major == 22 && node_minor >= 19 || node_major == 24 && node_minor >= 11 || node_major >= 26)); then
|
||||
fail "Node.js $node_version is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+."
|
||||
fi
|
||||
|
||||
if [[ -n "$version" ]]; then
|
||||
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || fail "version must be an exact semantic version."
|
||||
[[ "$version" =~ ^${version_pattern}$ ]] || fail "version must be an exact semantic version."
|
||||
else
|
||||
release_json="$(curl -fsSL --proto '=https' --tlsv1.2 --retry 3 -H 'Accept: application/vnd.github+json' "https://api.github.com/repos/$REPOSITORY/releases/latest")" || fail "the latest REA release could not be resolved. Check network access or pass --version VERSION, then retry."
|
||||
tag="$(printf '%s' "$release_json" | node -e 'let s="";process.stdin.on("data",c=>s+=c).on("end",()=>{const v=JSON.parse(s).tag_name;if(typeof v!=="string")process.exit(1);process.stdout.write(v)})' 2>/dev/null)" || fail "the release response was invalid. Retry later or pass --version VERSION."
|
||||
[[ "$tag" =~ ^rea-agents-([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?)$ ]] || fail "the latest release tag was invalid. Retry later or pass --version VERSION."
|
||||
[[ "$tag" =~ ^rea-agents-(${version_pattern})$ ]] || fail "the latest release tag was invalid. Retry later or pass --version VERSION."
|
||||
version="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
|
||||
# npm's exact-version resolver cleans build metadata from the requested version,
|
||||
# so it may install the base release and make the post-install identity check fail.
|
||||
if [[ "$version" =~ \+ ]]; then
|
||||
fail "npm cannot install an exact version with build metadata. Pass a version without build metadata, then retry."
|
||||
fi
|
||||
|
||||
prefix_args=()
|
||||
if [[ "$platform" == "Linux" ]]; then
|
||||
prefix_args=(--prefix "$HOME/.local")
|
||||
@@ -102,7 +110,7 @@ printf 'Installing %s@%s...\n' "$PACKAGE" "$version"
|
||||
npm install --global "${prefix_args[@]}" "$PACKAGE@$version" || fail "npm could not install REA. Check registry access and npm permissions, then retry."
|
||||
[[ -x "$install_bin" ]] || fail "npm completed without installing the rea command. Check the npm global bin directory and PATH, then retry."
|
||||
installed_version="$("$install_bin" --version 2>/dev/null | tr -d '[:space:]')" || fail "the installed REA version could not be read. Reinstall the requested version, then retry."
|
||||
[[ "$installed_version" == "$version" ]] || fail "installed version $installed_version does not match $version."
|
||||
[[ "$installed_version" == "$version" ]] || fail "installed version $installed_version does not match $version. Reinstall the requested version, then retry."
|
||||
|
||||
printf 'REA %s is installed.\n' "$version"
|
||||
if [[ "$start_setup" == true && -r /dev/tty && -w /dev/tty && -t 1 ]]; then
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"$schema": "./node_modules/knip/schema.json",
|
||||
"entry": ["src/main.ts", "src/cli.ts"],
|
||||
"entry": ["src/main.ts", "src/cli.ts", "src/contracts/errorSchemas.ts"],
|
||||
"project": ["src/**/*.ts", "tests/**/*.ts", "scripts/**/*.mjs"],
|
||||
"ignoreDependencies": [],
|
||||
"ignoreBinaries": ["ps"],
|
||||
"vitepress": {
|
||||
"entry": ["docs/.vitepress/config.ts"]
|
||||
},
|
||||
"ignoreBinaries": ["mkfifo"],
|
||||
"ignoreDependencies": ["electron", "esbuild", "webpack"],
|
||||
"ignoreExportsUsedInFile": true,
|
||||
"rules": {
|
||||
"files": "error",
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# Windows native controls
|
||||
|
||||
REA ships one Windows x64 Node-API 8 addon. Users do not compile it or install
|
||||
another runtime. Ghidra and a full JDK remain bring-your-own prerequisites.
|
||||
The addon uses Windows 10+ APIs and admits fixed local NTFS volumes only.
|
||||
|
||||
The package owns the artifact path. The loader checks the package version,
|
||||
ABI, Node-API compatibility, PE architecture, and SHA-256 before loading it.
|
||||
There is no environment override for loading an arbitrary native module.
|
||||
|
||||
## Build
|
||||
|
||||
The artifact lane requires Linux, MinGW-w64 `x86_64-w64-mingw32-g++`,
|
||||
`x86_64-w64-mingw32-dlltool`, and `tar`. It never installs these commands.
|
||||
Node-API headers are reused from the build cache or downloaded from the
|
||||
pinned official Node release with its published SHA-256 checked.
|
||||
|
||||
```sh
|
||||
npm run build:windows-native
|
||||
npm run verify:windows-native:artifact
|
||||
npm pack
|
||||
```
|
||||
|
||||
`build/` is ignored by Git. The npm file inventory includes only the addon and
|
||||
its manifest. Release CI builds both, and `prepublishOnly` rejects a missing,
|
||||
stale, mismatched, or wrong-architecture artifact. A development tarball built
|
||||
without this lane reports Windows native controls as unavailable.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Opaque Node objects carry a native type tag, resource kind, and closed state.
|
||||
A pathname, serialized object, or forged handle cannot grant authority.
|
||||
- Input admission opens every component with `OPEN_REPARSE_POINT`, verifies
|
||||
the actual NTFS object, and retains handles that deny replacement and source
|
||||
writing. An admitted child keeps each ancestor nonempty; ancestor handles
|
||||
permit unrelated writes while NTFS rejects in-place reparse conversion. Volume serial, 128-bit file ID, final handle path,
|
||||
requested path, size, and digest observations are preserved separately.
|
||||
- New runtime roots and private child objects are created relative to admitted
|
||||
parent handles with `NtCreateFile`. A protected DACL and current-user owner
|
||||
are installed at creation. Readback permits exactly the current user and SYSTEM. Child
|
||||
files inherit this boundary; private files are also checked by handle.
|
||||
- Snapshots stream through a 64 KiB buffer on a Node async worker. Cancellation
|
||||
is checked between reads, cleanup waits for settlement, and the digest
|
||||
commits to the bytes written. Descriptor and snapshot handles remain held
|
||||
until runtime cleanup.
|
||||
Snapshot ownership is single-flight per runtime: an overlapping request fails
|
||||
with `ERROR_BUSY` before acquiring cancellation ownership. The TypeScript owner
|
||||
records only the accepted promise and abort listener; aborting a rejected copy
|
||||
must not cancel the active copy.
|
||||
- `PROC_THREAD_ATTRIBUTE_JOB_LIST` assigns a suspended child atomically to an
|
||||
unnamed, non-inherited Job Object. Membership and kill-on-close policy are
|
||||
checked before resume. Breakaway is disabled, and cleanup waits for all
|
||||
job members and captured output, including detached descendants.
|
||||
- Only the suspended child's default file owner is normalized. Caller token
|
||||
privileges and ownership remain unchanged. Caller CPU affinity and process
|
||||
priority are preserved in the child. Environment names are sorted and
|
||||
deduplicated using Windows ordinal comparison, preserving their native Unicode
|
||||
semantics and caller-selected values.
|
||||
- Cleanup walks locked directory objects, removes files by handle, and unlinks
|
||||
reparse entries without traversing their targets. Normal close, cancellation,
|
||||
startup failure, and timeout release provider resources. Abrupt owner death
|
||||
kills the owned job; it can leave private runtime files for later operator
|
||||
cleanup and does not claim transactional deletion after a crash.
|
||||
|
||||
Windows failures retain their constraint, requested coordinate, Win32 code,
|
||||
and system message. Unsupported filesystems and reparse paths are distinct
|
||||
from OS access denial and missing packaged controls.
|
||||
|
||||
Ordinary drive-absolute paths accept backslashes, forward slashes, or mixed
|
||||
separators. File identity retains the original requested spelling. Separator
|
||||
translation does not resolve dot components, repeated separators, or extend
|
||||
the supported device/UNC namespaces; existing admission checks still apply.
|
||||
|
||||
The bearer-token descriptor remains under its immutable private lease until
|
||||
`runtime_close` on Windows; POSIX removes the descriptor after the bridge reads
|
||||
it. Job ownership guarantees process termination on owner death, not deletion
|
||||
of private files after a crash. SUBST drive aliases and changing DOS-device
|
||||
namespaces are outside the verified P0 scope and are not detected as a separate
|
||||
alias policy. Mounted-folder paths that report a reparse tag are rejected by
|
||||
component admission; broader namespace variants remain unverified. Preserve
|
||||
requested and final handle coordinates rather than treating them as one identity.
|
||||
|
||||
## Real verification
|
||||
|
||||
```powershell
|
||||
npm run verify:windows-native
|
||||
npm run verify:ghidra:windows
|
||||
```
|
||||
|
||||
The native lane checks DACL readback independently, source write/replacement
|
||||
rejection, unrelated sibling renames, reparse rejection, immutable snapshots,
|
||||
asynchronous cancellation, cleanup containment,
|
||||
descendant termination, unrelated-process preservation, normal exit, owner
|
||||
close, and forced owner exit. An optional independently built
|
||||
`tests/fixtures/windows/processBoundary.cc` executable adds breakaway and
|
||||
caller/child token observations and an in-place ancestor reparse attempt:
|
||||
|
||||
```powershell
|
||||
node scripts/verify-windows-native.mjs . C:\fixtures\process-boundary.exe
|
||||
```
|
||||
|
||||
`nonAdminRunner.cc` is a test-only launcher for an elevated development shell.
|
||||
It duplicates an existing non-elevated Explorer token belonging to the same
|
||||
user, creates private temporary window-station objects, and launches the test
|
||||
inside a kill-on-close fixture job. It does not create accounts, modify host
|
||||
policy, or change the caller's token. A real-provider claim must additionally
|
||||
verify the packaged CLI and MCP on that ordinary-user token.
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "authority.hpp"
|
||||
|
||||
namespace rea {
|
||||
|
||||
static const napi_type_tag RESOURCE_TAG = {0x45fcbf0b49e84fe3ULL, 0x9e3e5074130de843ULL};
|
||||
|
||||
void check(napi_status status) {
|
||||
if (status != napi_ok) throw Failure("Invalid native boundary value", L"", ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
napi_value object(napi_env env) { napi_value value; check(napi_create_object(env, &value)); return value; }
|
||||
napi_value null(napi_env env) { napi_value value; check(napi_get_null(env, &value)); return value; }
|
||||
napi_value string(napi_env env, const std::string& text) {
|
||||
napi_value value; check(napi_create_string_utf8(env, text.data(), text.size(), &value)); return value;
|
||||
}
|
||||
napi_value string(napi_env env, const std::wstring& text) {
|
||||
static_assert(sizeof(wchar_t) == sizeof(char16_t));
|
||||
napi_value value;
|
||||
check(napi_create_string_utf16(env, reinterpret_cast<const char16_t*>(text.data()), text.size(), &value));
|
||||
return value;
|
||||
}
|
||||
napi_value number(napi_env env, double numeric) {
|
||||
napi_value value; check(napi_create_double(env, numeric, &value)); return value;
|
||||
}
|
||||
napi_value boolean(napi_env env, bool flag) {
|
||||
napi_value value; check(napi_get_boolean(env, flag, &value)); return value;
|
||||
}
|
||||
void set(napi_env env, napi_value target, const char* key, napi_value value) {
|
||||
check(napi_set_named_property(env, target, key, value));
|
||||
}
|
||||
std::wstring wide(napi_env env, napi_value value) {
|
||||
size_t size = 0;
|
||||
check(napi_get_value_string_utf16(env, value, nullptr, 0, &size));
|
||||
require(size <= 32767, "Windows value exceeds the native string limit", L"", ERROR_INVALID_PARAMETER);
|
||||
std::vector<char16_t> buffer(size + 1);
|
||||
check(napi_get_value_string_utf16(env, value, buffer.data(), buffer.size(), &size));
|
||||
std::wstring result(reinterpret_cast<const wchar_t*>(buffer.data()), size);
|
||||
require(result.find(L'\0') == std::wstring::npos, "Windows value contains NUL", L"", ERROR_INVALID_PARAMETER);
|
||||
return result;
|
||||
}
|
||||
double numeric(napi_env env, napi_value value) {
|
||||
double result; check(napi_get_value_double(env, value, &result)); return result;
|
||||
}
|
||||
std::vector<napi_value> array(napi_env env, napi_value value) {
|
||||
bool valid; check(napi_is_array(env, value, &valid));
|
||||
require(valid, "Native arguments must be an array", L"", ERROR_INVALID_PARAMETER);
|
||||
uint32_t length; check(napi_get_array_length(env, value, &length));
|
||||
std::vector<napi_value> result(length);
|
||||
for (uint32_t index = 0; index < length; ++index)
|
||||
check(napi_get_element(env, value, index, &result[index]));
|
||||
return result;
|
||||
}
|
||||
static void finalize(napi_env, void* data, void*) { delete static_cast<Resource*>(data); }
|
||||
napi_value wrap(napi_env env, std::unique_ptr<Resource> value) {
|
||||
auto result = object(env);
|
||||
check(napi_type_tag_object(env, result, &RESOURCE_TAG));
|
||||
check(napi_wrap(env, result, value.get(), finalize, nullptr, nullptr));
|
||||
value.release();
|
||||
return result;
|
||||
}
|
||||
Resource& resource(napi_env env, napi_value value, Kind kind) {
|
||||
bool tagged = false;
|
||||
check(napi_check_object_type_tag(env, value, &RESOURCE_TAG, &tagged));
|
||||
require(tagged, "Native handle does not belong to REA", L"", ERROR_INVALID_HANDLE);
|
||||
void* pointer = nullptr; check(napi_unwrap(env, value, &pointer));
|
||||
auto& result = *static_cast<Resource*>(pointer);
|
||||
require(result.kind == kind && !result.closed, "Native handle is closed or has the wrong kind", L"", ERROR_INVALID_HANDLE);
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value failureValue(napi_env env, const Failure& failure) {
|
||||
wchar_t* message = nullptr;
|
||||
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, failure.win32, 0, reinterpret_cast<wchar_t*>(&message), 0, nullptr);
|
||||
std::wstring detail = message == nullptr ? L"" : message;
|
||||
if (message != nullptr) LocalFree(message);
|
||||
const std::wstring constraint(failure.constraint.begin(), failure.constraint.end());
|
||||
auto text = string(env, constraint + (failure.path.empty() ? L"" : L" [" + failure.path + L"]") +
|
||||
L" (Win32 " + std::to_wstring(failure.win32) + L"): " + detail);
|
||||
napi_value error; napi_create_error(env, nullptr, text, &error);
|
||||
const char* code = failure.win32 == ERROR_FILE_NOT_FOUND || failure.win32 == ERROR_PATH_NOT_FOUND ? "ENOENT"
|
||||
: failure.win32 == ERROR_ACCESS_DENIED ? "EACCES" : "ERR_REA_WINDOWS_NATIVE";
|
||||
set(env, error, "code", string(env, std::string(code)));
|
||||
set(env, error, "constraint", string(env, failure.constraint));
|
||||
set(env, error, "requestedPath", string(env, failure.path));
|
||||
set(env, error, "win32Code", number(env, failure.win32));
|
||||
set(env, error, "win32Message", string(env, detail));
|
||||
return error;
|
||||
}
|
||||
|
||||
static napi_value call(napi_env env, napi_callback_info info) {
|
||||
try {
|
||||
std::array<napi_value, 2> args;
|
||||
size_t count = args.size();
|
||||
check(napi_get_cb_info(env, info, &count, args.data(), nullptr, nullptr));
|
||||
require(count == 2, "Native call requires operation and arguments", L"", ERROR_INVALID_PARAMETER);
|
||||
const auto operation = wide(env, args[0]);
|
||||
const auto values = array(env, args[1]);
|
||||
if (operation == L"inspect") {
|
||||
require(values.empty(), "Inspect takes no arguments", L"", ERROR_INVALID_PARAMETER);
|
||||
return inspect(env);
|
||||
}
|
||||
if (operation.rfind(L"process_", 0) == 0) return processCall(env, operation, values);
|
||||
return filesystemCall(env, operation, values);
|
||||
} catch (const Failure& failure) {
|
||||
auto error = failureValue(env, failure);
|
||||
napi_throw(env, error);
|
||||
return nullptr;
|
||||
} catch (const std::exception& failure) {
|
||||
napi_throw_error(env, "ERR_REA_WINDOWS_NATIVE", failure.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static napi_value initialize(napi_env env, napi_value exports) {
|
||||
napi_value function;
|
||||
if (napi_create_function(env, "call", NAPI_AUTO_LENGTH, call, nullptr, &function) != napi_ok) return nullptr;
|
||||
if (napi_set_named_property(env, exports, "call", function) != napi_ok) return nullptr;
|
||||
return exports;
|
||||
}
|
||||
|
||||
} // namespace rea
|
||||
|
||||
NAPI_MODULE(NODE_GYP_MODULE_NAME, rea::initialize)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user