openclaw - ✅(Solved) Fix [Bug]: security audit "models.small_params" reports unmitigated tool exposure even with tools.byProvider deny applied [1 pull requests, 2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#80118Fetched 2026-05-11 03:18:36
View on GitHub
Comments
2
Participants
3
Timeline
10
Reactions
2
Timeline (top)
referenced ×6commented ×2closed ×1cross-referenced ×1

collectSmallModelRiskFindings() reports web=[web_search, web_fetch, browser] for every small model in the agent's fallback chain, but it does not consult tools.byProvider to compute the resolved per-model tool policy. Configuring tools.byProvider.<provider/model>.deny = ["web_search","web_fetch","browser"] — the per-model gating mechanism that #7284 closed as implemented — is the documented mitigation for this exact threat model, yet the audit ignores it and continues to flag CRITICAL.

This is a sibling bug to #74455 (alias resolution missing from the same collector). Both gaps live in src/security/audit-extra.summary.ts:213 (collectSmallModelRiskFindings()) which "infers parameter size from entry.id" without resolving alias or tool-policy references.

Root Cause

collectSmallModelRiskFindings() reports web=[web_search, web_fetch, browser] for every small model in the agent's fallback chain, but it does not consult tools.byProvider to compute the resolved per-model tool policy. Configuring tools.byProvider.<provider/model>.deny = ["web_search","web_fetch","browser"] — the per-model gating mechanism that #7284 closed as implemented — is the documented mitigation for this exact threat model, yet the audit ignores it and continues to flag CRITICAL.

This is a sibling bug to #74455 (alias resolution missing from the same collector). Both gaps live in src/security/audit-extra.summary.ts:213 (collectSmallModelRiskFindings()) which "infers parameter size from entry.id" without resolving alias or tool-policy references.

Fix Action

Fix / Workaround

collectSmallModelRiskFindings() reports web=[web_search, web_fetch, browser] for every small model in the agent's fallback chain, but it does not consult tools.byProvider to compute the resolved per-model tool policy. Configuring tools.byProvider.<provider/model>.deny = ["web_search","web_fetch","browser"] — the per-model gating mechanism that #7284 closed as implemented — is the documented mitigation for this exact threat model, yet the audit ignores it and continues to flag CRITICAL.

collectSmallModelRiskFindings() reads each entry.id from the fallback chain and reports the agent-level web tool list, treating the per-model tools.byProvider.deny as nonexistent. Operators applying the documented mitigation see no change in audit signal, which trains people to ignore the CRITICAL finding or apply the much heavier tools.deny=["group:web","browser"] global hammer the audit recommends.

  • Operators who follow the per-model gating documented in #7284 / docs/gateway/config-tools.md see no improvement in audit signal — the audit isn't informative about residual risk.
  • The audit's recommended fix (sandbox.mode="all" + global tool deny) is much heavier than the actual minimum-viable mitigation. Operators who'd take a scalpel are pushed toward a hammer or toward ignoring the finding.
  • Cumulative signal-to-noise erosion: a CRITICAL that doesn't move when you mitigate it eventually gets tuned out, including for installs where small models do have unmitigated tool access.

PR fix notes

PR #80126: fix(security): consult tools.byProvider deny when auditing small-model web tool exposure

Description (problem / solution / changelog)

Problem

collectSmallModelRiskFindings() in src/security/audit-extra.summary.ts checks tools.deny and per-agent tools.deny when computing web-tool exposure for small models, but ignores tools.byProvider — the per-provider/model policy gate introduced in #7284.

A user who correctly configures:

tools:
  byProvider:
    "openai/gpt-4o-mini": { deny: ["web_search", "web_fetch", "browser"] }

still sees CRITICAL severity for that model. The audit falsely claims unmitigated web exposure even though the per-model deny is applied at runtime.

This is a sibling bug to #74455 (alias resolution missing from the same collector).

Fix

Add lookupByProviderPolicy() — a minimal two-key lookup (exact provider/model key, then provider prefix) — and inject the matching byProvider entry into resolveToolPolicies() as both global and agent-scoped provider policies. This mirrors the layering in tool-policy-pipeline.ts.

// Before: resolveToolPolicies only checked tools.deny, agents.*.tools.deny
// After: also checks tools.byProvider[modelId], agents.*.tools.byProvider[modelId]

Tests

Added two cases to audit-small-model-risk.test.ts:

  • tools.byProvider deny for the exact model → detail shows web=[off] (exposure cleared)
  • tools.byProvider deny for a different model → exposure not suppressed (correct scoping)

Fixes #80118

Changed files

  • src/security/audit-extra.summary.ts (modified, +32/-1)
  • src/security/audit-small-model-risk.test.ts (modified, +44/-0)

Code Example

"tools": {
     "byProvider": {
       "openrouter/google/gemma-3-4b-it:free":         { "deny": ["web_search","web_fetch","browser"] },
       "openrouter/meta-llama/llama-3.2-3b-instruct:free": { "deny": ["web_search","web_fetch","browser"] }
       // ...one entry per small model
     }
   }
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (security audit false-positive / incomplete resolution)

Summary

collectSmallModelRiskFindings() reports web=[web_search, web_fetch, browser] for every small model in the agent's fallback chain, but it does not consult tools.byProvider to compute the resolved per-model tool policy. Configuring tools.byProvider.<provider/model>.deny = ["web_search","web_fetch","browser"] — the per-model gating mechanism that #7284 closed as implemented — is the documented mitigation for this exact threat model, yet the audit ignores it and continues to flag CRITICAL.

This is a sibling bug to #74455 (alias resolution missing from the same collector). Both gaps live in src/security/audit-extra.summary.ts:213 (collectSmallModelRiskFindings()) which "infers parameter size from entry.id" without resolving alias or tool-policy references.

Steps to reproduce

On 2026.5.7 with a fallback chain that includes models <=300B params, e.g. several free-tier OpenRouter / Groq / NVIDIA entries under agents.defaults.model.fallbacks:

  1. Run openclaw security audit --deep — observe CRITICAL models.small_params finding listing each small model with web=[web_search, web_fetch, browser].

  2. Apply per-model deny via tools.byProvider:

    "tools": {
      "byProvider": {
        "openrouter/google/gemma-3-4b-it:free":         { "deny": ["web_search","web_fetch","browser"] },
        "openrouter/meta-llama/llama-3.2-3b-instruct:free": { "deny": ["web_search","web_fetch","browser"] }
        // ...one entry per small model
      }
    }
  3. Restart the gateway, re-run openclaw security audit --deep.

  4. Observed: Same CRITICAL finding, identical web=[web_search, web_fetch, browser] listing per model. The per-model deny is invisible to the audit.

Expected behavior

For each small model collected, the audit should compute the resolved per-model tool policy via the same path the runtime uses (resolveProviderToolPolicy in src/agents/pi-tools.policy.ts, which already checks the exact provider/model byProvider key). The reported web=[...] list should reflect what the model can actually invoke at runtime, and a model whose web tools resolve to an empty set should not contribute to the CRITICAL finding.

If the resolved policy still leaves uncontrolled-input tools available, flag as today. If it doesn't, exclude the model or downgrade the finding accordingly.

Actual behavior

collectSmallModelRiskFindings() reads each entry.id from the fallback chain and reports the agent-level web tool list, treating the per-model tools.byProvider.deny as nonexistent. Operators applying the documented mitigation see no change in audit signal, which trains people to ignore the CRITICAL finding or apply the much heavier tools.deny=["group:web","browser"] global hammer the audit recommends.

Impact

  • Operators who follow the per-model gating documented in #7284 / docs/gateway/config-tools.md see no improvement in audit signal — the audit isn't informative about residual risk.
  • The audit's recommended fix (sandbox.mode="all" + global tool deny) is much heavier than the actual minimum-viable mitigation. Operators who'd take a scalpel are pushed toward a hammer or toward ignoring the finding.
  • Cumulative signal-to-noise erosion: a CRITICAL that doesn't move when you mitigate it eventually gets tuned out, including for installs where small models do have unmitigated tool access.

OpenClaw version

2026.5.7 (eeef486)

Operating system

Arch Linux

Install method

npm global

Suggested fix

Same file as #74455 (src/security/audit-extra.summary.ts:213). Two changes likely land together cleanly:

  1. Alias resolution (#74455's ask) — resolve entry.id through resolveModelRefFromString() before parameter-size inference.
  2. Tool-policy resolution (this issue) — for each collected small model, compute the resolved per-model tool policy via resolveProviderToolPolicy(cfg, "<provider>/<model>") and report only the tools that survive byProvider/global deny rules. If the resolved set excludes web_search/web_fetch/browser, drop the model from the CRITICAL contribution.

Both fixes use existing resolver APIs that the runtime already consults; the audit just needs to reuse them instead of inferring from raw entry.id.

Related

  • #74455 (closed) — same collector, sibling gap (alias resolution)
  • #7284 (closed as implemented in 2026.1.12) — established tools.byProvider with provider/model keys as the per-model gating primitive the audit should recognize
  • #45410 (closed as plugin-scope) — companion approval primitive for non-exec tools

Trailers per Linux kernel coding-assistant policy:

Signed-off-by: Lance [email protected] Assisted-by: Claude-Code:claude-opus-4-7

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

For each small model collected, the audit should compute the resolved per-model tool policy via the same path the runtime uses (resolveProviderToolPolicy in src/agents/pi-tools.policy.ts, which already checks the exact provider/model byProvider key). The reported web=[...] list should reflect what the model can actually invoke at runtime, and a model whose web tools resolve to an empty set should not contribute to the CRITICAL finding.

If the resolved policy still leaves uncontrolled-input tools available, flag as today. If it doesn't, exclude the model or downgrade the finding accordingly.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: security audit "models.small_params" reports unmitigated tool exposure even with tools.byProvider deny applied [1 pull requests, 2 comments, 3 participants]