Files
rea/src/domain/managedRuntimeCorrelation.ts
T

218 lines
7.6 KiB
TypeScript

import { createHash } from "node:crypto";
import canonicalize from "canonicalize";
import { z } from "zod";
import { evidenceSchema, parseEvidence } from "./evidence.js";
import {
cliMetadataGuidSchema,
managedMemberInspectionSchema,
} from "./managedArtifact.js";
import type { JsonValue } from "./jsonValue.js";
import { digestSchema } from "./../domain/digests.js";
import { prefixedDigestSchema } from "./../domain/digests.js";
const evidenceIdSchema = prefixedDigestSchema("ev");
const tokenSchema = z.string().regex(/^0x[0-9a-f]{8}$/u);
const boundedTextSchema = z.string().min(1);
const managedRuntimeEffectSchema = z.enum([
"attach",
"load",
"debugger",
"reflection",
"instrumentation",
]);
const managedRuntimeHostSchema = z.strictObject({
os: z.enum(["linux", "macos", "windows"]),
clr_family: z.enum(["dotnet", "dotnet-framework", "mono", "unknown"]),
architecture: z.enum(["x86", "x86_64", "arm", "arm64"]),
});
const managedRuntimeBoundsSchema = z.strictObject({
timeout_ms: z.number().int().min(1).default(5_000),
max_threads: z.number().int().min(1).default(32),
max_output_bytes: z.number().int().min(0).default(65_536),
allow_network: z.literal(false).default(false),
allow_ui: z.literal(false).default(false),
});
export const managedRuntimeCorrelationInputSchema = z.strictObject({
static_members: evidenceSchema,
method: z.strictObject({
token: tokenSchema,
signature_sha256: digestSchema,
normalized_il_sha256: digestSchema.nullable(),
}),
requested_effect: managedRuntimeEffectSchema,
host: managedRuntimeHostSchema,
bounds: managedRuntimeBoundsSchema.default({
timeout_ms: 5_000,
max_threads: 32,
max_output_bytes: 65_536,
allow_network: false,
allow_ui: false,
}),
});
export const managedRuntimeCorrelationResultSchema = z.strictObject({
correlation_id: prefixedDigestSchema("mrc"),
phase: z.literal("admission-plan"),
executed: z.literal(false),
authority_model: z.strictObject({
capability: z.literal("managed_runtime"),
permission_grant_id: z.string().min(1),
default_enabled: z.literal(false),
per_call_approval_required: z.literal(true),
}),
static_observation: z.strictObject({
evidence_id: evidenceIdSchema,
artifact_sha256: digestSchema,
artifact_path: z.string().min(1),
byte_length: z.number().int().min(0),
module_name: z.string().nullable(),
mvid: cliMetadataGuidSchema.nullable(),
metadata_status: z.enum(["absent", "complete", "partial", "malformed"]),
}),
method_lock: z.strictObject({
token: tokenSchema,
declaring_type: z.string().nullable(),
name: z.string(),
signature_sha256: digestSchema,
normalized_il_sha256: digestSchema.nullable(),
body_sha256: digestSchema.nullable(),
il_size: z.number().int().min(0),
body_status: z.enum([
"present",
"partial",
"absent",
"malformed",
"too-large",
]),
exact_build_required: z.literal(true),
}),
requested_runtime: z.strictObject({
effect: managedRuntimeEffectSchema,
host: managedRuntimeHostSchema,
executable_path: z.string().min(1),
network: z.literal("none"),
filesystem: z.literal("owned-artifacts-only"),
}),
effect_taxonomy: z.strictObject({
attaches_process: z.boolean(),
loads_target: z.boolean(),
uses_debugger: z.boolean(),
uses_reflection: z.boolean(),
instruments_code: z.boolean(),
invokes_target_code: z.literal(false),
}),
bounds: managedRuntimeBoundsSchema,
unsupported_until_executor_exists: z.literal(true),
evidence_links: z.array(evidenceIdSchema).length(1),
limitations: z.array(boundedTextSchema),
});
export type ManagedRuntimeCorrelationInput = z.infer<
typeof managedRuntimeCorrelationInputSchema
>;
export type ManagedRuntimeCorrelationResult = z.infer<
typeof managedRuntimeCorrelationResultSchema
>;
const sha256 = (value: JsonValue): string => {
const serialized = canonicalize(value);
if (serialized === undefined)
throw new TypeError("Managed runtime correlation canonicalization failed");
return createHash("sha256").update(serialized).digest("hex");
};
/** Authenticate static managed Evidence and build a non-executing runtime plan. */
export const planManagedRuntimeCorrelation = (
input: ManagedRuntimeCorrelationInput,
executablePath: string,
permissionGrantId: string,
): ManagedRuntimeCorrelationResult => {
const staticEvidence = parseEvidence(input.static_members);
if (staticEvidence.operation !== "inspect_managed_members")
throw new TypeError("Evidence operation is not inspect_managed_members");
const members = managedMemberInspectionSchema.parse(
staticEvidence.normalized_result,
);
const method = members.methods.find(
(item) =>
item.token === input.method.token &&
item.signature.raw_sha256 === input.method.signature_sha256 &&
item.body.status === "present" &&
item.body.normalized_il_sha256 !== null &&
input.method.normalized_il_sha256 !== null &&
item.body.normalized_il_sha256 === input.method.normalized_il_sha256,
);
if (method === undefined)
throw new TypeError(
"Requested method lock does not match a static managed member observation",
);
const withoutId = {
phase: "admission-plan" as const,
executed: false as const,
authority_model: {
capability: "managed_runtime" as const,
permission_grant_id: permissionGrantId,
default_enabled: false as const,
per_call_approval_required: true as const,
},
static_observation: {
evidence_id: staticEvidence.evidence_id,
artifact_sha256: members.artifact.sha256,
artifact_path: members.artifact.path,
byte_length: members.artifact.byte_length,
module_name: members.module?.name ?? null,
mvid: members.module?.mvid ?? null,
metadata_status: members.metadata.status,
},
method_lock: {
token: method.token,
declaring_type: method.declaring_type,
name: method.name,
signature_sha256: method.signature.raw_sha256,
normalized_il_sha256: method.body.normalized_il_sha256,
body_sha256: method.body.il_sha256,
il_size: method.body.il_size,
body_status: method.body.status,
exact_build_required: true as const,
},
requested_runtime: {
effect: input.requested_effect,
host: input.host,
executable_path: executablePath,
network: "none" as const,
filesystem: "owned-artifacts-only" as const,
},
effect_taxonomy: effectTaxonomy(input.requested_effect),
bounds: input.bounds,
unsupported_until_executor_exists: true as const,
evidence_links: [staticEvidence.evidence_id],
limitations: [
"This operation admits and records a runtime-correlation plan only; it did not attach, load, debug, reflect, instrument, invoke, or execute target code.",
"Runtime agreement would be a separate runtime observation and would not retroactively turn static inference into a byte observation.",
"The exact artifact SHA-256, MVID, method signature, and normalized IL shape must match before any future executor can run.",
"Network and UI effects are not admitted by this contract.",
],
};
return managedRuntimeCorrelationResultSchema.parse({
...withoutId,
correlation_id: `mrc_${sha256(withoutId)}`,
});
};
const effectTaxonomy = (
effect: ManagedRuntimeCorrelationInput["requested_effect"],
): ManagedRuntimeCorrelationResult["effect_taxonomy"] => ({
attaches_process: effect === "attach",
loads_target: effect === "load" || effect === "reflection",
uses_debugger: effect === "debugger",
uses_reflection: effect === "reflection",
instruments_code: effect === "instrumentation",
invokes_target_code: false,
});