fix(runtime): make observed target role optional
This commit is contained in:
1 parent
0ae3c0837b
commit
593a8e12ce
8 files changed
+56
-13
No files matched your search
@@ -52,7 +52,9 @@ const parameters = (
|
||||
...("target_id" in input
|
||||
? {
|
||||
target_id: input.target_id,
|
||||
runtime_kind: input.runtime_kind,
|
||||
...(input.runtime_kind === undefined
|
||||
? {}
|
||||
: { runtime_kind: input.runtime_kind }),
|
||||
observation_ms: input.observation_ms,
|
||||
limits: input.limits,
|
||||
}
|
||||
|
||||
@@ -96,8 +96,11 @@ export const finalizeInspectorCapture = async ({
|
||||
protocol_type: target.type,
|
||||
attached: target.attached,
|
||||
location: target.location,
|
||||
runtime_kind: input.runtime_kind,
|
||||
runtime_kind_authority: "caller-declared-unverified",
|
||||
runtime_kind: input.runtime_kind ?? "unknown",
|
||||
runtime_kind_authority:
|
||||
input.runtime_kind === undefined
|
||||
? "not-declared"
|
||||
: "caller-declared-unverified",
|
||||
},
|
||||
capture: {
|
||||
observation_ms: input.observation_ms,
|
||||
|
||||
@@ -140,7 +140,8 @@ export class V8InspectorProvider implements JavaScriptRuntimeObservationPort {
|
||||
options.signal,
|
||||
);
|
||||
const target = await authorizedTarget(discovery.targets, input, roots);
|
||||
assertRuntimeKind(target, input.runtime_kind);
|
||||
if (input.runtime_kind !== undefined)
|
||||
assertRuntimeKind(target, input.runtime_kind);
|
||||
connection = await CdpConnection.connect(
|
||||
target.webSocketUrl,
|
||||
"observe_javascript_runtime",
|
||||
|
||||
@@ -41,9 +41,11 @@ const listOptionsSchema = z.object({ ...scopeOptions });
|
||||
|
||||
const observeOptionsSchema = z.object({
|
||||
...scopeOptions,
|
||||
runtimeKind: javascriptRuntimeKindSchema.describe(
|
||||
"Declared target role; Inspector cannot authenticate the Electron role",
|
||||
),
|
||||
runtimeKind: javascriptRuntimeKindSchema
|
||||
.optional()
|
||||
.describe(
|
||||
"Declared target role; Inspector cannot authenticate the Electron role",
|
||||
),
|
||||
observationMs: z
|
||||
.number()
|
||||
.int()
|
||||
|
||||
@@ -49,7 +49,6 @@ export const JAVASCRIPT_RUNTIME_OBSERVATION_TOOL_CONTRACTS = [
|
||||
allowed_file_roots: [root],
|
||||
allowed_origins: [],
|
||||
target_id: "TARGET_ID_FROM_LIST_JAVASCRIPT_RUNTIME_TARGETS",
|
||||
runtime_kind: "node",
|
||||
approved: true,
|
||||
observation_ms: 100,
|
||||
limits: {
|
||||
|
||||
@@ -69,6 +69,9 @@ export const javascriptRuntimeKindSchema = z.enum([
|
||||
"electron-preload",
|
||||
"electron-renderer",
|
||||
]);
|
||||
const observedJavaScriptRuntimeKindSchema = javascriptRuntimeKindSchema.or(
|
||||
z.literal("unknown"),
|
||||
);
|
||||
|
||||
export const javascriptRuntimeObservationLimitsSchema = z.strictObject({
|
||||
max_events: z.number().int().min(1).max(50_000).default(10_000),
|
||||
@@ -88,7 +91,7 @@ export const observeJavaScriptRuntimeInputSchema = z
|
||||
.strictObject({
|
||||
...approvedRuntimeScope,
|
||||
target_id: z.string().trim().min(1).max(256),
|
||||
runtime_kind: javascriptRuntimeKindSchema,
|
||||
runtime_kind: javascriptRuntimeKindSchema.optional(),
|
||||
observation_ms: z.number().int().min(0).max(10_000).default(100),
|
||||
limits: javascriptRuntimeObservationLimitsSchema.default({
|
||||
max_events: 10_000,
|
||||
@@ -173,8 +176,11 @@ const javascriptRuntimeContextSchema = z.strictObject({
|
||||
export const javascriptRuntimeObservationSchema = z.strictObject({
|
||||
runtime: javascriptRuntimeVersionSchema,
|
||||
target: javascriptRuntimeTargetSchema.extend({
|
||||
runtime_kind: javascriptRuntimeKindSchema,
|
||||
runtime_kind_authority: z.literal("caller-declared-unverified"),
|
||||
runtime_kind: observedJavaScriptRuntimeKindSchema,
|
||||
runtime_kind_authority: z.enum([
|
||||
"caller-declared-unverified",
|
||||
"not-declared",
|
||||
]),
|
||||
}),
|
||||
capture: z.strictObject({
|
||||
observation_ms: z.number().int().min(0),
|
||||
|
||||
@@ -284,13 +284,14 @@ const assertV8RuntimeParameters = (
|
||||
.max(32),
|
||||
allowed_origins: z.array(browserOriginSchema).max(32),
|
||||
target_id: z.string().trim().min(1).max(256),
|
||||
runtime_kind: javascriptRuntimeKindSchema,
|
||||
runtime_kind: javascriptRuntimeKindSchema.optional(),
|
||||
})
|
||||
.passthrough()
|
||||
.parse(evidence.parameters);
|
||||
if (
|
||||
parameters.target_id !== result.target.target_id ||
|
||||
parameters.runtime_kind !== result.target.runtime_kind
|
||||
(parameters.runtime_kind !== undefined &&
|
||||
parameters.runtime_kind !== result.target.runtime_kind)
|
||||
)
|
||||
throw new TypeError(
|
||||
"Runtime Evidence target or declared role disagrees with its result",
|
||||
|
||||
@@ -172,6 +172,35 @@ describe("passive V8 Inspector provider", () => {
|
||||
);
|
||||
});
|
||||
|
||||
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 {
|
||||
const input = observeInput(
|
||||
fake.endpoint,
|
||||
fake.targetId,
|
||||
fixture.root,
|
||||
"node",
|
||||
);
|
||||
delete input.runtime_kind;
|
||||
const result = await new V8InspectorProvider().observe(input);
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(result.value.target).toMatchObject({
|
||||
protocol_type: "page",
|
||||
runtime_kind: "unknown",
|
||||
runtime_kind_authority: "not-declared",
|
||||
});
|
||||
} finally {
|
||||
await fake.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("passive V8 Inspector evidence", () => {
|
||||
test("marks capture truncation instead of claiming complete coverage", async () => {
|
||||
const fixture = await runtimeFixture();
|
||||
|
||||
Reference in new issue
Block a user