2026-07-29 22:00:38 +00:00
import { writeFile } from "node:fs/promises" ;
2026-07-24 13:16:35 +00:00
import { join } from "node:path" ;
import { pathToFileURL } from "node:url" ;
2026-07-29 22:00:38 +00:00
import { describe , expect , test } from "vitest" ;
2026-07-24 13:16:35 +00:00
2026-10-07 16:03:37 +08:00
import { createJavaScriptRuntimeObservationEvidence } from "../../../src/application/javascript/JavaScriptRuntimeObservationEvidence.js" ;
2026-10-10 03:30:58 +08:00
import { createBrowserEvidence } from "../../../src/application/BrowserEvidence.js" ;
2026-07-24 13:16:35 +00:00
import {
listJavaScriptRuntimeTargets ,
observeJavaScriptRuntime ,
2026-10-07 16:03:37 +08:00
} from "../../../src/application/javascript/JavaScriptRuntimeObservationService.js" ;
import { reconcileJavaScriptRuntimeEvidence } from "../../../src/application/javascript/JavaScriptRuntimeReconciliationService.js" ;
2026-10-10 03:30:58 +08:00
import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js" ;
2026-10-09 22:45:18 +08:00
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../../../src/inspector/providerIdentity.js" ;
2026-10-07 13:53:25 +08:00
import { V8InspectorProvider } from "../../../src/inspector/V8InspectorProvider.js" ;
2026-10-07 17:16:58 +08:00
import { JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE } from "../../../src/contracts/javascript/javascriptRuntimeReconciliationExample.js" ;
2026-07-24 13:16:35 +00:00
import type {
JavaScriptRuntimeObservation ,
ObserveJavaScriptRuntimeInput ,
2026-10-07 17:16:58 +08:00
} from "../../../src/domain/javascript/javascriptRuntimeObservation.js" ;
import { observeJavaScriptRuntimeInputSchema } from "../../../src/domain/javascript/javascriptRuntimeObservation.js" ;
2026-10-10 03:30:58 +08:00
import { inspectWebPageInputSchema } from "../../../src/domain/browserObservation.js" ;
import { createEvidence } from "../../../src/domain/evidence.js" ;
2026-10-07 17:16:58 +08:00
import { javascriptRuntimeReconciliationResultSchema } from "../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js" ;
2026-10-07 13:53:25 +08:00
import { startFakeV8Inspector } from "../../fixtures/inspector/fakeV8Inspector.js" ;
2026-10-10 03:30:58 +08:00
import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js" ;
2026-07-29 22:00:38 +00:00
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js" ;
2026-07-24 13:16:35 +00:00
2026-10-10 03:30:58 +08:00
test ( "reports static-layer Evidence digest failure at its caller path" , () => {
const layer = JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE . static_layers [ 0 ];
if ( layer === undefined ) throw new Error ( "Expected a static layer example" );
const runtime = createJavaScriptRuntimeObservationEvidence (
"observe_javascript_runtime" ,
{
inspector_endpoint : "http://127.0.0.1:9229" ,
target_id : "example-v8-target" ,
runtime_kind : "electron-main" ,
observation_ms : 100 ,
},
runtimeObservation ( "/tmp/index.html" , "/tmp/main.js" ),
V8_INSPECTOR_PROVIDER_IDENTITY ,
);
const result = reconcileJavaScriptRuntimeEvidence ({
static_layers : [
{
... layer ,
analysis : { ... layer . analysis , evidence_id : `ev_ ${ "0" . repeat ( 64 ) } ` },
},
],
runtime_observations : [ runtime ],
});
expect ( result ). toMatchObject ({
ok : false ,
error : {
_tag : "AnalysisInputError" ,
issues : [
{
path : [ "static_layers" , 0 , "analysis" , "evidence_id" ],
reason : "invalid_value" ,
message : "Evidence semantic identifier does not match its record" ,
},
],
},
});
});
test ( "reports browser origin scope mismatch at the caller parameter path" , async () => {
const browser = await startFakeCdpBrowser ();
try {
const provider = new CdpBrowserProvider ();
const input = inspectWebPageInputSchema . parse ({
cdp_endpoint : browser.endpoint ,
allowed_origins : [ browser . allowedOrigin ],
target_id : "allowed-page" ,
observation_ms : 0 ,
});
const observed = await provider . inspectPage ( input );
if ( ! observed . ok ) throw observed . error ;
const evidence = createBrowserEvidence (
"inspect_web_page" ,
input ,
observed . value ,
provider . identity (),
);
const invalidEvidence = createEvidence ( undefined , evidence . provider , {
predicateType : evidence.predicate_type ,
operation : evidence.operation ,
parameters : { ... evidence . parameters , allowed_origins : [] },
result : evidence.normalized_result ,
confidence : evidence.confidence ,
authority : evidence.authority ,
environment : evidence.environment ,
limitations : evidence.limitations ,
});
const result = reconcileJavaScriptRuntimeEvidence ({
static_layers : JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE.static_layers ,
runtime_observations : [ invalidEvidence ],
});
expect ( result ). toMatchObject ({
ok : false ,
error : {
_tag : "AnalysisInputError" ,
issues : [
{
path : [ "runtime_observations" , 0 , "parameters" , "allowed_origins" ],
reason : "invalid_value" ,
message :
"Browser Evidence target is outside its recorded origin scope" ,
},
],
},
});
} finally {
await browser . close ();
}
});
2026-10-09 20:02:52 +08:00
describe ( "Inspector execution-context lifecycle metadata" , () => {
test . each ([
[
"Runtime.executionContextDestroyed" ,
{ executionContextId : 1 },
"destroyed" ,
],
[ "Runtime.executionContextsCleared" , {}, "cleared" ],
] as const )(
"preserves observed context metadata after %s" ,
async ( method , params , state ) => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
runtimeEvents : [
{
method : "Runtime.executionContextCreated" ,
params : {
context : {
id : 1 ,
origin : "https://context.example" ,
name : "worker#one" ,
},
},
},
{ method , params },
],
});
try {
const result = await new V8InspectorProvider (). observe (
observeInput ( fake . endpoint , fake . targetId , "node" ),
);
if ( ! result . ok ) throw result . error ;
expect ( result . value . execution_contexts ). toEqual ([
{
context_key : "1" ,
state ,
name : "worker#one" ,
origin : "https://context.example" ,
},
]);
} finally {
await fake . close ();
}
},
);
test ( "replaces context metadata only when a new context creation is observed" , async () => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
runtimeEvents : [
{
method : "Runtime.executionContextCreated" ,
params : {
context : {
id : 1 ,
origin : "https://old.example" ,
name : "old context" ,
},
},
},
{
method : "Runtime.executionContextDestroyed" ,
params : { executionContextId : 1 },
},
{
method : "Runtime.executionContextCreated" ,
params : { context : { id : 1 , origin : "" , name : "" } },
},
{
method : "Runtime.executionContextDestroyed" ,
params : { executionContextId : 2 },
},
],
});
try {
const result = await new V8InspectorProvider (). observe (
observeInput ( fake . endpoint , fake . targetId , "node" ),
);
if ( ! result . ok ) throw result . error ;
expect ( result . value . execution_contexts ). toEqual ([
{ context_key : "1" , state : "created" , name : "" , origin : "" },
{ context_key : "2" , state : "destroyed" , name : null , origin : null },
]);
} finally {
await fake . close ();
}
});
2026-10-17 04:26:25 -07:00
test . each ([
[ "malformed" , "2" , "1" ],
[ "valid" , 2 , 1 ],
] as const )(
"accounts for %s context IDs in lifecycle events" ,
async ( mode , createdId , destroyedId ) => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
runtimeEvents : [
{
method : "Runtime.executionContextCreated" ,
params : { context : { id : 1 , name : "main" , origin : "" } },
},
{
method : "Runtime.executionContextCreated" ,
params : { context : { id : createdId , name : "worker" , origin : "" } },
},
{
method : "Runtime.executionContextDestroyed" ,
params : { executionContextId : destroyedId },
},
],
scriptEvents : [],
});
try {
const result = await new V8InspectorProvider (). observe (
observeInput ( fake . endpoint , fake . targetId , "node" ),
);
if ( ! result . ok ) throw result . error ;
const invalidContextUnknowns = result . value . unknowns . filter (( text ) =>
text . includes ( "malformed execution-context ID" ),
);
expect ( result . value . capture ). toMatchObject ({
events_observed : 3 ,
events_retained : 3 ,
events_dropped : 0 ,
truncated : false ,
});
if ( mode === "malformed" ) {
expect ( result . value . execution_contexts ). toEqual ([
{ context_key : "1" , state : "created" , name : "main" , origin : "" },
]);
expect ( invalidContextUnknowns ). toEqual ([
"2 execution-context lifecycle events with a missing or malformed execution-context ID were excluded; the affected contexts' lifecycle state is unknown." ,
]);
return ;
}
expect ( result . value . execution_contexts ). toEqual ([
{ context_key : "1" , state : "destroyed" , name : "main" , origin : "" },
{ context_key : "2" , state : "created" , name : "worker" , origin : "" },
]);
expect ( invalidContextUnknowns ). toEqual ([]);
} finally {
await fake . close ();
}
},
);
2026-10-09 20:02:52 +08:00
});
2026-10-09 21:01:52 +08:00
test ( "rejects unknown runtime observation fields" , () => {
expect (
observeJavaScriptRuntimeInputSchema . safeParse ({
inspector_endpoint : "http://127.0.0.1:9229" ,
target_id : "target-1" ,
unknown_field : true ,
}). success ,
). toBe ( false );
});
test ( "returns every target exposed by the selected Inspector endpoint inline" , async () => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
additionalTargetCount : 205 ,
2026-09-29 12:16:10 +08:00
});
2026-10-09 21:01:52 +08:00
try {
const listed = await new V8InspectorProvider (). listTargets ({
inspector_endpoint : fake.endpoint ,
});
if ( ! listed . ok ) throw listed . error ;
expect ( listed . value . targets ). toHaveLength ( 206 );
expect ( listed . value . targets [ 0 ] ? . target_id ). toBe ( fake . targetId );
} finally {
await fake . close ();
}
});
2026-09-29 12:16:10 +08:00
2026-10-12 12:36:39 +08:00
test . each ([
{ label : "omitted" , attached : undefined , expected : null },
{ label : "explicitly detached" , attached : false , expected : false },
] as const )(
"preserves $label V8 attachment state through listing and observation" ,
async ({ attached , expected }) => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
...( attached === undefined ? {} : { attached }),
});
try {
const provider = new V8InspectorProvider ();
const listed = await provider . listTargets ({
inspector_endpoint : fake.endpoint ,
});
if ( ! listed . ok ) throw listed . error ;
expect ( listed . value . targets [ 0 ] ? . attached ). toBe ( expected );
if ( expected === null )
expect (
listed . value . limitations . some (( item ) =>
item . includes ( "attachment state" ),
),
). toBe ( true );
const observed = await provider . observe (
observeInput ( fake . endpoint , fake . targetId , "node" ),
);
if ( ! observed . ok ) throw observed . error ;
expect ( observed . value . target . attached ). toBe ( expected );
if ( expected === null )
expect (
observed . value . limitations . some (( item ) =>
item . includes ( "attachment state" ),
),
). toBe ( true );
} finally {
await fake . close ();
}
},
);
test ( "rejects a V8 target explicitly marked attached" , async () => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
attached : true ,
});
try {
const result = await new V8InspectorProvider (). observe (
observeInput ( fake . endpoint , fake . targetId , "node" ),
);
expect ( result ). toMatchObject ({
ok : false ,
error : { reason : "target_not_allowed" },
});
expect ( fake . commands ). toEqual ([]);
} finally {
await fake . close ();
}
});
2026-10-09 21:01:52 +08:00
test ( "includes targets outside the old caller root filter" , async () => {
const fixture = await runtimeFixture ();
const outside = await temporaryFile ( "outside.js" );
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
additionalTargetUrl : pathToFileURL ( outside ). href ,
});
try {
const result = await new V8InspectorProvider (). listTargets ({
inspector_endpoint : fake.endpoint ,
2026-09-29 07:08:46 +08:00
});
2026-10-09 21:01:52 +08:00
if ( ! result . ok ) throw result . error ;
expect ( result . value . targets ). toHaveLength ( 2 );
expect ( result . value . targets . map (({ location }) => location )). toContainEqual (
expect . objectContaining ({ file_path : outside }),
);
} finally {
await fake . close ();
}
});
test ( "captures complete metadata with two enable commands" , async () => {
const fixture = await runtimeFixture ();
const outside = await temporaryFile ( "secret.js" );
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
scriptUrls : [
pathToFileURL ( fixture . entry ). href ,
"node:fs" ,
pathToFileURL ( outside ). href ,
],
2026-09-29 07:08:46 +08:00
});
2026-10-09 21:01:52 +08:00
try {
const result = await new V8InspectorProvider (). observe (
observeInput ( fake . endpoint , fake . targetId , "node" ),
);
if ( ! result . ok ) throw result . error ;
expect ( result . value . scripts . items ). toHaveLength ( 3 );
expect ( result . value . scripts . excluded . unsupported_location ). toBe ( 0 );
expect ( result . value . execution_contexts ). toEqual ([
{
context_key : "1" ,
state : "created" ,
name : "node[fixture]" ,
origin : "" ,
},
]);
expect ( new Set ( fake . commands . map (({ method }) => method ))). toEqual (
new Set ([ "Runtime.enable" , "Debugger.enable" ]),
);
expect ( JSON . stringify ( result . value )). toContain ( outside );
expect ( result . value . unavailable_without_instrumentation ). toContain (
"Electron IPC messages and handlers" ,
);
} finally {
await fake . close ();
}
});
2026-09-29 07:08:46 +08:00
2026-10-09 21:01:52 +08:00
test ( "excludes an Electron main target that reports only bare file://" , async () => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : "file://" ,
scriptUrls : [ pathToFileURL ( fixture . entry ). href ],
2026-07-24 13:16:35 +00:00
});
2026-10-09 21:01:52 +08:00
try {
const provider = new V8InspectorProvider ();
const listed = await provider . listTargets ({
inspector_endpoint : fake.endpoint ,
});
if ( ! listed . ok ) throw listed . error ;
expect ( listed . value . targets ). toEqual ([]);
2026-07-24 13:16:35 +00:00
2026-10-09 21:01:52 +08:00
const observed = await provider . observe (
observeInput ( fake . endpoint , fake . targetId , "electron-main" ),
);
expect ( observed . ok ). toBe ( false );
if ( observed . ok ) return ;
expect ( observed . error . message ). toMatch ( /target/u);
} finally {
await fake . close ();
}
});
test . each ([
[ "node" , "node" ],
[ "electron-main" , "node" ],
[ "electron-preload" , "page" ],
[ "electron-renderer" , "page" ],
] as const )(
"admits declared %s only on its protocol target family" ,
async ( runtimeKind , targetType ) => {
2026-07-24 13:16:35 +00:00
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
2026-10-09 21:01:52 +08:00
targetType ,
2026-07-24 13:16:35 +00:00
});
try {
const result = await new V8InspectorProvider (). observe (
2026-10-09 21:01:52 +08:00
observeInput ( fake . endpoint , fake . targetId , runtimeKind ),
2026-07-24 13:16:35 +00:00
);
2026-10-09 21:01:52 +08:00
expect ( result . ok ). toBe ( true );
2026-08-05 19:07:46 +08:00
} finally {
await fake . close ();
}
2026-10-09 21:01:52 +08:00
},
);
2026-07-24 13:16:35 +00:00
2026-09-29 14:53:50 +08:00
describe ( "complete Inspector script hashes" , () => {
test ( "keeps more than one hundred long script hashes distinct" , async () => {
const fixture = await runtimeFixture ();
const url = pathToFileURL ( fixture . entry ). href ;
const prefix = "h" . repeat ( 512 );
const scriptHashes = Array . from (
{ length : 101 },
( _ , index ) => ` ${ prefix }${ String ( index ) } ` ,
);
const fake = await startFakeV8Inspector ({
targetUrl : url ,
scriptUrls : scriptHashes.map (() => url ),
scriptHashes ,
});
try {
2026-10-09 05:09:18 +08:00
const result = await new V8InspectorProvider (). observe ({
... observeInput ( fake . endpoint , fake . targetId , "node" ),
observation_ms : 1_000 ,
});
2026-10-09 03:13:43 +08:00
if ( ! result . ok ) throw result . error ;
2026-09-29 14:53:50 +08:00
expect ( result . value . scripts . items ). toHaveLength ( scriptHashes . length );
expect (
new Set ( result . value . scripts . items . map (({ cdp_hash }) => cdp_hash )),
). toEqual ( new Set ( scriptHashes ));
} finally {
await fake . close ();
}
});
});
2026-09-29 08:20:18 +08:00
describe ( "undeclared V8 runtime role" , () => {
test ( "observes a target without inventing a runtime role" , async () => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
targetType : "page" ,
});
try {
2026-09-29 16:28:46 +08:00
const input = observeInput ( fake . endpoint , fake . targetId , "node" );
2026-09-29 08:20:18 +08:00
delete input . runtime_kind ;
const result = await new V8InspectorProvider (). observe ( input );
2026-10-09 03:13:43 +08:00
if ( ! result . ok ) throw result . error ;
2026-09-29 08:20:18 +08:00
expect ( result . value . target ). toMatchObject ({
protocol_type : "page" ,
runtime_kind : "unknown" ,
runtime_kind_authority : "not-declared" ,
});
} finally {
await fake . close ();
}
});
});
2026-10-09 05:09:18 +08:00
describe ( "V8 Inspector script retention" , () => {
2026-09-29 14:53:50 +08:00
test ( "retains authorized script locations longer than the former byte ceiling" , async () => {
const longUrl = `https://example.test/ ${ "a" . repeat ( 20 _000 ) } ` ;
const fake = await startFakeV8Inspector ({
targetUrl : longUrl ,
scriptUrls : [ longUrl ],
});
try {
const input = {
2026-09-29 16:28:46 +08:00
... observeInput ( fake . endpoint , fake . targetId , "node" ),
2026-09-29 14:53:50 +08:00
};
const result = await new V8InspectorProvider (). observe ( input );
2026-10-09 03:13:43 +08:00
if ( ! result . ok ) throw result . error ;
2026-09-29 14:53:50 +08:00
expect ( result . value . scripts . items ). toHaveLength ( 1 );
expect ( result . value . scripts . items [ 0 ] ? . location ). toMatchObject ({
kind : "url" ,
origin : "https://example.test" ,
sanitized_url : longUrl ,
});
} finally {
await fake . close ();
}
});
2026-09-29 13:02:30 +08:00
test ( "returns every authorized script beyond the former collection ceiling" , async () => {
2026-07-24 13:16:35 +00:00
const fixture = await runtimeFixture ();
2026-09-29 13:02:30 +08:00
const scriptUrls = [
pathToFileURL ( fixture . entry ). href ,
... Array . from ({ length : 2_004 }, () => pathToFileURL ( fixture . entry ). href ),
];
2026-07-24 13:16:35 +00:00
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
2026-09-29 13:02:30 +08:00
scriptUrls ,
2026-07-24 13:16:35 +00:00
});
try {
2026-09-30 00:41:05 +08:00
const input = {
... observeInput ( fake . endpoint , fake . targetId , "node" ),
observation_ms : 1_000 ,
};
2026-09-29 13:02:30 +08:00
const result = await new V8InspectorProvider (). observe ( input );
2026-10-09 03:13:43 +08:00
if ( ! result . ok ) throw result . error ;
2026-09-29 13:02:30 +08:00
expect ( result . value . capture . truncated ). toBe ( false );
expect ( result . value . capture . truncation_reasons ). toEqual ([]);
expect ( result . value . capture . events_dropped ). toBe ( 0 );
expect ( result . value . scripts . items ). toHaveLength ( scriptUrls . length );
2026-07-24 13:16:35 +00:00
} finally {
await fake . close ();
}
});
2026-10-09 05:09:18 +08:00
});
describe ( "bounded V8 Inspector capture" , () => {
test ( "bounds aggregate script and context metadata while retaining a useful partial capture" , async () => {
const fixture = await runtimeFixture ();
const url = pathToFileURL ( fixture . entry ). href ;
const scriptHashes = Array . from (
{ length : 2_300 },
( _ , index ) => ` ${ "h" . repeat ( 4 _096 ) }${ String ( index ) } ` ,
);
const fake = await startFakeV8Inspector ({
targetUrl : url ,
scriptUrls : scriptHashes.map (() => url ),
scriptHashes ,
contextTransitionCount : 2_000 ,
});
try {
const result = await new V8InspectorProvider (). observe ({
... observeInput ( fake . endpoint , fake . targetId , "node" ),
observation_ms : 1_000 ,
});
if ( ! result . ok ) throw result . error ;
expect ( result . value . capture . truncated ). toBe ( true );
expect ( result . value . capture . truncation_reasons ). toEqual ([
"retained_metadata_budget_exceeded" ,
]);
expect ( result . value . capture . events_dropped ). toBeGreaterThan ( 0 );
expect ( result . value . capture . metadata_bytes_retained ). toBeLessThanOrEqual (
8 * 1024 * 1024 ,
);
expect ( result . value . capture . events_observed ). toBeGreaterThan (
result . value . capture . events_retained ,
);
expect ( result . value . execution_contexts ). toHaveLength ( 1 );
} finally {
await fake . close ();
}
});
2026-07-24 13:16:35 +00:00
2026-10-09 05:09:18 +08:00
test ( "rejects a CDP event larger than the configured transport payload budget" , async () => {
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
oversizedEventBytes : 16 * 1024 * 1024 ,
});
try {
const result = await new V8InspectorProvider (). observe (
observeInput ( fake . endpoint , fake . targetId , "node" ),
);
expect ( result . ok ). toBe ( false );
if ( result . ok ) return ;
expect ( result . error ). toMatchObject ({
_tag : "BrowserObservationError" ,
reason : "payload_limit" ,
});
expect ( result . error . userMessage ). toContain ( "protocol budget" );
} finally {
await fake . close ();
}
});
});
describe ( "passive V8 Inspector evidence" , () => {
2026-10-05 20:16:44 +08:00
test ( "produces deterministic Evidence through the service without grants" , async () => {
2026-07-24 13:16:35 +00:00
const fixture = await runtimeFixture ();
const fake = await startFakeV8Inspector ({
targetUrl : pathToFileURL ( fixture . entry ). href ,
});
try {
2026-09-29 16:28:46 +08:00
const input = observeInput ( fake . endpoint , fake . targetId , "node" );
2026-07-24 13:16:35 +00:00
const first = await observeJavaScriptRuntime (
new V8InspectorProvider (),
input ,
);
const second = await observeJavaScriptRuntime (
new V8InspectorProvider (),
input ,
);
expect ( first . ok ). toBe ( true );
expect ( second . ok ). toBe ( true );
if ( ! first . ok || ! second . ok ) return ;
expect ( second . value . evidence_id ). toBe ( first . value . evidence_id );
const listed = await listJavaScriptRuntimeTargets (
new V8InspectorProvider (),
{
inspector_endpoint : fake.endpoint ,
},
);
expect ( listed . ok ). toBe ( true );
} finally {
await fake . close ();
}
});
test ( "reconciles V8 script presence with static Application Graph Evidence" , () => {
const root = "/Applications/Example.app/Contents/Resources/app" ;
const observation = runtimeObservation (
` ${ root } /index.html` ,
` ${ root } /renderer.js` ,
);
const runtimeEvidence = createJavaScriptRuntimeObservationEvidence (
"observe_javascript_runtime" ,
{
inspector_endpoint : "http://127.0.0.1:9229" ,
target_id : "example-v8-target" ,
runtime_kind : "electron-main" ,
observation_ms : 100 ,
},
observation ,
V8_INSPECTOR_PROVIDER_IDENTITY ,
);
const reconciled = reconcileJavaScriptRuntimeEvidence ({
static_layers : JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE.static_layers ,
runtime_observations : [ runtimeEvidence ],
});
2026-10-09 03:13:43 +08:00
if ( ! reconciled . ok ) throw reconciled . error ;
2026-07-24 13:16:35 +00:00
const result = javascriptRuntimeReconciliationResultSchema . parse (
reconciled . value . normalized_result ,
);
expect ( result . runtime_captures [ 0 ] ? . kind ). toBe ( "v8-inspector" );
expect ( result . summary . runtime_scripts ). toBe ( 1 );
expect ( result . summary . matched ). toBeGreaterThan ( 0 );
});
2026-10-09 23:16:42 +08:00
test ( "keeps unverified partial script URLs unresolved during reconciliation" , () => {
const rawUrl = "file:///tmp/not-verified.js?caller-value=kept" ;
const resolvedObservation = runtimeObservation (
"/Applications/Example.app/Contents/Resources/app/index.html" ,
"/Applications/Example.app/Contents/Resources/app/renderer.js" ,
);
const resolvedScript = resolvedObservation . scripts . items [ 0 ];
if ( resolvedScript === undefined )
throw new Error ( "Expected the runtime fixture to have one script" );
const observation : JavaScriptRuntimeObservation = {
... resolvedObservation ,
scripts : {
... resolvedObservation . scripts ,
items : [
... resolvedObservation . scripts . items . map (( script ) => ({
... script ,
location : {
kind : "unresolved" as const ,
reported_url : rawUrl ,
reason : "location-authorization-not-attempted" as const ,
},
})),
{
... resolvedScript ,
script_key : `v8_script_ ${ "5" . repeat ( 64 ) } ` ,
location : {
kind : "unresolved" ,
reported_url : "" ,
reason : "location-authorization-not-attempted" ,
},
cdp_hash : null ,
length : 0 ,
},
],
},
};
const runtimeEvidence = createJavaScriptRuntimeObservationEvidence (
"observe_javascript_runtime" ,
{
inspector_endpoint : "http://127.0.0.1:9229" ,
target_id : "example-v8-target" ,
runtime_kind : "electron-main" ,
observation_ms : 100 ,
},
observation ,
V8_INSPECTOR_PROVIDER_IDENTITY ,
);
const reconciled = reconcileJavaScriptRuntimeEvidence ({
static_layers : JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE.static_layers ,
runtime_observations : [ runtimeEvidence ],
});
if ( ! reconciled . ok ) throw reconciled . error ;
const result = javascriptRuntimeReconciliationResultSchema . parse (
reconciled . value . normalized_result ,
);
expect ( result . summary . runtime_scripts ). toBe ( 2 );
expect ( result . reconciliations ). toContainEqual (
expect . objectContaining ({
entity_kind : "script" ,
status : "unknown" ,
reason : "runtime-location-unresolved" ,
}),
);
expect ( JSON . stringify ( result . graph )). toContain ( rawUrl );
expect ( JSON . stringify ( result . graph )). toContain ( "v8-hash" );
expect ( JSON . stringify ( result . graph )). toContain ( '"reported_url":""' );
});
2026-07-24 13:16:35 +00:00
});
const runtimeFixture = async () => {
2026-07-29 22:00:38 +00:00
const root = await createTestTempDirectory ( "rea-v8-runtime-" );
2026-07-24 13:16:35 +00:00
const entry = join ( root , "entry.js" );
await writeFile ( entry , "export const value = 1;\n" );
return { root , entry };
};
const temporaryFile = async ( name : string ) : Promise < string > => {
2026-07-29 22:00:38 +00:00
const root = await createTestTempDirectory ( "rea-v8-outside-" );
2026-07-24 13:16:35 +00:00
const path = join ( root , name );
await writeFile ( path , "export {};\n" );
return path ;
};
const observeInput = (
endpoint : string ,
targetId : string ,
runtimeKind : ObserveJavaScriptRuntimeInput [ "runtime_kind" ],
) : ObserveJavaScriptRuntimeInput => ({
inspector_endpoint : endpoint ,
target_id : targetId ,
runtime_kind : runtimeKind ,
observation_ms : 10 ,
});
const runtimeObservation = (
targetPath : string ,
scriptPath : string ,
) : JavaScriptRuntimeObservation => ({
runtime : {
product : "node.js/v24.4.1" ,
protocol_version : "1.3" ,
v8_version : "13.6" ,
},
target : {
target_id : "example-v8-target" ,
protocol_type : "node" ,
attached : false ,
location : { kind : "file" , file_path : targetPath },
runtime_kind : "electron-main" ,
runtime_kind_authority : "caller-declared-unverified" ,
},
capture : {
observation_ms : 100 ,
events_observed : 1 ,
events_retained : 1 ,
events_dropped : 0 ,
metadata_bytes_retained : 100 ,
truncated : false ,
truncation_reasons : [],
},
scripts : {
items : [
{
script_key : `v8_script_ ${ "4" . repeat ( 64 ) } ` ,
location : { kind : "file" , file_path : scriptPath },
execution_context_key : "1" ,
cdp_hash : "v8-hash" ,
length : 29 ,
is_module : true ,
status : "observed-loaded" ,
},
],
observed_total : 1 ,
excluded : {
unsupported_location : 0 ,
invalid_protocol_value : 0 ,
},
},
execution_contexts : [],
directly_observed : [ "Debugger.scriptParsed established script presence." ],
unavailable_without_instrumentation : [
"require/import caller-to-callee edges" ,
],
unknowns : [ "Capture is bounded." ],
limitations : [ "Passive Inspector metadata only." ],
});