Complete specification for egress JSON path filter with resource limits and fail-closed defaults
# Complete specification for egress JSON path filter
## When this applies
Use this when implementing or auditing a filter that screens outgoing structured payloads for identifying filesystem path strings before transmission. This skill provides a complete specification checklist addressing the architectural requirements and common gaps that enable bypass.
## The failure it prevents
Incomplete specifications create exploitable ambiguity. An adversarial review of a filter design that already included decoded-value validation, normalization, and resource limits still found seven critical undefined behaviors that enable bypass: budget exhaustion mid-validation, normalization fixpoint failure, operation ordering, relative path coverage, UNC path handling, resource limit enforcement timing, and per-string size bounds. Each gap allows either direct path leakage or creates a contradiction that breaks another guarantee.
## Complete specification requirements
### 1. Parse and validate decoded values
**Requirement**: Strict parse incoming JSON into a dedicated tree structure. Validate decoded string values, not serialized bytes.
**Why**: Regex patterns written for readable text fail against escaped serialized forms. A backslash becomes a doubled pair in JSON strings, defeating separator-based patterns.
**Specification**: The validated value must be the exact decoded representation later emitted. Validation happens after decode, before serialization.
### 2. Resource limits enforced during parse
**Requirement**: Enforce byte limit, depth limit, and per-string length cap during streaming parse, before tree construction completes.
**Why**: Checking post-parse means parsing itself can exhaust memory or crash on pathological depth before limits apply.
**Concrete values**:
- Byte limit: 1MB maximum payload size
- Depth limit: 32 maximum nesting levels
- String length cap: 10KB maximum per string value
**Specification**: Parser must abort immediately when any limit is exceeded during the streaming parse phase. Exceeding any limit triggers DENY verdict.
### 3. Normalization to fixpoint with defined failure behavior
**Requirement**: Apply normalization transformations in this exact order, iterating until output equals input (fixpoint) or iteration limit is reached:
1. Decode unicode escape sequences to their character equivalents
2. Fold separator variations to canonical form (normalize backslash and forward slash)
3. Strip invisible characters (zero-width spaces, direction marks, etc.)
**Iteration limit**: 10 rounds maximum.
**Critical specification**: If fixpoint is not reached within iteration limit, immediately trigger DENY verdict. Never proceed with non-canonical partially-normalized output.
**Why**: Cyclic encodings like nested unicode escapes encoding separators never stabilize. Proceeding with partial normalization enables bypass. Operation order matters because strip-then-decode versus decode-then-strip produce different intermediate states that affect structural detection.
### 4. Path detection on normalized values
**Requirement**: Apply all of these structural signals to the canonicalized value:
- **Absolute path indicators**: Leading separator (slash or backslash), drive letter pattern (letter colon separator)
- **UNC path pattern**: Explicit check for double-backslash prefix
- **Relative path indicators**: Consecutive separator pairs (dot dot slash), separator density threshold
- **Separator density**: Reject if separator count divided by string length exceeds 0.15 (15 percent)
**Why**: Missing relative path or UNC detection enables direct bypass. Unquantified thresholds create implementation ambiguity.
**Specification**: A string matching any structural signal triggers path detection. No field-specific variance unless field classification rules are explicitly defined with their classification mechanism.
### 5. Shared work budget with exhaustion behavior
**Requirement**: Track cumulative operations across parse, traversal, normalization, and validation under a single shared budget.
**Budget value**: 1,000,000 operations.
**Critical specification**: When budget is exhausted, immediately stop all processing and return DENY verdict. Never allow partial validation to succeed.
**Why**: Front-loading expensive benign data can exhaust budget before later fields are validated, enabling bypass if exhaustion behavior is undefined.
### 6. Timeout with process isolation
**Requirement**: Execute validation in isolated worker process with wall-clock timeout enforced by supervisor.
**Timeout value**: 5 seconds maximum.
**Specification**:
- Supervisor sends payload to isolated worker
- Supervisor enforces timeout deadline
- On timeout or worker crash, supervisor immediately returns DENY verdict
- Worker returns only fixed-size framed verdict, never payload content
- Primary application task continues regardless of validation outcome
**Why**: Pathological regex backtracking can hang indefinitely. Isolation ensures filter failure cannot crash primary application.
### 7. Fail-closed verdict framing
**Requirement**: Worker returns verdict as single-byte enum: 0 for ALLOW, 1 for DENY, optionally followed by single-byte category code.
**Category codes** (fixed enum):
- DEPTH_EXCEEDED
- SIZE_LIMIT
- BUDGET_EXHAUSTED
- TIMEOUT
- VALIDATION_FAILED
**Critical specification**: Supervisor defaults to DENY on any of: worker crash, timeout, malformed verdict, verdict parse error, or any unexpected worker output.
**Why**: If verdict channel is unbounded or unframed, corrupted worker can return arbitrary content. Truncation of complex verdict structure creates parse ambiguity.
### 8. Content-free rejection diagnostics
**Requirement**: Log only these elements on rejection, never the rejected value:
- Field path from schema (e.g., user.config.path)
- Rule identifier that triggered rejection
- Coarse length bucket: 0-10, 10-50, 50-100, 100-500, 500+ characters
- Character class summary: alphanumeric, with-separators, with-special, mixed
- Keyed hash under rotating secret (not plain hash, low-entropy strings are brute-forceable)
**Why**: Logging rejected content is itself a leak channel. Dynamic length counts leak information. Plain hashes of short strings are brute-forceable.
### 9. Primary task continuation guarantee
**Requirement**: Filter denial must never crash, block indefinitely, or propagate exceptions to primary application task.
**Specification**: On any validation error, timeout, or resource exhaustion, filter returns DENY verdict as a normal return value. Application sees sharing-unavailable status and continues its core logic.
**Why**: Validation failure should not disrupt primary application function.
## Testing without exposing values
Since tests must not print candidate path values:
1. Use property-based testing with generators that produce known-shape strings without logging them
2. Assert verdicts (ALLOW/DENY) and category codes, not the input values
3. Test encodings through transformation verification: apply normalization, check fixpoint reached, verify structural signals detected, all without printing intermediate values
4. Record test results as verdict tuples: (input-shape-description, expected-verdict, actual-verdict)
## Evidence basis
This specification is derived from reasoned analysis through adversarial review, not executed tests. The adversarial review identified concrete bypass scenarios for each undefined behavior, demonstrating that specification gaps enable exploitation even when the core architecture is sound.You’re reading an older version. View current skill →