mirror of
https://github.com/morluto/rea.git
synced 2026-10-30 19:35:56 +00:00
fix(cli): validate outline-depth before numeric coercion (#7505)
Fixes morluto/rea#7425 Signed-off-by: pqminh27 <minh8a1hb@gmail.com>
This commit is contained in:
3 files changed
+87
-19
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { projectCliAnalysisResult } from "./projectCliAnalysisResult.js";
|
||||
import { z } from "incur";
|
||||
import { cliIntegerArgument } from "./options.js";
|
||||
|
||||
import type { DirectAnalysis } from "../composition/directAnalysis.js";
|
||||
import type { ReferenceSourcePolicy } from "../domain/referenceSourcePolicy.js";
|
||||
@@ -288,13 +289,7 @@ const registerReferenceSourceCommand = (
|
||||
.describe(
|
||||
"Complete committed graph or compact historical-source summary",
|
||||
),
|
||||
outlineDepth: z
|
||||
.number()
|
||||
.int()
|
||||
.safe()
|
||||
// After .safe(): its safe-range check overwrites an earlier minimum,
|
||||
// which advertised negative depths the parser rejects.
|
||||
.nonnegative()
|
||||
outlineDepth: cliIntegerArgument(0)
|
||||
.optional()
|
||||
.describe(
|
||||
"Summary outline path depth (default: 2; 0 omits entries); requires --detail summary",
|
||||
|
||||
@@ -394,25 +394,44 @@ describe("Apktool locale selection input schema", () => {
|
||||
});
|
||||
|
||||
describe("reference-source outline-depth input schema", () => {
|
||||
it("aligns advertised bounds with the parser's accepted values", async () => {
|
||||
it("parses decimal CLI tokens into the advertised safe integer schema", async () => {
|
||||
const cli = createCli({});
|
||||
const advertised = await advertisedSchema(cli, "import-reference-source");
|
||||
const outlineDepth = advertised.options?.properties?.outlineDepth;
|
||||
expect(outlineDepth).toMatchObject({ type: "integer", minimum: 0 });
|
||||
|
||||
const parser = parserSchema(cli, "import-reference-source", "options");
|
||||
expect(parser?.safeParse({ outlineDepth: -1 }).success).toBe(false);
|
||||
expect(parser?.safeParse({ outlineDepth: 0 }).success).toBe(true);
|
||||
expect(parser?.safeParse({ outlineDepth: 2 }).success).toBe(true);
|
||||
expect(parser?.safeParse({}).success).toBe(true);
|
||||
const parsedValues = [
|
||||
["0", 0],
|
||||
["2", 2],
|
||||
["02", 2],
|
||||
[String(Number.MAX_SAFE_INTEGER), Number.MAX_SAFE_INTEGER],
|
||||
] as const;
|
||||
for (const [token, expected] of parsedValues) {
|
||||
const parsed = parser?.safeParse({ outlineDepth: token });
|
||||
expect(parsed?.success).toBe(true);
|
||||
if (parsed === undefined || !parsed.success)
|
||||
throw new Error(`Expected outline-depth token ${token} to parse`);
|
||||
expect(parsed.data).toMatchObject({ outlineDepth: expected });
|
||||
}
|
||||
for (const token of ["", "-1", "9007199254740992"]) {
|
||||
expect(parser?.safeParse({ outlineDepth: token }).success).toBe(false);
|
||||
}
|
||||
|
||||
const ajv = new Ajv2020({ strict: false });
|
||||
const validate = ajv.compile({
|
||||
...advertised.options,
|
||||
$schema: "https://json-schema.org/draft/2020-12/schema",
|
||||
});
|
||||
for (const [token] of parsedValues) {
|
||||
const parsed = parser?.safeParse({ outlineDepth: token });
|
||||
if (parsed === undefined || !parsed.success)
|
||||
throw new Error(`Expected outline-depth token ${token} to parse`);
|
||||
expect(validate(parsed.data)).toBe(true);
|
||||
}
|
||||
expect(validate({ outlineDepth: -1 })).toBe(false);
|
||||
expect(validate({ outlineDepth: 0 })).toBe(true);
|
||||
expect(validate({ outlineDepth: 2 })).toBe(true);
|
||||
expect(validate({ outlineDepth: 9007199254740992 })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -58,11 +58,22 @@ describe.skipIf(process.platform === "win32")(
|
||||
manifest_id: `hsm_${createHash("sha256").update(semanticJson).digest("hex")}`,
|
||||
};
|
||||
expect((await run("--detail", "full")).stdout).toBe(complete.stdout);
|
||||
for (const depth of [undefined, 0, 1, 10]) {
|
||||
for (const { depth, token } of [
|
||||
{ depth: undefined, token: undefined },
|
||||
{ depth: 0, token: "0" },
|
||||
{ depth: 1, token: "1" },
|
||||
{ depth: 2, token: "2" },
|
||||
{ depth: 2, token: "02" },
|
||||
{ depth: 10, token: "10" },
|
||||
{
|
||||
depth: Number.MAX_SAFE_INTEGER,
|
||||
token: String(Number.MAX_SAFE_INTEGER),
|
||||
},
|
||||
]) {
|
||||
const result = await run(
|
||||
"--detail",
|
||||
"summary",
|
||||
...(depth === undefined ? [] : ["--outline-depth", String(depth)]),
|
||||
...(token === undefined ? [] : ["--outline-depth", token]),
|
||||
);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.json).toHaveProperty("manifest", expectedManifest);
|
||||
@@ -155,12 +166,55 @@ describe("compiled historical-source summary input boundaries", () => {
|
||||
},
|
||||
);
|
||||
|
||||
for (const [label, options] of [
|
||||
["--outline-depth=", ["--detail", "summary", "--outline-depth="]],
|
||||
[
|
||||
"an empty outline-depth argument",
|
||||
["--detail", "summary", "--outline-depth", ""],
|
||||
],
|
||||
...[
|
||||
" ",
|
||||
"\t",
|
||||
"1\n",
|
||||
"0x1",
|
||||
"0b1",
|
||||
"1e0",
|
||||
"+1",
|
||||
"1.0",
|
||||
"-0",
|
||||
"-1",
|
||||
"1.5",
|
||||
"NaN",
|
||||
"9007199254740992",
|
||||
].map((value) => [
|
||||
JSON.stringify(value),
|
||||
["--detail", "summary", "--outline-depth", value],
|
||||
]),
|
||||
] as const) {
|
||||
cliTest(`rejects ${label} as an outline depth`, async ({ cli }) => {
|
||||
const root = await createTestTempDirectory(
|
||||
"rea-reference-summary-input-",
|
||||
);
|
||||
const result = await cli.run({
|
||||
arguments: ["import-reference-source", root, ...options, "--json"],
|
||||
cwd: root,
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.json).toMatchObject({
|
||||
code: "VALIDATION_ERROR",
|
||||
fieldErrors: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
path: expect.stringContaining("outlineDepth"),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
expect(result.stdout).not.toContain("HistoricalSourceGraph");
|
||||
expect(result.stdout).not.toContain("HistoricalSourceSummary");
|
||||
});
|
||||
}
|
||||
|
||||
for (const options of [
|
||||
["--detail", "summry"],
|
||||
["--detail", "summary", "--outline-depth", "-1"],
|
||||
["--detail", "summary", "--outline-depth", "1.5"],
|
||||
["--detail", "summary", "--outline-depth", "NaN"],
|
||||
["--detail", "summary", "--outline-depth", "9007199254740992"],
|
||||
["--outline-depth", "1"],
|
||||
["--detail", "full", "--outline-depth", "1"],
|
||||
]) {
|
||||
|
||||
Reference in new issue
Block a user