* fix(cli): refuse an integrity policy the binary analyze lane cannot honor `analyze` declares --integrity-policy for its JavaScript lane and forwards it only there. On a binary target the flag was accepted and then dropped: inspect_artifact-style integrity handling does not exist on this path, and binary_overview is a strictObject with no input at all, so the request could never take effect and the run proceeded as if the caller had not asked. The same branch already refuses source_paths on a binary target with a typed AnalysisInputError, and the sibling `inspect` command refuses the flag outright, so the refusal is the established handling here. The policy option carries a default, so the check keys off a value that differs from the owner schema's own default rather than the flag's presence; otherwise every binary analyze would be refused. The default is read from the schema instead of being restated. Verified with 5 integrity policy CLI cases, check:fast and verify:test-discovery (1023 files) on macOS arm64, and by running the built CLI for the no-flag, explicit-default and record-and-continue cases. The regression case failed with the command reaching the binary lane before the change. * fix(cli): reject explicit default integrity policies for binaries * fix(cli): keep an omitted integrity policy absent on the binary lane `javascriptApplicationOptions.shape.integrityPolicy` fills an omission with its default, and `.optional()` keeps reporting that parsed value, so `options.integrityPolicy !== undefined` was true for every `analyze <target>` call: ordinary binary analysis was rejected before the target was even read. List `z.undefined()` first in a union so an omitted option stays absent while the owner still validates a supplied value, and reapply the owner's metadata so help and `--schema` keep describing the option. Validation: tests/boundary/cli/javascriptIntegrityPolicyCli.test.ts reports 1 failed | 5 passed without this change (the case "still routes a binary analyze target without an integrity policy" returned invalid_request for integrity_policy instead of reaching the target) and 6 passed with it; `npm run check:fast` passes on macOS 26.5.1 arm64 with Node.js 24.18.0. * fix(cli): omit integrity defaults from binary option schema * test(ci): isolate report fixtures and declare PowerPC Java bounds --------- Co-authored-by: 吴佳浩 <wujiahao@supremind.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { promisify } from "node:util";
|
|
|
|
import { expect, it } from "vitest";
|
|
|
|
import { createStrippedAsarAddon } from "../../fixtures/strippedAsarAddon.js";
|
|
|
|
const execute = promisify(execFile);
|
|
const run = async (arguments_: readonly string[]): Promise<unknown> => {
|
|
const { stdout } = await execute(
|
|
process.execPath,
|
|
["scripts/rea.mjs", ...arguments_, "--json"],
|
|
{ cwd: process.cwd(), maxBuffer: 16 * 1024 * 1024 },
|
|
);
|
|
return JSON.parse(stdout);
|
|
};
|
|
|
|
const runFailure = async (arguments_: readonly string[]): Promise<unknown> => {
|
|
try {
|
|
await run(arguments_);
|
|
} catch (cause: unknown) {
|
|
if (typeof cause !== "object" || cause === null || !("stdout" in cause))
|
|
throw cause;
|
|
const stdout = cause.stdout;
|
|
if (typeof stdout !== "string") throw cause;
|
|
return JSON.parse(stdout);
|
|
}
|
|
throw new Error("Expected the CLI command to fail");
|
|
};
|
|
|
|
it.each(["analyze-javascript-application", "analyze"])(
|
|
"%s carries the integrity policy into shared JavaScript analysis",
|
|
async (command) => {
|
|
const { archive, addon } = await createStrippedAsarAddon();
|
|
const result = await run([
|
|
command,
|
|
archive,
|
|
"--integrity-policy",
|
|
"record-and-continue",
|
|
]);
|
|
expect(result).toMatchObject({
|
|
operation: "analyze_javascript_application",
|
|
parameters: { integrity_policy: "record-and-continue" },
|
|
normalized_result: {
|
|
integrity_contradictions: [
|
|
{ logical_path: addon, trust: "observed-untrusted" },
|
|
],
|
|
graph: { coverage: { status: "partial" } },
|
|
},
|
|
});
|
|
},
|
|
);
|
|
|
|
it("rejects the JavaScript integrity policy on binary-only inspection", async () => {
|
|
await expect(
|
|
run(["inspect", "unused.asar", "--integrityPolicy", "record-and-continue"]),
|
|
).rejects.toMatchObject({
|
|
code: 1,
|
|
stdout: expect.stringContaining("Unknown flag: --integrityPolicy"),
|
|
});
|
|
});
|
|
|
|
it.each(["fail", "record-and-continue"] as const)(
|
|
"rejects the explicitly selected %s integrity policy on a binary analyze target",
|
|
async (policy) => {
|
|
const failure = await runFailure([
|
|
"analyze",
|
|
"unused.bin",
|
|
"--integrity-policy",
|
|
policy,
|
|
]);
|
|
expect(failure).toMatchObject({
|
|
code: "invalid_request",
|
|
details: {
|
|
issues: [
|
|
{
|
|
path: ["integrity_policy"],
|
|
reason: "invalid_value",
|
|
message:
|
|
"An integrity policy requires the static JavaScript ASAR/directory analysis lane; binary_overview accepts no input.",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
},
|
|
);
|
|
|
|
it("still routes a binary analyze target without an integrity policy", async () => {
|
|
await expect(run(["analyze", "unused.bin"])).rejects.toMatchObject({
|
|
code: 1,
|
|
stdout: expect.stringContaining("target does not exist"),
|
|
});
|
|
});
|
|
|
|
it("advertises the optional integrity policy without an undefined union", async () => {
|
|
const schema = (await run(["analyze", "--schema"])) as {
|
|
options?: { properties?: { integrityPolicy?: unknown } };
|
|
};
|
|
const policy = schema.options?.properties?.integrityPolicy;
|
|
expect(policy).toMatchObject({
|
|
type: "string",
|
|
enum: ["fail", "record-and-continue"],
|
|
description: "Behavior when declared application integrity does not match",
|
|
});
|
|
expect(JSON.stringify(policy)).not.toContain("undefined");
|
|
});
|