chore: integrate portable schemas and PID target contracts

Keep one producer-owned PID syscall catalog while incorporating the portable
canonical-base64 schema fix. Consolidate equivalent resource cases around the
stronger read/write, failed-attempt, self and distinct-PID controls.

The integrated policy and base64 families pass 91 tests across four files.

Assisted-by: Reimu (gensokyo-skills:reimu-incident-triage)
This commit is contained in:
gensokyo-dev committed 2026-10-19 15:39:50 +00:00
commit 1c73ea7e4e
7 files changed
+137 -32

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
import { Ajv2020 } from "ajv/dist/2020.js";
import { expect, it } from "vitest";
import { z } from "zod";
import { canonicalBase64Schema } from "./base64Schema.js";
import { CANONICAL_BASE64_PATTERN } from "./stringPatterns.js";
it("keeps runtime and projected canonical base64 contracts equivalent", () => {
const samples = [
"",
"AA==",
"AAA=",
"////",
"AB==",
"AAB=",
"A",
"AA",
"AAA",
"====",
"AA==\n",
"AA==\r\n",
"AA==\u2028",
"AA==\u2029",
"AA\nAA",
"AA=\n=",
"AA-_",
"a b c",
42,
null,
];
for (const io of ["input", "output"] as const) {
const projected = z.toJSONSchema(canonicalBase64Schema, { io });
const validate = new Ajv2020({ strict: true }).compile(projected);
expect(JSON.stringify(projected)).not.toMatch(/\(\?<?[=!]/u);
for (const sample of samples) {
const expected =
typeof sample === "string" && CANONICAL_BASE64_PATTERN.test(sample);
expect(
canonicalBase64Schema.safeParse(sample).success,
String(sample),
).toBe(expected);
expect(validate(sample), JSON.stringify({ io, sample })).toBe(expected);
}
}
});
it("validates large projected bodies without Unicode-set regexp stack exhaustion", () => {
const encoded = Buffer.alloc(24 * 1024 * 1024 - 3, 0x81).toString("base64");
const projected = z.toJSONSchema(canonicalBase64Schema);
const validate = new Ajv2020({ strict: true }).compile(projected);
const samples = [
{ value: encoded, expected: true },
{ value: `${encoded.slice(0, -1)}!`, expected: false },
{ value: `${encoded.slice(0, -3)}R==`, expected: false },
{ value: `${encoded}A`, expected: false },
];
for (const { value, expected } of samples) {
expect(canonicalBase64Schema.safeParse(value).success).toBe(expected);
expect(validate(value)).toBe(expected);
for (const flag of ["", "u", "v"]) {
const positive = new RegExp(projected.pattern!, flag);
const negative = new RegExp(
(projected.not as { pattern: string }).pattern,
flag,
);
expect(positive.test(value) && !negative.test(value), flag).toBe(
expected,
);
}
}
});
+16
View File
@@ -0,0 +1,16 @@
import { z } from "zod";
import {
BASE64_QUARTETS_PATTERN,
CANONICAL_BASE64_INVALID_PATTERN,
} from "./stringPatterns.js";
/** Exact canonical base64 using stack-bounded, portable JSON Schema patterns. */
export const canonicalBase64Schema = z
.string()
.regex(BASE64_QUARTETS_PATTERN)
.refine((value) => !CANONICAL_BASE64_INVALID_PATTERN.test(value), {
message: "Expected canonical padded base64",
})
// JSON Schema negation expresses the same rejection without look-around.
.meta({ not: { pattern: CANONICAL_BASE64_INVALID_PATTERN.source } });
+4 -6
View File
@@ -1,7 +1,7 @@
import { z } from "zod";
import { digestSchema } from "../digests.js";
import { localPathStringSchema } from "../localPath.js";
import { CANONICAL_BASE64_PATTERN } from "../stringPatterns.js";
import { canonicalBase64Schema } from "../base64Schema.js";
const sourceRangeSchema = z.strictObject({
offset: z.number().int().nonnegative(),
@@ -25,9 +25,7 @@ export const goModuleMetadataSchema = z.strictObject({
dependencies: z.array(goModuleSchema),
settings: z.array(z.strictObject({ key: z.string(), value: z.string() })),
unparsed_lines: z.array(z.string()),
unparsed_line_bytes_base64: z.array(
z.string().regex(CANONICAL_BASE64_PATTERN),
),
unparsed_line_bytes_base64: z.array(canonicalBase64Schema),
complete: z.boolean(),
});
@@ -43,9 +41,9 @@ export const goBuildInfoSchema = z.strictObject({
header_offset: z.number().int().nonnegative(),
encoding: z.enum(["inline", "pointer"]),
go_version: z.string().nullable(),
go_version_bytes_base64: z.string().regex(CANONICAL_BASE64_PATTERN),
go_version_bytes_base64: canonicalBase64Schema,
module_text: z.string().nullable(),
module_bytes_base64: z.string().regex(CANONICAL_BASE64_PATTERN),
module_bytes_base64: canonicalBase64Schema,
version_location: sourceRangeSchema,
module_location: sourceRangeSchema,
module: goModuleMetadataSchema,
+3 -4
View File
@@ -2,16 +2,15 @@ import { z } from "zod";
import { canBorrowParsedJsonSnapshot } from "../immutableJson.js";
import { localPathStringSchema } from "../localPath.js";
import { CANONICAL_BASE64_PATTERN } from "../stringPatterns.js";
import { canonicalBase64Schema } from "../base64Schema.js";
const unsignedHex = z.string().regex(/^0x(?:0|[1-9a-f][0-9a-f]{0,15})$/);
const index = z.number().int().nonnegative();
const scalar = z.union([z.string(), z.number().int()]);
const range = z.strictObject({ offset: unsignedHex, bytes: unsignedHex });
const canonicalBase64 = z.string().regex(CANONICAL_BASE64_PATTERN);
const name = z.strictObject({
display: z.string(),
bytes_base64: canonicalBase64.nullable(),
bytes_base64: canonicalBase64Schema.nullable(),
location: range.nullable(),
unknown_reason: z.string().nullable(),
});
@@ -151,7 +150,7 @@ const binaryLayoutObjectSchema = z.strictObject({
z.strictObject({
section_index: index,
location: range,
encoded_bytes_base64: canonicalBase64,
encoded_bytes_base64: canonicalBase64Schema,
entries: z.array(
z.strictObject({ decoded_index: index, reported_offset: unsignedHex }),
),
+4 -5
View File
@@ -2,13 +2,12 @@ import { z } from "zod";
import type { BinaryArchitecture } from "../binaryTargetTypes.js";
import { localPathStringSchema } from "../localPath.js";
import { CANONICAL_BASE64_PATTERN } from "../stringPatterns.js";
import { canonicalBase64Schema } from "../base64Schema.js";
import { recordedNoteCoverageIssues } from "./recordedCrashNoteCoverage.js";
const hex = z.string().regex(/^0x(?:0|[1-9a-f][0-9a-f]{0,15})$/);
const index = z.number().int().nonnegative();
const signed32 = z.number().int().min(-2147483648).max(2147483647);
const base64 = z.string().regex(CANONICAL_BASE64_PATTERN);
const range = z.strictObject({ offset: hex, bytes: hex });
const diagnostics = z.strictObject({
stdout: z.string(),
@@ -222,17 +221,17 @@ const recordedCrashObjectSchema = z.strictObject({
type: z.union([z.string(), index]),
location: range,
owner_location: range,
owner_bytes_base64: base64,
owner_bytes_base64: canonicalBase64Schema,
owner_display: z.string().nullable(),
descriptor_location: range,
descriptor_bytes_base64: base64,
descriptor_bytes_base64: canonicalBase64Schema,
}),
),
note_padding: z.array(
z.strictObject({
segment_index: index,
location: range,
bytes_base64: base64,
bytes_base64: canonicalBase64Schema,
}),
),
threads: z.array(
+16 -7
View File
@@ -5,16 +5,25 @@ export const IDENTIFIER_PATTERN = /^[A-Za-z][A-Za-z0-9._\x2d]*$/u;
export const STABLE_IDENTIFIER_PATTERN = /^[A-Za-z][A-Za-z0-9._:\x2f\x2d]*$/u;
// Repeated alphabet quartets exhaust V8's regexp stack on large bodies,
// including under JSON Schema's Unicode-set mode. Check alphabet and terminal
// padding separately, leaving an unconstrained fixed-width loop for length.
const PADDED_BASE64_PREFIX = String.raw`^(?!.*[^A-Za-z0-9+\x2f=])(?!.*=[^=])(?!.*===)`;
// especially under JSON Schema's Unicode-set mode. Check invalid content
// separately from an unconstrained fixed-width loop for length.
export const BASE64_QUARTETS_PATTERN = /^(?:....)*$/;
const PADDED_BASE64_INVALID_PATTERN = /[^A-Za-z0-9+\x2f=]|=[^=]|===/;
export const CANONICAL_BASE64_INVALID_PATTERN = new RegExp(
`${PADDED_BASE64_INVALID_PATTERN.source}|[^AQgw]==$|[^AEIMQUYcgkosw048=]=$`,
);
const excludingInvalidBase64 = (invalid: RegExp): RegExp =>
new RegExp(
`^(?!.*(?:${invalid.source}))${BASE64_QUARTETS_PATTERN.source.slice(1)}`,
);
/** Padded base64 syntax, allowing nonzero pad bits and the empty encoding. */
export const PADDED_BASE64_PATTERN = new RegExp(
`${PADDED_BASE64_PREFIX}(?:....)*$`,
export const PADDED_BASE64_PATTERN = excludingInvalidBase64(
PADDED_BASE64_INVALID_PATTERN,
);
/** Canonical padded base64, including zero pad bits and the empty encoding. */
export const CANONICAL_BASE64_PATTERN = new RegExp(
`${PADDED_BASE64_PREFIX}(?!.*[^AQgw]==$)(?!.*[^AEIMQUYcgkosw048=]=$)(?:....)*$`,
export const CANONICAL_BASE64_PATTERN = excludingInvalidBase64(
CANONICAL_BASE64_INVALID_PATTERN,
);
@@ -176,16 +176,6 @@ it.each([
});
it.each([
"prlimit64(900, RLIMIT_NOFILE, NULL, {rlim_cur=1024, rlim_max=4096}) = 0",
"sched_getaffinity(900, 8, [0]) = 8",
"sched_setaffinity(900, 8, [0]) = 0",
"sched_getparam(900, {sched_priority=0}) = 0",
"sched_setparam(900, {sched_priority=0}) = 0",
"sched_getscheduler(900) = 0",
"sched_setscheduler(900, SCHED_OTHER, {sched_priority=0}) = 0",
"sched_getattr(900, {}, 56, 0) = 0",
"sched_setattr(900, {}, 0) = 0",
"sched_rr_get_interval(900, {tv_sec=0, tv_nsec=0}) = 0",
"getpgid(900) = 900",
"getsid(900) = 900",
"get_robust_list(900, [0x1], [24]) = 0",
@@ -238,3 +228,26 @@ it.each([
])("retains unresolved target facts: %s", (line) => {
expect(() => check(101, line)).toThrow("Unresolved process target");
});
it.each([
"prlimit64(900, RLIMIT_NOFILE, NULL, {rlim_cur=1024, rlim_max=1024}) = 0",
"prlimit64(900, RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=1024}, NULL) = 0",
"sched_setaffinity(900, 128, [0 1]) = 0",
"sched_getaffinity(900, 128, [0 1]) = 8",
"sched_setscheduler(900, SCHED_OTHER, {sched_priority=0}) = 0",
"sched_getscheduler(900) = SCHED_OTHER",
"sched_setparam(900, {sched_priority=0}) = 0",
"sched_getparam(900, {sched_priority=0}) = 0",
"sched_setattr(900, {size=56, sched_policy=SCHED_OTHER}, 0) = 0",
"sched_getattr(900, {size=56, sched_policy=SCHED_OTHER}, 56, 0) = 0",
"sched_rr_get_interval(900, {tv_sec=0, tv_nsec=0}) = 0",
])("rejects historical PID resource and scheduler access: %s", (line) => {
for (const live of [true, false]) {
expect(() => check(101, line, live)).toThrow("non-inventory lookup");
expect(() =>
check(101, line.replace(/ = .*/u, " = -1 ESRCH (No such process)"), live),
).toThrow("non-inventory lookup");
}
expect(check(101, line.replace("(900", "(9000"))).toEqual([]);
expect(check(101, line.replace("(900", "(0"))).toEqual([]);
});