Skip to content

[WRONG BRANCH] fix(google): bound tool schema $ref expansion to prevent exponential DoS - #182

Draft
luvs01 wants to merge 1 commit into
mainfrom
codex/propose-fix-for-dos-vulnerability-sdpgdb
Draft

[WRONG BRANCH] fix(google): bound tool schema $ref expansion to prevent exponential DoS#182
luvs01 wants to merge 1 commit into
mainfrom
codex/propose-fix-for-dos-vulnerability-sdpgdb

Conversation

@luvs01

@luvs01 luvs01 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Motivation

  • The Google/Gemini tool-parameter sanitizer could exponentialy expand branching local $ref graphs, allowing a crafted tool schema to exhaust CPU/memory during request construction.

Description

  • Add a bounded sanitization state with a MAX_SCHEMA_NODES budget and a SanitizeState struct that tracks remainingNodes and activeRefs to limit total work and detect active $ref cycles.
  • Propagate the SanitizeState through sanitizeSchema, sanitizeProperties, normalizeAnyOf, and array items handling so every traversal decrements the budget and re-entrancy is prevented.
  • Inline local $ref targets only when not already active and ensure an active-ref is removed on return to avoid infinite recursion while still allowing safe inlining of shared defs.
  • Add a regression test that verifies branching recursive $defs are bounded and produce a small safe schema instead of exponential expansion (added to tests/google-tool-schema.test.ts).

Testing

  • Ran the focused regression suite bun test tests/google-tool-schema.test.ts, which executed 20 tests and all passed.
  • Ran TypeScript checks via bun run typecheck (bun x tsc --noEmit) and it succeeded.
  • Attempted to run the full test suite (bun run test) in this environment but the full-run was queued/contended with an existing test runner and could not be completed reliably here; focused tests and typecheck passed and CI should run the full suite.

Codex Task

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of recursive and unusually large schemas to prevent excessive expansion.
    • Schemas that exceed safety limits now resolve to an empty schema instead of causing processing issues.
  • Tests

    • Added coverage for branching recursive references to verify bounded schema processing.

@github-actions github-actions Bot added the bug Something isn't working label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

⚠️ Wrong target branch

This pull request currently targets main, but pull requests must target one of dev.

@luvs01 Please retarget this PR to dev. All contributions go to dev; main receives only release promotions. See our Contributing guide for details. Thanks! 🙏

Its title has been prefixed with [WRONG BRANCH].

This pull request is being kept as a draft automatically. Once every issue above is resolved, it will be marked ready for review again.

@github-actions github-actions Bot changed the title fix(google): bound tool schema $ref expansion to prevent exponential DoS [WRONG BRANCH] fix(google): bound tool schema $ref expansion to prevent exponential DoS Aug 9, 2026
@github-actions
github-actions Bot marked this pull request as draft August 9, 2026 01:23
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The schema sanitizer now bounds recursive expansion with active-reference tracking and a global node budget. Recursive $ref branches resolve to empty schemas, with regression coverage for branching $defs trees.

Changes

Bounded schema sanitization

Layer / File(s) Summary
Sanitizer state and recursive propagation
src/adapters/google-tool-schema.ts
Lines 10-15 add MAX_SCHEMA_NODES and SanitizeState. Lines 72-77 and 161-181 pass shared state through anyOf, properties, and array items.
Recursive reference guard and regression coverage
src/adapters/google-tool-schema.ts, tests/google-tool-schema.test.ts
Lines 108-147 enforce the node budget and prevent expansion of active $ref references. Lines 299-324 verify that recursive left and right references become empty schemas.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: hayderncenterpoint, lidge-jun

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: bounding Google tool schema $ref expansion to prevent exponential denial-of-service risk.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/propose-fix-for-dos-vulnerability-sdpgdb

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/google-tool-schema.test.ts`:
- Around line 299-323: Extend the recursive-reference coverage near “bounds
expansion of branching recursive refs” with an acyclic layered $defs fan-out
that produces more than 1,024 sanitized schema nodes. Sanitize the generated
schema and assert its resulting node count is at most the configured 1,024-node
budget, ensuring remainingNodes is propagated and enforced independently of
activeRefs.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 02cd524f-c25c-49a2-a69f-222bc6b70c7a

📥 Commits

Reviewing files that changed from the base of the PR and between 8a9c0ef and b8e66c0.

📒 Files selected for processing (2)
  • src/adapters/google-tool-schema.ts
  • tests/google-tool-schema.test.ts

Comment on lines +299 to +323
test("bounds expansion of branching recursive refs", () => {
const out = sanitizeGeminiToolParameters({
type: "object",
properties: { tree: { $ref: "#/$defs/Tree" } },
$defs: {
Tree: {
type: "object",
properties: {
left: { $ref: "#/$defs/Tree" },
right: { $ref: "#/$defs/Tree" },
},
},
},
});

expect(out).toEqual({
type: "object",
properties: {
tree: {
type: "object",
properties: { left: {}, right: {} },
},
},
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage that exhausts the node budget.

This test only verifies activeRefs. If remainingNodes is removed or is not propagated, this test still passes because the recursive $ref becomes active after one expansion.

Add an acyclic layered $defs fan-out that exceeds 1,024 sanitized nodes. Assert that the output schema-node count remains at or below the budget.

Proposed regression-test shape
+  test("bounds acyclic shared-definition fan-out by node budget", () => {
+    const defs: Record<string, unknown> = {};
+    for (let index = 0; index < 17; index += 1) {
+      const ref = `#/$defs/Node${index + 1}`;
+      defs[`Node${index}`] = {
+        type: "object",
+        properties: { left: { $ref: ref }, right: { $ref: ref } },
+      };
+    }
+    defs.Node17 = { type: "string" };
+
+    const out = sanitizeGeminiToolParameters({
+      type: "object",
+      properties: { tree: { $ref: "`#/`$defs/Node0" } },
+      $defs: defs,
+    });
+
+    // Count schemas through `properties` and `items`, then require <= 1,024.
+    expect(countSanitizedSchemas(out)).toBeLessThanOrEqual(1_024);
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/google-tool-schema.test.ts` around lines 299 - 323, Extend the
recursive-reference coverage near “bounds expansion of branching recursive refs”
with an acyclic layered $defs fan-out that produces more than 1,024 sanitized
schema nodes. Sanitize the generated schema and assert its resulting node count
is at most the configured 1,024-node budget, ensuring remainingNodes is
propagated and enforced independently of activeRefs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aardvark bug Something isn't working codex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant