mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 08:17:02 +00:00
fix(plist): reserve Evidence framing in nesting allowance
This commit is contained in:
1 parent
bd0642e0a4
commit
5d5fe72218
7 files changed
+27
-22
No files matched your search
@@ -674,11 +674,12 @@ node of the decoder's DOM at a measured upper bound, so a plist made of many
|
||||
small elements reaches it at a few MiB of source. JSON decode memory has not
|
||||
been measured and has no equivalent decode estimate. A refused input reports non-retryable `resource_constraint` with
|
||||
`reported_limits.maximum_input_bytes` or `maximum_decoded_bytes` as applicable.
|
||||
Ordinary direct-source decoding has a ceiling of 998 source nesting levels,
|
||||
derived from the existing 1000-level JSON result capacity by reserving one level
|
||||
for typed scalar fields and one for the result's `value` field. Deeper input reports
|
||||
`resource_constraint` with `reported_limits.maximum_nesting_depth: 998` and names
|
||||
the fixed nesting allowance. XML admission also requires native validation, which
|
||||
Ordinary direct-source decoding has a ceiling of 997 source nesting levels,
|
||||
derived from the existing 1000-level JSON result capacity. Three levels are
|
||||
reserved for typed scalar fields, the report value and its Evidence envelope.
|
||||
Deeper input reports `resource_constraint` with
|
||||
`reported_limits.maximum_nesting_depth: 997` and names the fixed nesting allowance.
|
||||
XML admission also requires native validation, which
|
||||
can refuse input below this decoding ceiling. Compatibility conversions can
|
||||
likewise encounter an earlier native utility refusal; both retain native
|
||||
diagnostics. Archive and Interface Builder decoding retain their separate
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MAX_NATIVE_PLIST_NESTING_DEPTH } from "../../domain/apple/plistValue.js";
|
||||
import { requireOutputSchema } from "../toolOutputSchemaPrimitives.js";
|
||||
import { z } from "zod";
|
||||
import { nativeCallObservationInputSchema } from "../../domain/native/nativeCallObservation.js";
|
||||
@@ -121,7 +122,7 @@ export const NATIVE_TOOL_CONTRACTS = [
|
||||
),
|
||||
native(
|
||||
"inspect_plist",
|
||||
"Requires an open binary or bundle-resource session; use open_binary or open_bundle_resources first. Parse its Info.plist by default, or select a local plist with path within that session. Supplying path does not open a session. Returns normalized JSON rather than plutil text. native_command_timeout_ms is each file or plutil command's wall clock (omitted means 60000 ms). The 67108864-byte plist input and decode allowances and the 998-level nesting allowance have no runtime override. XML sources also require native plutil validation, which can refuse shallower inputs with its own diagnostics. The 4096-byte markup probe is a probe window, not a document cap, and has no runtime override.",
|
||||
`Requires an open binary or bundle-resource session; use open_binary or open_bundle_resources first. Parse its Info.plist by default, or select a local plist with path within that session. Supplying path does not open a session. Returns normalized JSON rather than plutil text. native_command_timeout_ms is each file or plutil command's wall clock (omitted means 60000 ms). The 67108864-byte plist input and decode allowances and the ${MAX_NATIVE_PLIST_NESTING_DEPTH}-level nesting allowance have no runtime override. XML sources also require native plutil validation, which can refuse shallower inputs with its own diagnostics. The 4096-byte markup probe is a probe window, not a document cap, and has no runtime override.`,
|
||||
z.object({
|
||||
path: localPathStringSchema
|
||||
.optional()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { types } from "node:util";
|
||||
import { z } from "zod";
|
||||
|
||||
import type { JsonValue } from "../jsonValue.js";
|
||||
import { MAX_JSON_DEPTH, type JsonValue } from "../jsonValue.js";
|
||||
import { copyPlistUidIdentity } from "./plistUid.js";
|
||||
import { UnknownPlistScalar } from "./unknownPlistScalar.js";
|
||||
|
||||
@@ -18,6 +18,9 @@ const jsonScalarSchema = z.union([
|
||||
*/
|
||||
export const MAX_PLIST_DEPTH = 128;
|
||||
|
||||
/** Reserve typed scalar fields, the report value, and Evidence.normalized_result. */
|
||||
export const MAX_NATIVE_PLIST_NESTING_DEPTH = MAX_JSON_DEPTH - 3;
|
||||
|
||||
/** Agent-visible refusal for nesting past the JSON projection allowance. */
|
||||
const plistNestingLimitMessage = (maximumDepth = MAX_PLIST_DEPTH): string =>
|
||||
`Plist exceeds ${String(maximumDepth)} nesting levels. This release has no runtime override for this ${String(maximumDepth)}-level nesting allowance.`;
|
||||
|
||||
@@ -59,8 +59,8 @@ it.each([
|
||||
] as const)(
|
||||
"bounds ordinary %s by the projected JSON capacity, including scalar wrappers",
|
||||
(encoding) => {
|
||||
// One level for typed scalar fields and one for normalized_result.value.
|
||||
const maximum = MAX_JSON_DEPTH - 2;
|
||||
// Typed scalar fields, report.value and Evidence.normalized_result each add a level.
|
||||
const maximum = MAX_JSON_DEPTH - 3;
|
||||
for (const depth of [maximum, maximum + 1]) {
|
||||
let binary: PlistValue = Buffer.from([0, 1]);
|
||||
if (encoding === "binary data")
|
||||
@@ -99,7 +99,9 @@ it.each([
|
||||
} else {
|
||||
if (!parsed.ok) throw parsed.error;
|
||||
expect(() =>
|
||||
jsonValueSchema.parse({ value: parsed.value.value }),
|
||||
jsonValueSchema.parse({
|
||||
normalized_result: { value: parsed.value.value },
|
||||
}),
|
||||
).not.toThrow();
|
||||
let leaf: unknown = parsed.value.value;
|
||||
for (let level = 0; level < depth; level += 1) {
|
||||
|
||||
@@ -5,13 +5,14 @@ import {
|
||||
AnalysisResourceConstraintError,
|
||||
} from "../../domain/analysisErrorCore.js";
|
||||
import { fixedAllowanceOptions } from "../../domain/fixedAllowanceRemediation.js";
|
||||
import { MAX_JSON_DEPTH, type JsonValue } from "../../domain/jsonValue.js";
|
||||
import type { JsonValue } from "../../domain/jsonValue.js";
|
||||
import {
|
||||
binaryDatePrecisionOverlapLimitation,
|
||||
negativeZeroReal,
|
||||
negativeZeroRealLimitation,
|
||||
nonFiniteRealLimitation,
|
||||
projectPlistValue,
|
||||
MAX_NATIVE_PLIST_NESTING_DEPTH,
|
||||
unknownDateLimitation,
|
||||
unknownScalarLimitation,
|
||||
type ProjectedPlistValue,
|
||||
@@ -49,9 +50,6 @@ export const MAX_NATIVE_PLIST_BYTES = 64 * 1024 * 1024;
|
||||
/** Provisional decode allowance. This release has no runtime override. */
|
||||
const MAX_NATIVE_PLIST_DECODE_BYTES = 64 * 1024 * 1024;
|
||||
|
||||
// Reserve one level for typed scalar fields and one for normalized_result.value.
|
||||
export const MAX_NATIVE_PLIST_NESTING_DEPTH = MAX_JSON_DEPTH - 2;
|
||||
|
||||
const mebibytes = (bytes: number): string =>
|
||||
`${String(bytes / (1024 * 1024))} MiB`;
|
||||
|
||||
|
||||
@@ -56,12 +56,12 @@ describe.skipIf(process.platform !== "darwin")(
|
||||
{ format: "binary", depth: 200, data: false },
|
||||
{ format: "xml", depth: 200, data: false },
|
||||
{ format: "json", depth: 200, data: false },
|
||||
{ format: "binary", depth: 997, data: true },
|
||||
{ format: "binary", depth: 998, data: true },
|
||||
{ format: "binary", depth: 999, data: true },
|
||||
{ format: "xml", depth: 997, data: true },
|
||||
{ format: "xml", depth: 998, data: true },
|
||||
{ format: "xml", depth: 999, data: true },
|
||||
{ format: "json", depth: 997, data: false },
|
||||
{ format: "json", depth: 998, data: false },
|
||||
{ format: "json", depth: 999, data: false },
|
||||
] as const) {
|
||||
const { format, depth, data } = fixture;
|
||||
const path = join(root, `${format}-${depth}.plist`);
|
||||
@@ -81,7 +81,7 @@ describe.skipIf(process.platform !== "darwin")(
|
||||
// Compare its upper-edge admission with the actual utility, while
|
||||
// requiring the historical depth-129/200 controls to succeed below.
|
||||
const native =
|
||||
format === "xml" && depth === 998
|
||||
format === "xml" && depth === 997
|
||||
? await lintXmlPlist(path)
|
||||
: undefined;
|
||||
const output = await cli.run({
|
||||
@@ -90,7 +90,7 @@ describe.skipIf(process.platform !== "darwin")(
|
||||
});
|
||||
const response = await call("inspect_plist", { path });
|
||||
expect(Buffer.compare(await readFile(path), bytes)).toBe(0);
|
||||
if (depth === 999) {
|
||||
if (depth === 998) {
|
||||
expect(output.exitCode, output.stderr).toBe(1);
|
||||
const expectedFailure = {
|
||||
code: "resource_constraint",
|
||||
@@ -98,7 +98,7 @@ describe.skipIf(process.platform !== "darwin")(
|
||||
retryable: false,
|
||||
details: {
|
||||
resource: "memory",
|
||||
reported_limits: { maximum_nesting_depth: 998 },
|
||||
reported_limits: { maximum_nesting_depth: 997 },
|
||||
},
|
||||
remediation: {
|
||||
action: expect.stringMatching(/\S/u),
|
||||
|
||||
@@ -96,8 +96,8 @@ it.each([
|
||||
budget: "nesting",
|
||||
value: nested(MAX_JSON_DEPTH - 1),
|
||||
reason: /binary plist nesting.*decode budget/iu,
|
||||
limits: { maximum_nesting_depth: MAX_JSON_DEPTH - 2 },
|
||||
allowance: /998-level.*plist nesting.*no runtime override/iu,
|
||||
limits: { maximum_nesting_depth: MAX_JSON_DEPTH - 3 },
|
||||
allowance: /997-level.*plist nesting.*no runtime override/iu,
|
||||
},
|
||||
])(
|
||||
"names the fixed binary plist $budget allowance instead of host memory",
|
||||
|
||||
Reference in new issue
Block a user