Skip to content

gstack-redact: invalid --max-bytes silently turns the fail-closed oversize guard into fail-open #1824

@jbetala7

Description

@jbetala7

Observed problem

gstack-redact's oversize check is documented and designed to fail closed (lib/redact-engine.ts: "Fail CLOSED on oversize input"). A malformed --max-bytes value silently turns it into fail open — the guard is skipped and oversized input is scanned (or, with a negative value, every input is blocked).

Root cause is a two-step gap:

  1. bin/gstack-redact parses the flag with parseInt(maxBytes, 10) and passes the result straight through:

    const maxBytes = arg("--max-bytes");
    return {
      ...
      ...(maxBytes ? { maxBytes: parseInt(maxBytes, 10) } : {}),
    };

    parseInt("foo", 10)NaN, which is passed to the engine as maxBytes: NaN.

  2. The engine only guards against null/undefined:

    const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
    ...
    if (byteLen > maxBytes) { /* fail-closed block */ }

    ?? does not catch NaN, so maxBytes stays NaN, and byteLen > NaN is always false. The fail-closed block never fires.

Current behavior on upstream main

  • gstack-redact --max-bytes notanumber < huge_input — the oversize fail-closed guard is skipped entirely (input that should be blocked at the cap is scanned anyway). The user believes they set a cap; they silently got no cap.
  • gstack-redact --max-bytes -5 < anythingbyteLen > -5 is always true, so every input is blocked as "too large", with a nonsensical > -5 bytes message.
  • Calling scan(input, { maxBytes: NaN }) directly returns oversize: false for arbitrarily large input.

Expected behavior

An invalid size cap must never weaken the fail-closed guard:

  • The engine should treat a non-finite or non-positive maxBytes as invalid and fall back to the known-good DEFAULT_MAX_BYTES (1 MiB), so the guard stays intact for every caller.
  • The CLI should reject a malformed --max-bytes with a clear error and a usage exit code (1), instead of silently passing NaN.

Duplicate searches performed

Candidate fix shape

  • lib/redact-engine.ts: replace opts.maxBytes ?? DEFAULT_MAX_BYTES with a validity check (Number.isFinite && > 0, else DEFAULT_MAX_BYTES).
  • bin/gstack-redact: validate --max-bytes is a positive integer; otherwise write an error to stderr and process.exit(1).
  • Regression tests in test/redact-engine.test.ts (invalid maxBytes still fails closed) and test/gstack-redact-cli.test.ts (malformed --max-bytes exits 1).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions