fix(cli): refuse a JavaScript integrity policy the binary analyze lane cannot honor (#2808)
* 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>
This commit is contained in:
parent
1ae9000587
commit
641a8395ee
|
|
@ -45,6 +45,24 @@ const registerOverviewCommands = (
|
|||
.describe("Load and update a local analysis snapshot"),
|
||||
...nativeOpenOptionFields,
|
||||
});
|
||||
// `analyze` declares the JavaScript integrity policy for its JavaScript
|
||||
// lane. Preserve whether the caller supplied it until the target lane is
|
||||
// known, then apply the JavaScript default only in that lane. The owner
|
||||
// field fills an omission with that default, and an `.optional()` wrapper
|
||||
// keeps reporting it, so every binary target would look like an explicit
|
||||
// policy. Remove the owner's default before making the CLI option optional;
|
||||
// reapply its metadata so help and `--schema` keep describing the option.
|
||||
const ownerIntegrityPolicy =
|
||||
javascriptApplicationOptions.shape.integrityPolicy;
|
||||
const defaultIntegrityPolicy = ownerIntegrityPolicy.parse(undefined);
|
||||
const policyMetadata = ownerIntegrityPolicy.meta();
|
||||
const omittableIntegrityPolicy = ownerIntegrityPolicy
|
||||
.removeDefault()
|
||||
.optional();
|
||||
const laneIntegrityPolicy =
|
||||
policyMetadata === undefined
|
||||
? omittableIntegrityPolicy
|
||||
: omittableIntegrityPolicy.meta(policyMetadata);
|
||||
cli.command(CLI_COMMANDS.analyze, {
|
||||
description: "Get an overview of an app",
|
||||
args: z.object({
|
||||
|
|
@ -54,7 +72,7 @@ const registerOverviewCommands = (
|
|||
.describe("App, program, or analysis database path"),
|
||||
}),
|
||||
options: overviewOptions.extend({
|
||||
integrityPolicy: javascriptApplicationOptions.shape.integrityPolicy,
|
||||
integrityPolicy: laneIntegrityPolicy,
|
||||
sourcePath: javascriptApplicationOptions.shape.sourcePath,
|
||||
}),
|
||||
run: async ({ args, options }) => {
|
||||
|
|
@ -67,7 +85,8 @@ const registerOverviewCommands = (
|
|||
runCliJavaScriptApplicationAnalysis(
|
||||
{
|
||||
input_path: resolve(args.path),
|
||||
integrity_policy: options.integrityPolicy,
|
||||
integrity_policy:
|
||||
options.integrityPolicy ?? defaultIntegrityPolicy,
|
||||
...(options.sourcePath === undefined
|
||||
? {}
|
||||
: { source_paths: options.sourcePath }),
|
||||
|
|
@ -90,6 +109,19 @@ const registerOverviewCommands = (
|
|||
},
|
||||
],
|
||||
);
|
||||
if (options.integrityPolicy !== undefined)
|
||||
throw new AnalysisInputError(
|
||||
"analyze_javascript_application",
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
path: ["integrity_policy"],
|
||||
reason: "invalid_value",
|
||||
message:
|
||||
"An integrity policy requires the static JavaScript ASAR/directory analysis lane; binary_overview accepts no input.",
|
||||
},
|
||||
],
|
||||
);
|
||||
return runDirectAnalysis(
|
||||
args.path,
|
||||
"binary_overview",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,19 @@ const run = async (arguments_: readonly string[]): Promise<unknown> => {
|
|||
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) => {
|
||||
|
|
@ -46,3 +59,48 @@ it("rejects the JavaScript integrity policy on binary-only inspection", async ()
|
|||
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");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue