Skip to content

Add policy-based filtering for tool calls - #1534

Open
aantn wants to merge 12 commits into
masterfrom
claude/rbac-tool-filtering-design-71Y5o
Open

aantn wants to merge 12 commits into
masterfrom
claude/rbac-tool-filtering-design-71Y5o

Conversation

@aantn

@aantn aantn commented Feb 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces a comprehensive policy-based filtering system to HolmesGPT that enables security-conscious deployments to control which tools can be called and with what parameters. The system uses Python expressions evaluated in a sandboxed environment to define flexible access control rules.

Key Changes

  • New holmes/core/policy.py module: Implements the core policy engine with:

    • PolicyConfig: Configuration model for policy rules
    • PolicyRule: Individual rule definition with pattern matching and conditional expressions
    • PolicyEnforcer: Evaluates policy rules against tool calls using simpleeval
    • PolicyResult: Result object indicating whether a tool call is allowed
    • Support for both blacklist mode (default: allow) and whitelist mode (default: deny)
  • Policy integration into holmes/config.py:

    • Added policy field to Config class
    • Added policy_enforcer property that lazily initializes from config
    • Integrated with existing configuration system
  • Tool call enforcement in holmes/core/tool_calling_llm.py:

    • Added policy_enforcer parameter to ToolCallingLLM.__init__
    • Policy checks are performed before tool execution in _directly_invoke_tool_call
    • Denied calls return an error result with the policy denial message
  • Comprehensive documentation (docs/reference/policy-filtering.md):

    • Policy semantics and configuration guide
    • Multiple real-world examples (namespace restrictions, sensitive resources, role-based access, multi-tenant isolation)
    • Debugging guidance and security considerations
  • Extensive test coverage (tests/core/test_policy.py):

    • 698 lines of tests covering all policy features
    • Tests for rule matching, expression evaluation, AND semantics, context/vars
    • Real-world scenario tests (namespace restriction, sensitive resources, production restrictions, etc.)
  • Dependencies: Added simpleeval ^1.0.0 to pyproject.toml for safe expression evaluation

Implementation Details

Policy Semantics

  • No matching rules: Tool is allowed/denied based on default setting
  • Matching rules: ALL matching rules' when conditions must evaluate to True (AND semantics)
  • Expression environment: Sandboxed with access to tool, params, context, rule vars, and safe built-in functions
  • Helper functions: match() (fnmatch), regex(), startswith(), endswith(), contains()

Security Features

  • Expressions evaluated in sandboxed environment using simpleeval
  • Errors in policy evaluation result in denial (fail-safe)
  • Support for both blacklist and whitelist modes
  • Can be combined with Kubernetes RBAC for defense-in-depth
  • Logging of all policy decisions at INFO level

Configuration Examples

Blacklist mode (default allow, block specific tools):

policy:
  default: allow
  rules:
    - name: team-namespaces
      match: ["kubectl_*"]
      when: 'params.get("namespace", "").startswith("team-a-")'

Whitelist mode (default deny, allow specific tools):

policy:
  default: deny
  rules:
    - name: allow-prometheus
      match: ["prometheus_*"]
      when: "True"

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX

Summary by CodeRabbit

  • New Features

    • Policy-based filtering for tool calls with configurable allow/deny defaults, pattern matching, conditional rules, AND semantics, and optional rate limits; runtime enforcement now blocks disallowed calls with informative messages.
  • Documentation

    • Comprehensive policy reference with config, examples, deployment, debugging, and security guidance.
  • Tests

    • Extensive test suite covering policy behavior, matching, expressions, bash templates, rate limiting, and real-world scenarios.
  • Chores

    • Minor test cleanup and formatting fixes.

Explores 7 architecture options for policy-based filtering of tool
calls and MCP calls:

1. Simple YAML Rules - allowlist/denylist patterns
2. Kyverno-Style Policies - rich declarative validation/mutation
3. OPA/Rego - industry standard policy engine
4. K8s-Native RBAC - ServiceAccount-based enforcement
5. ABAC - attribute-based access control
6. CEL - Common Expression Language (K8s 1.26+ style)
7. Hybrid Approach - layered combination (recommended)

Includes trade-offs, concrete namespace restriction examples,
comparison matrix, and implementation roadmap.

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
Add a policy enforcement system that filters tool calls based on
configurable rules using Python expressions evaluated with simpleeval.

Features:
- Namespace allow/deny lists with glob pattern support
- Tool allow/deny lists with glob pattern support
- Custom rules with Python expressions
- Context-aware policies (team, role, etc.)
- Helper functions: match(), regex(), startswith(), endswith(), contains()

Configuration example:
```yaml
policy:
  namespaces:
    allow: ["team-a-*"]
    deny: ["kube-system"]
  rules:
    - name: no-secrets-in-prod
      match: ["kubectl_*"]
      expression: 'not (params.get("namespace", "").startswith("prod") and params.get("kind") == "secret")'
```

Integration:
- PolicyEnforcer class in holmes/core/policy.py
- Hooked into tool_calling_llm.py:_directly_invoke_tool_call()
- PolicyConfig added to Config class

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
Replace confusing expression-must-be-true-to-allow with explicit:
- effect: allow/deny - what happens when condition matches
- when: expression - condition to evaluate
- default: allow/deny - fallback when no rule matches

Rules evaluated in order, first matching rule wins.

Example:
```yaml
policy:
  default: deny
  rules:
    - name: deny-system-ns
      effect: deny
      when: 'params.get("namespace") in ["kube-system"]'
    - name: allow-team-a
      effect: allow
      when: 'params.get("namespace", "").startswith("team-a-")'
```

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
Default is ALLOW everything. Users opt-in to restrictions by adding
deny rules. Much simpler mental model - no effect field needed.

Config format:
```yaml
policy:
  deny:
    - name: block-system-namespaces
      match: ["kubectl_*"]
      when: 'params.get("namespace") in ["kube-system"]'
      message: "System namespaces are restricted"

    - name: block-bash
      match: ["bash/*"]
      # no 'when' = always deny when tool matches
```

Semantics:
- If ANY deny rule matches (tool pattern + when condition) → DENY
- Otherwise → ALLOW
- Future: allow rules can be added with different semantics

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
Change from deny-only model to scoped validation semantics:
- Tools matching NO rules → ALLOW (default open)
- Tools matching one or more rules → ALL matching rules' 'when' must pass
- If 'when' is omitted → treated as False (blocks matched tools)

This provides intuitive allowlist-style patterns where rules define
constraints that must be satisfied, rather than blocklist patterns.

Rename DenyRule to PolicyRule and config.deny to config.rules.

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
The exploration document is no longer needed now that the
implementation is complete. The code in holmes/core/policy.py
is self-documenting.

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
Add `default` field to PolicyConfig:
- `default: allow` (default) - unmatched tools allowed (blacklist mode)
- `default: deny` - unmatched tools denied (whitelist mode)

The `default` setting ONLY affects tools matching NO rules.
When rules DO match, ALL matching rules' `when` conditions must
still be True (AND semantics, unchanged).

Omitting `when` still blocks matched tools regardless of default.

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
Breaking change: `when` is now required in policy rules.
Use `when: "True"` to always allow matched tools,
`when: "False"` to always block.

This makes rule intent explicit and removes ambiguity.

Add comprehensive policy filtering documentation at
docs/reference/policy-filtering.md covering:
- Configuration and semantics
- Expression language and built-in functions
- Examples: blacklist, whitelist, multi-tenant, layered
- Helm configuration
- Debugging tips

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
@linux-foundation-easycla

linux-foundation-easycla Bot commented Feb 9, 2026 •

Copy link
Copy Markdown

CLA Signed

The committers listed above are authorized under a signed CLA.

@netlify

netlify Bot commented Feb 9, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for holmes-docs ready!

Name Link
🔨 Latest commit 16e86fa
🔍 Latest deploy log https://app.netlify.com/projects/holmes-docs/deploys/69943eb91107560008c232e6
😎 Deploy Preview https://deploy-preview-1534--holmes-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Feb 9, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a new, configurable policy engine for rule-based filtering of tool calls, integrates lazy PolicyEnforcer initialization into Config, wires enforcement into ToolCallingLLM to short-circuit disallowed tool invocations, adds tests and documentation, and introduces a runtime dependency for sandboxed Python expression evaluation.

Changes

Cohort / File(s) Summary
Policy Engine Core
holmes/core/policy.py
New policy engine implementation: models for PolicyConfig/PolicyRule/AllowCondition, Python and Bash condition evaluation, helper functions (match, regex, env, http_*), rate-limit tracking, PolicyEnforcer, PolicyResult, global enforcer management, and error handling.
Configuration
holmes/config.py
Adds policy: Optional[PolicyConfig] to Config, private _policy_enforcer attr and policy_enforcer lazy property that initializes/caches a PolicyEnforcer from config.
Tool Invocation Integration
holmes/core/tool_calling_llm.py
ToolCallingLLM accepts an optional policy_enforcer (falls back to global); enforcer is consulted before invoking tools and tool calls are short-circuited with denial results when policy blocks.
Tests
tests/core/test_policy.py, tests/plugins/...
Large new test suite covering rule validation, Python/Bash condition evaluation, helper functions, matching semantics, default allow/deny behaviors, rate-limiting, and many real-world scenarios; minor test cleanup in plugin tests.
Documentation
docs/reference/policy-filtering.md, docs/development/...
New comprehensive policy-filtering documentation added and small doc formatting tweaks/history updates.
Dependencies
pyproject.toml
Adds simpleeval = "^1.0.0" to runtime dependencies for sandboxed Python expression evaluation.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Config
    participant PolicyEnforcer
    participant ToolCallingLLM
    participant Tool

    User->>Config: Initialize with policy config
    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)
    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)

    User->>ToolCallingLLM: Request tool invocation
    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)

    rect rgba(200, 100, 100, 0.5)
        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name
        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox
        PolicyEnforcer->>PolicyEnforcer: Check rate limits and helpers (env/http)
    end

    alt Policy allows
        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)
        ToolCallingLLM->>Tool: Invoke tool with params
        Tool-->>ToolCallingLLM: Return result
        ToolCallingLLM-->>User: Return tool result
    else Policy denies
        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)
        ToolCallingLLM-->>User: Return denial StructuredToolResult
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • arikalon1
  • Avi-Robusta
  • RoiGlinik
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add policy-based filtering for tool calls' accurately and concisely captures the main feature introduced in the PR: a policy-based filtering system for controlling tool invocations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In `@docs/reference/policy-filtering.md`:
- Around line 208-211: Update the fenced code block that contains the two
"Policy denied tool ..." log lines to include a language identifier (e.g.,
change the opening "```" to "```text" or "```log") so markdownlint MD040 is
satisfied; locate the block with the lines "Policy denied tool 'kubectl_get'..."
and "Policy denied tool 'bash/run_command'..." and replace the fence
accordingly.

In `@holmes/config.py`:
- Around line 125-129: The policy enforcer property (Config.policy_enforcer) is
never passed into ToolCallingLLM, so the global default enforcer stays
uninitialized and enforcement never runs; update each factory that constructs
ToolCallingLLM—create_console_toolcalling_llm, create_agui_toolcalling_llm,
create_toolcalling_llm, create_issue_investigator,
create_console_issue_investigator—to pass policy_enforcer=self.policy_enforcer
(or config.policy_enforcer) into the ToolCallingLLM(...) call so
ToolCallingLLM.__init__ receives the enforcer instead of relying on
get_policy_enforcer() returning None.

In `@holmes/core/policy.py`:
- Around line 224-226: The current logger.info call prints the full params dict
(logger.info(f"Policy denied tool '{tool_name}' with params {params}:
{message}")), which can expose sensitive values; change it to log only
non-sensitive information by either logging the param keys (e.g.,
list(params.keys())) or creating a redacted copy via a helper (e.g.,
redact_sensitive_params(params)) before inserting into the message, keeping the
same logger.info, tool_name and message variables but never interpolating raw
params values at INFO level.
- Around line 124-145: The SAFE_FUNCTIONS dict in holmes/core/policy.py exposes
getattr (and optionally hasattr) which can bypass simpleeval's AST protections;
remove getattr from SAFE_FUNCTIONS (and remove hasattr if you agree with the
reviewer) so attribute-access cannot be invoked from expressions, and update any
tests or callers that relied on SAFE_FUNCTIONS containing getattr/hasattr to use
safe, explicit helpers instead.
- Around line 208-215: The code builds the evaluation context by merging
rule.vars into names after setting "tool", "params", and "context", allowing
rule.vars to silently shadow those built-ins (seen in matching_rules loop and
the names dict using tool_name, params, context, **rule.vars); change this to
validate and prevent shadowing by detecting if any key in rule.vars conflicts
with the reserved keys ("tool", "params", "context") and either raise/log a
clear error or remove those keys from rule.vars before merging, ensuring
matching_rules processing does not allow rule.vars to override the actual tool
call data.
- Around line 147-163: The shared self._evaluator created in Policy.__init__ is
mutated during Policy.check() and causes a race when ToolCallingLLM runs
concurrent calls; to fix, stop using the shared evaluator and instead create a
fresh EvalWithCompoundTypes inside Policy.check() (copy the SAFE_FUNCTIONS and
helper functions onto that local evaluator and set its names before calling
eval), or alternatively protect the existing self._evaluator with a
threading.Lock around the code that mutates names and calls eval; update
Policy.check() (the method that sets evaluator.names and calls eval) to use the
per-call local evaluator or acquire/release the lock so concurrent
_directly_invoke_tool_call executions in ToolCallingLLM are safe.

In `@holmes/core/tool_calling_llm.py`:
- Around line 535-546: The policy_enforcer.check call in
_directly_invoke_tool_call is missing the context argument, so update the code
to pass a context dict into PolicyEnforcer.check (e.g.,
policy_enforcer.check(tool_name, tool_params, context)) and propagate that
context into _directly_invoke_tool_call's signature (and all callers) or
otherwise obtain a proper context object (not an empty dict) before calling;
ensure the unique symbols involved are _directly_invoke_tool_call and
policy_enforcer.check (and update callers of _directly_invoke_tool_call
accordingly) so context-based rules like context.get("role") work correctly.
🧹 Nitpick comments (2)
holmes/core/policy.py (1)

240-248: Use logging.exception to preserve the traceback.

Per Ruff TRY400, logging.exception is preferred over logging.error when inside an except block, as it automatically includes the traceback for debugging.

Proposed fix
             except Exception as e:
-                logger.error(
+                logger.exception(
                     f"Policy rule '{rule.name}' evaluation failed: {e}. Denying by default."
                 )
tests/core/test_policy.py (1)

257-275: Consider adding a test for vars shadowing built-in names (tool, params, context).

If a rule defines vars: {"params": "overridden"}, it would silently shadow the actual params dict. A test documenting the expected behavior (whether it's an error, warning, or intentional override) would clarify the contract.

Comment on lines +208 to +211
```
Policy denied tool 'kubectl_get' with params {'namespace': 'kube-system'}: Only team-a namespaces are allowed
Policy denied tool 'bash/run_command': no matching rules (default: deny)
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a language identifier to the fenced code block.

The log output example is missing a language specifier, which triggers a markdownlint warning (MD040). Use log or text for non-code output.

Proposed fix
-```
+```text
 Policy denied tool 'kubectl_get' with params {'namespace': 'kube-system'}: Only team-a namespaces are allowed
 Policy denied tool 'bash/run_command': no matching rules (default: deny)
 ```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
Policy denied tool 'kubectl_get' with params {'namespace': 'kube-system'}: Only team-a namespaces are allowed
Policy denied tool 'bash/run_command': no matching rules (default: deny)
```
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 208-208: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In `@docs/reference/policy-filtering.md` around lines 208 - 211, Update the fenced
code block that contains the two "Policy denied tool ..." log lines to include a
language identifier (e.g., change the opening "```" to "```text" or "```log") so
markdownlint MD040 is satisfied; locate the block with the lines "Policy denied
tool 'kubectl_get'..." and "Policy denied tool 'bash/run_command'..." and
replace the fence accordingly.

Comment thread holmes/config.py
Comment on lines +125 to +129
@property
def policy_enforcer(self) -> Optional[PolicyEnforcer]:
if self._policy_enforcer is None and self.policy is not None:
self._policy_enforcer = init_policy_from_config(self.policy)
return self._policy_enforcer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check ToolCallingLLM.__init__ signature and policy_enforcer handling
rg -A 15 'class ToolCallingLLM' holmes/core/tool_calling_llm.py

Repository: HolmesGPT/holmesgpt

Length of output: 467


🏁 Script executed:

# Check all factory methods that create ToolCallingLLM
rg -B 3 -A 8 'return ToolCallingLLM\(' holmes/config.py

Repository: HolmesGPT/holmesgpt

Length of output: 1245


🏁 Script executed:

# Check if policy_enforcer property is accessed anywhere
rg -n '\.policy_enforcer' holmes/

Repository: HolmesGPT/holmesgpt

Length of output: 354


🏁 Script executed:

# Check IssueInvestigator.__init__ signature
rg -A 10 'class IssueInvestigator' holmes/

Repository: HolmesGPT/holmesgpt

Length of output: 695


🏁 Script executed:

# Search for any explicit calls to init_policy_from_config or config.policy_enforcer
rg -n 'init_policy_from_config|\.policy_enforcer' holmes/ --type py

Repository: HolmesGPT/holmesgpt

Length of output: 668


🏁 Script executed:

# Check what get_policy_enforcer() does and where the global is managed
rg -B 3 -A 8 'def get_policy_enforcer\|def set_policy_enforcer' holmes/

Repository: HolmesGPT/holmesgpt

Length of output: 45


🏁 Script executed:

# Search for get_policy_enforcer definition and global policy management
rg -n 'def get_policy_enforcer|_policy_enforcer_global|GLOBAL.*POLICY' holmes/ --type py

Repository: HolmesGPT/holmesgpt

Length of output: 143


🏁 Script executed:

# Check holmes/core/policy.py for global management and init_policy_from_config
rg -A 20 'def init_policy_from_config' holmes/core/policy.py

Repository: HolmesGPT/holmesgpt

Length of output: 397


🏁 Script executed:

# Look for any module-level policy enforcer or global state
rg -B 3 -A 3 'get_policy_enforcer' holmes/core/tool_calling_llm.py

Repository: HolmesGPT/holmesgpt

Length of output: 634


🏁 Script executed:

# Confirm: search for any place where config.policy_enforcer is accessed
rg -n 'config\.policy_enforcer\|self\.policy_enforcer' holmes/ --type py

Repository: HolmesGPT/holmesgpt

Length of output: 45


🏁 Script executed:

# Check set_policy_enforcer to understand global state management
rg -B 2 -A 6 'def set_policy_enforcer' holmes/core/policy.py

Repository: HolmesGPT/holmesgpt

Length of output: 337


Policy enforcer is never wired into ToolCallingLLM — policy enforcement will not activate.

None of the factory methods (create_console_toolcalling_llm, create_agui_toolcalling_llm, create_toolcalling_llm, create_issue_investigator, create_console_issue_investigator) pass policy_enforcer to ToolCallingLLM. When ToolCallingLLM.__init__ receives no explicit enforcer, it falls back to get_policy_enforcer() (line 184), which returns None because the global _default_enforcer is never initialized—the Config.policy_enforcer lazy property that would set it is never accessed anywhere in the codebase.

Pass policy_enforcer=self.policy_enforcer in each factory method call to ToolCallingLLM(), or ensure the lazy property is triggered during config initialization.

Proposed fix: pass policy_enforcer in factory methods

Example for create_console_toolcalling_llm (apply to all factory methods):

     return ToolCallingLLM(
         tool_executor,
         self.max_steps,
         self._get_llm(tracer=tracer, model_key=model_name),
+        policy_enforcer=self.policy_enforcer,
     )
🤖 Prompt for AI Agents
In `@holmes/config.py` around lines 125 - 129, The policy enforcer property
(Config.policy_enforcer) is never passed into ToolCallingLLM, so the global
default enforcer stays uninitialized and enforcement never runs; update each
factory that constructs ToolCallingLLM—create_console_toolcalling_llm,
create_agui_toolcalling_llm, create_toolcalling_llm, create_issue_investigator,
create_console_issue_investigator—to pass policy_enforcer=self.policy_enforcer
(or config.policy_enforcer) into the ToolCallingLLM(...) call so
ToolCallingLLM.__init__ receives the enforcer instead of relying on
get_policy_enforcer() returning None.

Comment thread holmes/core/policy.py Outdated
Comment thread holmes/core/policy.py
Comment on lines +147 to +163
def __init__(self, config: Optional[PolicyConfig] = None):
self.config = config or PolicyConfig()
self._evaluator = EvalWithCompoundTypes()

# Add safe functions
self._evaluator.functions.update(self.SAFE_FUNCTIONS)

# Add helper functions
self._evaluator.functions["match"] = lambda pattern, string: fnmatch.fnmatch(
string or "", pattern
)
self._evaluator.functions["regex"] = lambda pattern, string: bool(
re.search(pattern, string or "")
)
self._evaluator.functions["startswith"] = lambda s, prefix: (s or "").startswith(prefix)
self._evaluator.functions["endswith"] = lambda s, suffix: (s or "").endswith(suffix)
self._evaluator.functions["contains"] = lambda s, sub: sub in (s or "")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Race condition: shared _evaluator instance is not thread-safe.

ToolCallingLLM executes tool calls concurrently via ThreadPoolExecutor (see tool_calling_llm.py line 451). Each concurrent call to _directly_invoke_tool_call may invoke self.policy_enforcer.check(), which mutates self._evaluator.names on line 219 before calling self._evaluator.eval() on line 220. A concurrent call can overwrite names between these two lines, causing one thread to evaluate its expression against the wrong tool/params.

Fix: create a new EvalWithCompoundTypes per check() invocation, or use a lock.

🔒 Proposed fix: create evaluator per-call
     def __init__(self, config: Optional[PolicyConfig] = None):
         self.config = config or PolicyConfig()
-        self._evaluator = EvalWithCompoundTypes()
-
-        # Add safe functions
-        self._evaluator.functions.update(self.SAFE_FUNCTIONS)
-
-        # Add helper functions
-        self._evaluator.functions["match"] = lambda pattern, string: fnmatch.fnmatch(
-            string or "", pattern
-        )
-        self._evaluator.functions["regex"] = lambda pattern, string: bool(
-            re.search(pattern, string or "")
-        )
-        self._evaluator.functions["startswith"] = lambda s, prefix: (s or "").startswith(prefix)
-        self._evaluator.functions["endswith"] = lambda s, suffix: (s or "").endswith(suffix)
-        self._evaluator.functions["contains"] = lambda s, sub: sub in (s or "")
+        self._functions: dict = {}
+        self._functions.update(self.SAFE_FUNCTIONS)
+        self._functions["match"] = lambda pattern, string: fnmatch.fnmatch(
+            string or "", pattern
+        )
+        self._functions["regex"] = lambda pattern, string: bool(
+            re.search(pattern, string or "")
+        )
+        self._functions["startswith"] = lambda s, prefix: (s or "").startswith(prefix)
+        self._functions["endswith"] = lambda s, suffix: (s or "").endswith(suffix)
+        self._functions["contains"] = lambda s, sub: sub in (s or "")
+
+    def _create_evaluator(self, names: dict) -> EvalWithCompoundTypes:
+        """Create a new evaluator instance with the given names (thread-safe)."""
+        evaluator = EvalWithCompoundTypes()
+        evaluator.functions.update(self._functions)
+        evaluator.names = names
+        return evaluator

Then in check(), replace:

-                self._evaluator.names = names
-                condition_passed = self._evaluator.eval(rule.when)
+                evaluator = self._create_evaluator(names)
+                condition_passed = evaluator.eval(rule.when)
🤖 Prompt for AI Agents
In `@holmes/core/policy.py` around lines 147 - 163, The shared self._evaluator
created in Policy.__init__ is mutated during Policy.check() and causes a race
when ToolCallingLLM runs concurrent calls; to fix, stop using the shared
evaluator and instead create a fresh EvalWithCompoundTypes inside Policy.check()
(copy the SAFE_FUNCTIONS and helper functions onto that local evaluator and set
its names before calling eval), or alternatively protect the existing
self._evaluator with a threading.Lock around the code that mutates names and
calls eval; update Policy.check() (the method that sets evaluator.names and
calls eval) to use the per-call local evaluator or acquire/release the lock so
concurrent _directly_invoke_tool_call executions in ToolCallingLLM are safe.

Comment thread holmes/core/policy.py Outdated
Comment on lines +208 to +215
for rule in matching_rules:
# Build evaluation context
names = {
"tool": tool_name,
"params": params,
"context": context,
**rule.vars,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Rule vars can silently shadow built-in expression variables.

The spread **rule.vars on line 214 is applied after tool, params, and context are set. If a rule defines vars: {"params": ...} or vars: {"tool": ...}, it would silently override the actual tool call data, leading to incorrect policy evaluation.

🛡️ Proposed fix: validate or warn on shadowing
+            _RESERVED_NAMES = {"tool", "params", "context"}
+
             names = {
                 "tool": tool_name,
                 "params": params,
                 "context": context,
-                **rule.vars,
             }
+            for k, v in rule.vars.items():
+                if k in _RESERVED_NAMES:
+                    logger.warning(
+                        f"Policy rule '{rule.name}' var '{k}' shadows built-in name; ignoring"
+                    )
+                else:
+                    names[k] = v
🤖 Prompt for AI Agents
In `@holmes/core/policy.py` around lines 208 - 215, The code builds the evaluation
context by merging rule.vars into names after setting "tool", "params", and
"context", allowing rule.vars to silently shadow those built-ins (seen in
matching_rules loop and the names dict using tool_name, params, context,
**rule.vars); change this to validate and prevent shadowing by detecting if any
key in rule.vars conflicts with the reserved keys ("tool", "params", "context")
and either raise/log a clear error or remove those keys from rule.vars before
merging, ensuring matching_rules processing does not allow rule.vars to override
the actual tool call data.

Comment thread holmes/core/policy.py Outdated
Comment on lines +224 to +226
logger.info(
f"Policy denied tool '{tool_name}' with params {params}: {message}"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Logging full params dict may expose sensitive data.

The params dict could contain sensitive values (e.g., credentials, tokens, or PII passed as tool parameters). Logging the full dict at INFO level in production could be a compliance concern. Consider logging only param keys or redacting sensitive values.

Proposed fix
                     logger.info(
-                        f"Policy denied tool '{tool_name}' with params {params}: {message}"
+                        f"Policy denied tool '{tool_name}' (params keys: {list(params.keys())}): {message}"
                     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info(
f"Policy denied tool '{tool_name}' with params {params}: {message}"
)
logger.info(
f"Policy denied tool '{tool_name}' (params keys: {list(params.keys())}): {message}"
)
🤖 Prompt for AI Agents
In `@holmes/core/policy.py` around lines 224 - 226, The current logger.info call
prints the full params dict (logger.info(f"Policy denied tool '{tool_name}' with
params {params}: {message}")), which can expose sensitive values; change it to
log only non-sensitive information by either logging the param keys (e.g.,
list(params.keys())) or creating a redacted copy via a helper (e.g.,
redact_sensitive_params(params)) before inserting into the message, keeping the
same logger.info, tool_name and message variables but never interpolating raw
params values at INFO level.

Comment on lines +535 to +546
# Policy enforcement: check if tool call is allowed
if self.policy_enforcer:
policy_result = self.policy_enforcer.check(tool_name, tool_params)
if not policy_result.allowed:
logging.warning(
f"Policy denied tool '{tool_name}' with params {tool_params}: {policy_result.message}"
)
return StructuredToolResult(
status=StructuredToolResultStatus.ERROR,
error=f"Policy denied: {policy_result.message}",
params=tool_params,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

context is never passed to policy_enforcer.check(), breaking context-based rules.

The check() call on line 537 omits the context parameter. This means any policy rules that reference context.get("role"), context.get("team"), etc. — as documented and tested — will always see an empty dict, and the rules will not behave as expected.

You need to either:

  1. Plumb a context dict through to _directly_invoke_tool_call, or
  2. Set a default context on the PolicyEnforcer at initialization time.
🤖 Prompt for AI Agents
In `@holmes/core/tool_calling_llm.py` around lines 535 - 546, The
policy_enforcer.check call in _directly_invoke_tool_call is missing the context
argument, so update the code to pass a context dict into PolicyEnforcer.check
(e.g., policy_enforcer.check(tool_name, tool_params, context)) and propagate
that context into _directly_invoke_tool_call's signature (and all callers) or
otherwise obtain a proper context object (not an empty dict) before calling;
ensure the unique symbols involved are _directly_invoke_tool_call and
policy_enforcer.check (and update callers of _directly_invoke_tool_call
accordingly) so context-based rules like context.get("role") work correctly.

- Change policy rule syntax from `when:` to `allow_if:` with explicit
  `python:` or `bash:` condition type
- Add bash command conditions with Jinja2-style templating for external
  checks like Kubernetes RBAC verification via kubectl auth can-i
- Support template variables: {{ params.X }}, {{ context.X }}, {{ tool }}
- Support template filters: | default:"value", | quote
- Fix mypy type errors with proper annotations
- Update documentation with comprehensive examples

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
Auto-formatting changes from ruff, isort, and end-of-file fixer.

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a configurable, rule-based policy engine for tool-call filtering, integrates it into Config and ToolCallingLLM with lazy enforcer initialization, adds expression evaluation dependency, extensive tests, and comprehensive documentation for policy configuration and semantics.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy engine: PolicyConfig/PolicyRule/AllowCondition, sandboxed Python and Bash condition evaluation, PolicyEnforcer, PolicyResult, rule matching, global enforcer management and logging.|\n|**Configuration & Integration** <br> `holmes/config.py`, `holmes/core/tool_calling_llm.py`|Adds `policy: Optional[PolicyConfig]` and cached `policy_enforcer` property to Config; ToolCallingLLM accepts/injects a PolicyEnforcer and short-circuits tool invocation when policy denies.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive unit tests covering rule validation, expression evaluation, matching, default-allow/deny semantics, context/vars, Bash templates, and real-world scenarios; minor test cleanup/formatting changes.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy filtering documentation and small doc formatting adjustments.|\n|**Dependencies** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                            |\n| :---------------: | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                            |\n|    Title check    | ✅ Passed | The title directly and accurately describes the main change: adding policy-based filtering for tool calls, which is the core feature introduced across multiple files. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:16:41 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"102A:1B3502:34582:E320B:698A0868","x-ratelimit-limit":"15000","x-ratelimit-remaining":"14925","x-ratelimit-reset":"1770656812","x-ratelimit-resource":"core","x-ratelimit-used":"75","x-xss-protection":"0"},"data":""}}

2 similar comments
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a configurable, rule-based policy engine for tool-call filtering, integrates it into Config and ToolCallingLLM with lazy enforcer initialization, adds expression evaluation dependency, extensive tests, and comprehensive documentation for policy configuration and semantics.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy engine: PolicyConfig/PolicyRule/AllowCondition, sandboxed Python and Bash condition evaluation, PolicyEnforcer, PolicyResult, rule matching, global enforcer management and logging.|\n|**Configuration & Integration** <br> `holmes/config.py`, `holmes/core/tool_calling_llm.py`|Adds `policy: Optional[PolicyConfig]` and cached `policy_enforcer` property to Config; ToolCallingLLM accepts/injects a PolicyEnforcer and short-circuits tool invocation when policy denies.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive unit tests covering rule validation, expression evaluation, matching, default-allow/deny semantics, context/vars, Bash templates, and real-world scenarios; minor test cleanup/formatting changes.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy filtering documentation and small doc formatting adjustments.|\n|**Dependencies** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                            |\n| :---------------: | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                            |\n|    Title check    | ✅ Passed | The title directly and accurately describes the main change: adding policy-based filtering for tool calls, which is the core feature introduced across multiple files. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:16:41 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"102A:1B3502:34582:E320B:698A0868","x-ratelimit-limit":"15000","x-ratelimit-remaining":"14925","x-ratelimit-reset":"1770656812","x-ratelimit-resource":"core","x-ratelimit-used":"75","x-xss-protection":"0"},"data":""}}

@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a configurable, rule-based policy engine for tool-call filtering, integrates it into Config and ToolCallingLLM with lazy enforcer initialization, adds expression evaluation dependency, extensive tests, and comprehensive documentation for policy configuration and semantics.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy engine: PolicyConfig/PolicyRule/AllowCondition, sandboxed Python and Bash condition evaluation, PolicyEnforcer, PolicyResult, rule matching, global enforcer management and logging.|\n|**Configuration & Integration** <br> `holmes/config.py`, `holmes/core/tool_calling_llm.py`|Adds `policy: Optional[PolicyConfig]` and cached `policy_enforcer` property to Config; ToolCallingLLM accepts/injects a PolicyEnforcer and short-circuits tool invocation when policy denies.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive unit tests covering rule validation, expression evaluation, matching, default-allow/deny semantics, context/vars, Bash templates, and real-world scenarios; minor test cleanup/formatting changes.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy filtering documentation and small doc formatting adjustments.|\n|**Dependencies** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                            |\n| :---------------: | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                            |\n|    Title check    | ✅ Passed | The title directly and accurately describes the main change: adding policy-based filtering for tool calls, which is the core feature introduced across multiple files. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:16:41 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"102A:1B3502:34582:E320B:698A0868","x-ratelimit-limit":"15000","x-ratelimit-remaining":"14925","x-ratelimit-reset":"1770656812","x-ratelimit-resource":"core","x-ratelimit-used":"75","x-xss-protection":"0"},"data":""}}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@docs/reference/policy-filtering.md`:
- Around line 80-100: The docs overstate available builtins; update the
policy-filtering.md Python Expression Language section to accurately reflect
that only the explicitly whitelisted functions in SAFE_FUNCTIONS (referenced in
policy.py) are available — replace "Standard Python functions are also
available... etc." with a clear statement listing or linking to SAFE_FUNCTIONS
and examples of common excluded builtins (e.g., print, open, type, range, map,
filter, zip, enumerate, round, hex) so users are not misled; ensure the doc
references the SAFE_FUNCTIONS symbol in policy.py for the authoritative list.

In `@holmes/core/policy.py`:
- Line 396: Remove the local import "import re as re_module" from inside the
_render_template function and use the module-level "re" already imported at the
top of the file (replace all uses of re_module with re); update the
_render_template function to reference re directly and delete the in-function
import to comply with the project's import guidelines.

In `@tests/core/test_policy.py`:
- Around line 39-49: Move the inline imports of pytest to the module top: add a
single "import pytest" at the top of the file, then remove the in-function
imports inside the test_neither_raises and test_both_raises functions (which
call AllowCondition); ensure no other inline pytest imports remain in those
tests.
🧹 Nitpick comments (3)
docs/reference/policy-filtering.md (1)

288-294: Consider removing the Security Considerations section.

As per coding guidelines, security best practices sections should be skipped in MkDocs documentation — users are assumed to understand basics like least privilege and defense-in-depth. The content here (RBAC, sandboxing caveats, default: deny) could be folded into the relevant configuration/examples sections as brief inline notes instead.

holmes/core/policy.py (2)

377-466: Consider using the real Jinja2 library instead of this hand-rolled template engine.

Jinja2 is already a project dependency (v3.1.2). The custom template parser here (~90 lines) supports a subset of Jinja2 syntax with bespoke filter handling. Using jinja2.Environment with a sandboxed environment would be more robust, better-tested, and support the full filter/expression syntax users might expect from "Jinja2-style templating."


476-499: Global enforcer state lacks thread-safety documentation.

_default_enforcer, set_policy_enforcer, and get_policy_enforcer use unsynchronized module-level global state. This is likely fine since the enforcer is set once during startup, but a brief docstring note would prevent future misuse (e.g., dynamic policy reloading in a multi-threaded context).

Comment on lines +80 to +100
## Python Expression Language

### Available Variables

| Variable | Description |
|----------|-------------|
| `tool` | Name of the tool being called |
| `params` | Dictionary of parameters passed to the tool |
| `context` | Additional context (user, team, etc.) |

### Built-in Functions

| Function | Description |
|----------|-------------|
| `match(pattern, string)` | Glob pattern matching (fnmatch) |
| `regex(pattern, string)` | Regular expression matching |
| `startswith(s, prefix)` | String prefix check |
| `endswith(s, suffix)` | String suffix check |
| `contains(s, sub)` | Substring check |

Standard Python functions are also available: `len`, `str`, `int`, `bool`, `list`, `dict`, `any`, `all`, `min`, `max`, etc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Documentation states standard Python functions are available but some are actually restricted.

Line 100 says "Standard Python functions are also available: len, str, int, bool, list, dict, any, all, min, max, etc." — however, looking at policy.py, the available functions are explicitly limited to SAFE_FUNCTIONS. Functions like print, open, type, range, map, filter, zip, enumerate, round, hex, etc. are not available. The wording "Standard Python functions" with trailing "etc." overpromises and will confuse users who try other builtins.

Proposed fix
-Standard Python functions are also available: `len`, `str`, `int`, `bool`, `list`, `dict`, `any`, `all`, `min`, `max`, etc.
+Additional safe functions are available: `len`, `str`, `int`, `float`, `bool`, `list`, `dict`, `set`, `tuple`, `abs`, `min`, `max`, `sum`, `sorted`, `any`, `all`, `isinstance`, `hasattr`, `getattr`.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Python Expression Language
### Available Variables
| Variable | Description |
|----------|-------------|
| `tool` | Name of the tool being called |
| `params` | Dictionary of parameters passed to the tool |
| `context` | Additional context (user, team, etc.) |
### Built-in Functions
| Function | Description |
|----------|-------------|
| `match(pattern, string)` | Glob pattern matching (fnmatch) |
| `regex(pattern, string)` | Regular expression matching |
| `startswith(s, prefix)` | String prefix check |
| `endswith(s, suffix)` | String suffix check |
| `contains(s, sub)` | Substring check |
Standard Python functions are also available: `len`, `str`, `int`, `bool`, `list`, `dict`, `any`, `all`, `min`, `max`, etc.
## Python Expression Language
### Available Variables
| Variable | Description |
|----------|-------------|
| `tool` | Name of the tool being called |
| `params` | Dictionary of parameters passed to the tool |
| `context` | Additional context (user, team, etc.) |
### Built-in Functions
| Function | Description |
|----------|-------------|
| `match(pattern, string)` | Glob pattern matching (fnmatch) |
| `regex(pattern, string)` | Regular expression matching |
| `startswith(s, prefix)` | String prefix check |
| `endswith(s, suffix)` | String suffix check |
| `contains(s, sub)` | Substring check |
Additional safe functions are available: `len`, `str`, `int`, `float`, `bool`, `list`, `dict`, `set`, `tuple`, `abs`, `min`, `max`, `sum`, `sorted`, `any`, `all`, `isinstance`, `hasattr`, `getattr`.
🤖 Prompt for AI Agents
In `@docs/reference/policy-filtering.md` around lines 80 - 100, The docs overstate
available builtins; update the policy-filtering.md Python Expression Language
section to accurately reflect that only the explicitly whitelisted functions in
SAFE_FUNCTIONS (referenced in policy.py) are available — replace "Standard
Python functions are also available... etc." with a clear statement listing or
linking to SAFE_FUNCTIONS and examples of common excluded builtins (e.g., print,
open, type, range, map, filter, zip, enumerate, round, hex) so users are not
misled; ensure the doc references the SAFE_FUNCTIONS symbol in policy.py for the
authoritative list.

Comment thread holmes/core/policy.py
Comment on lines +340 to +348

try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
timeout=10, # 10 second timeout
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Command injection risk: bash template values are not quoted by default.

subprocess.run(..., shell=True) executes the rendered template as a shell command. Template substitution (e.g., {{ params.kind }}) injects values directly into the command string. The | quote filter exists but is opt-in. If a user writes:

bash: 'kubectl auth can-i get {{ params.kind }}'

and params.kind is "pods; curl attacker.com", the injected command runs unescaped. Consider auto-quoting all template values by default and requiring an explicit | raw or | noquote filter to opt out, rather than the current opt-in | quote.

🧰 Tools
🪛 Ruff (0.14.14)

[error] 342-342: subprocess call with shell=True identified, security issue

(S602)

Comment thread holmes/core/policy.py
- {{ params.key | default:"value" }} - with default
- {{ params.key | quote }} - shell-escaped
"""
import re as re_module

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Move import re to the top of the file — re is already imported at line 60.

Line 396 re-imports re as re_module inside _render_template. Since re is already imported at the module level (line 60) with no name collision, use the existing import directly.

Proposed fix
-        import re as re_module
-
-        def get_value(path: str, default: Optional[str] = None) -> Any:
+        def get_value(path: str, default: Optional[str] = None) -> Any:

Then replace all re_module references with re:

-        def replace_match(m: re_module.Match) -> str:
+        def replace_match(m: re.Match) -> str:
-        return re_module.sub(pattern, replace_match, template)
+        return re.sub(pattern, replace_match, template)

As per coding guidelines, **/*.py: "ALWAYS place Python imports at the top of the file, not inside functions or methods".

🤖 Prompt for AI Agents
In `@holmes/core/policy.py` at line 396, Remove the local import "import re as
re_module" from inside the _render_template function and use the module-level
"re" already imported at the top of the file (replace all uses of re_module with
re); update the _render_template function to reference re directly and delete
the in-function import to comply with the project's import guidelines.

Comment thread tests/core/test_policy.py
Comment on lines +39 to +49
def test_neither_raises(self):
import pytest

with pytest.raises(ValueError, match="exactly one"):
AllowCondition()

def test_both_raises(self):
import pytest

with pytest.raises(ValueError, match="exactly one"):
AllowCondition(python="True", bash="echo ok")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Move import pytest to the top of the file.

pytest is imported inside test_neither_raises (line 40) and test_both_raises (line 47). Per coding guidelines, Python imports must always be placed at the top of the file, not inside functions or methods.

Proposed fix

Add at top of file (after line 1):

import pytest

Then remove the inline imports:

     def test_neither_raises(self):
-        import pytest
-
         with pytest.raises(ValueError, match="exactly one"):
             AllowCondition()

     def test_both_raises(self):
-        import pytest
-
         with pytest.raises(ValueError, match="exactly one"):
             AllowCondition(python="True", bash="echo ok")

As per coding guidelines, **/*.py: "ALWAYS place Python imports at the top of the file, not inside functions or methods".

🤖 Prompt for AI Agents
In `@tests/core/test_policy.py` around lines 39 - 49, Move the inline imports of
pytest to the module top: add a single "import pytest" at the top of the file,
then remove the in-function imports inside the test_neither_raises and
test_both_raises functions (which call AllowCondition); ensure no other inline
pytest imports remain in those tests.

- Add http_get() and http_post() functions for making HTTP requests
  in Python policy expressions
- Add env() function for accessing environment variables
- Support basic auth (tuple) and bearer token (string) authentication
- Add comprehensive tests for HTTP helpers
- Update documentation with Confluence access control examples
  showing both Python (with http_get/http_post) and Bash (with curl)
  alternatives

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> \n> \n> ```ascii\n>  ____________________________________________________________________________________________________________________________________________________________________________________________\n> < The object-oriented model makes it easy to build up programs by accretion. What this often means, in practice, is that it provides a structured way to write spaghetti code. - Paul Graham >\n>  --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n>   \\\n>    \\   (\\__/)\n>        (•ㅅ•)\n>        /   づ\n> ```\n> \n> <sub>✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.</sub>\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a configurable, rule-based policy engine for tool-call filtering, integrates it into Config and ToolCallingLLM with lazy enforcer initialization, adds expression evaluation dependency, extensive tests, and comprehensive documentation for policy configuration and semantics.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy engine: PolicyConfig/PolicyRule/AllowCondition, sandboxed Python and Bash condition evaluation, PolicyEnforcer, PolicyResult, rule matching, global enforcer management and logging.|\n|**Configuration & Integration** <br> `holmes/config.py`, `holmes/core/tool_calling_llm.py`|Adds `policy: Optional[PolicyConfig]` and cached `policy_enforcer` property to Config; ToolCallingLLM accepts/injects a PolicyEnforcer and short-circuits tool invocation when policy denies.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive unit tests covering rule validation, expression evaluation, matching, default-allow/deny semantics, context/vars, Bash templates, and real-world scenarios; minor test cleanup/formatting changes.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy filtering documentation and small doc formatting adjustments.|\n|**Dependencies** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                            |\n| :---------------: | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                            |\n|    Title check    | ✅ Passed | The title directly and accurately describes the main change: adding policy-based filtering for tool calls, which is the core feature introduced across multiple files. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:27:11 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8088:28CDA1:19F368:6F65FE:698A0ADC","x-ratelimit-limit":"15000","x-ratelimit-remaining":"14895","x-ratelimit-reset":"1770656812","x-ratelimit-resource":"core","x-ratelimit-used":"105","x-xss-protection":"0"},"data":""}}

3 similar comments
@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> \n> \n> ```ascii\n>  ____________________________________________________________________________________________________________________________________________________________________________________________\n> < The object-oriented model makes it easy to build up programs by accretion. What this often means, in practice, is that it provides a structured way to write spaghetti code. - Paul Graham >\n>  --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n>   \\\n>    \\   (\\__/)\n>        (•ㅅ•)\n>        /   づ\n> ```\n> \n> <sub>✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.</sub>\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a configurable, rule-based policy engine for tool-call filtering, integrates it into Config and ToolCallingLLM with lazy enforcer initialization, adds expression evaluation dependency, extensive tests, and comprehensive documentation for policy configuration and semantics.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy engine: PolicyConfig/PolicyRule/AllowCondition, sandboxed Python and Bash condition evaluation, PolicyEnforcer, PolicyResult, rule matching, global enforcer management and logging.|\n|**Configuration & Integration** <br> `holmes/config.py`, `holmes/core/tool_calling_llm.py`|Adds `policy: Optional[PolicyConfig]` and cached `policy_enforcer` property to Config; ToolCallingLLM accepts/injects a PolicyEnforcer and short-circuits tool invocation when policy denies.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive unit tests covering rule validation, expression evaluation, matching, default-allow/deny semantics, context/vars, Bash templates, and real-world scenarios; minor test cleanup/formatting changes.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy filtering documentation and small doc formatting adjustments.|\n|**Dependencies** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                            |\n| :---------------: | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                            |\n|    Title check    | ✅ Passed | The title directly and accurately describes the main change: adding policy-based filtering for tool calls, which is the core feature introduced across multiple files. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:27:11 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8088:28CDA1:19F368:6F65FE:698A0ADC","x-ratelimit-limit":"15000","x-ratelimit-remaining":"14895","x-ratelimit-reset":"1770656812","x-ratelimit-resource":"core","x-ratelimit-used":"105","x-xss-protection":"0"},"data":""}}

@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> \n> \n> ```ascii\n>  ____________________________________________________________________________________________________________________________________________________________________________________________\n> < The object-oriented model makes it easy to build up programs by accretion. What this often means, in practice, is that it provides a structured way to write spaghetti code. - Paul Graham >\n>  --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n>   \\\n>    \\   (\\__/)\n>        (•ㅅ•)\n>        /   づ\n> ```\n> \n> <sub>✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.</sub>\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a configurable, rule-based policy engine for tool-call filtering, integrates it into Config and ToolCallingLLM with lazy enforcer initialization, adds expression evaluation dependency, extensive tests, and comprehensive documentation for policy configuration and semantics.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy engine: PolicyConfig/PolicyRule/AllowCondition, sandboxed Python and Bash condition evaluation, PolicyEnforcer, PolicyResult, rule matching, global enforcer management and logging.|\n|**Configuration & Integration** <br> `holmes/config.py`, `holmes/core/tool_calling_llm.py`|Adds `policy: Optional[PolicyConfig]` and cached `policy_enforcer` property to Config; ToolCallingLLM accepts/injects a PolicyEnforcer and short-circuits tool invocation when policy denies.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive unit tests covering rule validation, expression evaluation, matching, default-allow/deny semantics, context/vars, Bash templates, and real-world scenarios; minor test cleanup/formatting changes.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy filtering documentation and small doc formatting adjustments.|\n|**Dependencies** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                            |\n| :---------------: | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                            |\n|    Title check    | ✅ Passed | The title directly and accurately describes the main change: adding policy-based filtering for tool calls, which is the core feature introduced across multiple files. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:27:11 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8088:28CDA1:19F368:6F65FE:698A0ADC","x-ratelimit-limit":"15000","x-ratelimit-remaining":"14895","x-ratelimit-reset":"1770656812","x-ratelimit-resource":"core","x-ratelimit-used":"105","x-xss-protection":"0"},"data":""}}

@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> \n> \n> ```ascii\n>  ____________________________________________________________________________________________________________________________________________________________________________________________\n> < The object-oriented model makes it easy to build up programs by accretion. What this often means, in practice, is that it provides a structured way to write spaghetti code. - Paul Graham >\n>  --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n>   \\\n>    \\   (\\__/)\n>        (•ㅅ•)\n>        /   づ\n> ```\n> \n> <sub>✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.</sub>\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a configurable, rule-based policy engine for tool-call filtering, integrates it into Config and ToolCallingLLM with lazy enforcer initialization, adds expression evaluation dependency, extensive tests, and comprehensive documentation for policy configuration and semantics.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy engine: PolicyConfig/PolicyRule/AllowCondition, sandboxed Python and Bash condition evaluation, PolicyEnforcer, PolicyResult, rule matching, global enforcer management and logging.|\n|**Configuration & Integration** <br> `holmes/config.py`, `holmes/core/tool_calling_llm.py`|Adds `policy: Optional[PolicyConfig]` and cached `policy_enforcer` property to Config; ToolCallingLLM accepts/injects a PolicyEnforcer and short-circuits tool invocation when policy denies.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive unit tests covering rule validation, expression evaluation, matching, default-allow/deny semantics, context/vars, Bash templates, and real-world scenarios; minor test cleanup/formatting changes.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy filtering documentation and small doc formatting adjustments.|\n|**Dependencies** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                            |\n| :---------------: | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                            |\n|    Title check    | ✅ Passed | The title directly and accurately describes the main change: adding policy-based filtering for tool calls, which is the core feature introduced across multiple files. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:27:11 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"8088:28CDA1:19F368:6F65FE:698A0ADC","x-ratelimit-limit":"15000","x-ratelimit-remaining":"14895","x-ratelimit-reset":"1770656812","x-ratelimit-resource":"core","x-ratelimit-used":"105","x-xss-protection":"0"},"data":""}}

@coderabbitai

coderabbitai Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nAdds a new policy-based filtering subsystem for tool calls (policy models, enforcer, evaluation), integrates lazy enforcer creation into Config, wires runtime checks into ToolCallingLLM to short-circuit denied calls, adds tests and docs, and introduces simpleeval dependency.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Policy Engine Core** <br> `holmes/core/policy.py`|New policy module: PolicyConfig/PolicyRule/AllowCondition, PolicyEnforcer, PolicyResult, sandboxed Python (simpleeval) and Bash (templated) condition evaluation, rule matching, globals, and logging.|\n|**Configuration** <br> `holmes/config.py`|Adds `policy: Optional[PolicyConfig]` field and cached `policy_enforcer` PrivateAttr with lazy init via init_policy_from_config.|\n|**Integration — Tool Invocation** <br> `holmes/core/tool_calling_llm.py`|ToolCallingLLM accepts optional `policy_enforcer` (falls back to global) and checks policy in _directly_invoke_tool_call, returning a denial result without invoking the tool when disallowed.|\n|**Tests** <br> `tests/core/test_policy.py`, `tests/plugins/...`|Extensive new tests for policy semantics, expression and template evaluation, matching, default allow/deny, AND semantics, context/vars, error paths; minor test cleanup in plugins tests.|\n|**Documentation** <br> `docs/reference/policy-filtering.md`, `docs/development/...`|New comprehensive policy-filtering documentation and small doc formatting/history edits.|\n|**Dependencies / Project** <br> `pyproject.toml`|Adds runtime dependency `simpleeval = \"^1.0.0\"` for safe Python expression evaluation.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    actor User\n    participant Config\n    participant PolicyEnforcer\n    participant ToolCallingLLM\n    participant Tool\n\n    User->>Config: Initialize with policy config\n    Config->>PolicyEnforcer: Create via lazy property (policy_enforcer)\n    ToolCallingLLM->>ToolCallingLLM: Receive policy_enforcer (global or injected)\n\n    User->>ToolCallingLLM: Request tool invocation\n    ToolCallingLLM->>PolicyEnforcer: check(tool_name, params, context)\n\n    rect rgba(200, 100, 100, 0.5)\n        PolicyEnforcer->>PolicyEnforcer: Match rules against tool_name\n        PolicyEnforcer->>PolicyEnforcer: Evaluate conditions (Python/Bash) in sandbox\n    end\n\n    alt Policy allows\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=true)\n        ToolCallingLLM->>Tool: Invoke tool with params\n        Tool-->>ToolCallingLLM: Return result\n        ToolCallingLLM-->>User: Return tool result\n    else Policy denies\n        PolicyEnforcer-->>ToolCallingLLM: PolicyResult(allowed=false, message)\n        ToolCallingLLM-->>User: Return denial StructuredToolResult\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~45 minutes\n\n## Possibly related PRs\n\n- HolmesGPT/holmesgpt#1355 — Modifies `holmes/core/tool_calling_llm.py`, overlapping changes to ToolCallingLLM initialization and invocation flow.  \n- HolmesGPT/holmesgpt#919 — Also touches `holmes/core/tool_calling_llm.py`, related to tool invocation behavior and initialization.\n\n## Suggested reviewers\n\n- arikalon1\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |\n| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 79.10% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                               |\n| :---------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------------- |\n| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                               |\n|    Title check    | ✅ Passed | The title accurately captures the main change: adding a policy-based filtering system for controlling tool calls throughout the codebase. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=HolmesGPT/holmesgpt&utm_content=1534)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}},"response":{"url":"https://api.github.com/repos/HolmesGPT/holmesgpt/issues/comments/3871408142","status":500,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-length":"0","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Mon, 09 Feb 2026 16:37:46 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-media-type":"github.v3; format=json","x-github-request-id":"0850:21AE59:110E80B:495AC18:698A0D58","x-ratelimit-limit":"60","x-ratelimit-remaining":"0","x-ratelimit-reset":"1770658413","x-ratelimit-resource":"core","x-ratelimit-used":"1864","x-xss-protection":"0"},"data":""}}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@docs/reference/policy-filtering.md`:
- Around line 222-234: The Python example under the allow_if python block uses
the walrus operator (:=) which simpleeval doesn't support; replace the inline
walrus usage by assigning account separately before the permission check: call
http_get(...) and store its .get(...)[0].get("accountId") into an account
variable, then call http_post(...) using that account to evaluate
.get("hasPermission", False); update the allow_if python block to perform these
two sequential operations (assign account, then call http_post) and remove the
walrus expression so simpleeval can evaluate it.
🧹 Nitpick comments (3)
holmes/core/policy.py (2)

520-622: HTTP helpers silently return {} on failure — policy expressions may misinterpret this.

Both _http_get and _http_post return {} on any error (network failure, 4xx/5xx, non-JSON response). A policy expression like http_get(...).get("hasPermission", False) will evaluate to False on network error, which denies the tool call. This is fail-safe (good), but the user gets no indication that the denial was due to a network error vs. an actual permission denial. Consider having the error path set a distinguishable marker or at least log at a higher level when these are used within policy evaluation.


625-648: Global mutable singleton _default_enforcer is not thread-safe.

set_policy_enforcer and get_policy_enforcer read/write a module-level global without synchronization. In practice this is likely only set at startup, but if init_policy_from_config is ever called concurrently (e.g., config reload), it could race. Consider using a threading.Lock or documenting that it must only be called during single-threaded initialization.

docs/reference/policy-filtering.md (1)

371-377: Consider removing the Security Considerations section.

As per coding guidelines for docs/**/*.md: "skip Security Best Practices sections - assume users understand basics like rotating credentials, using least privilege, and deleting local secrets."

That said, some of these points (e.g., sandboxing scope, quote filter for bash) are specific to this feature and may be worth keeping in a more concise form — use your judgment.

Comment thread docs/reference/policy-filtering.md
- Add RateLimitConfig with sliding window, max_total, and max_per group
- RateLimitTracker provides thread-safe in-memory call tracking
- Rate limits compose with allow_if: denied calls don't count against limits
- Support duration strings: "30s", "5m", "1h", "1d", "1h30m"
- Group by any dotted path: params.namespace, context.cluster, etc.
- Each rule tracks counters independently
- Add 26 new tests covering tracker, config validation, and integration
- Update documentation with rate limiting examples

https://claude.ai/code/session_01ELTfzzxV3mBS2RzTAZiXeX
Signed-off-by: Claude <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
holmes/core/policy.py (2)

504-512: Use logger.exception instead of logger.error to preserve tracebacks.

Per Ruff TRY400, logger.exception automatically includes the traceback. Applies to lines 505, 535, 564, and 571.

-            logger.error(
+            logger.exception(
                 f"Policy rule '{rule.name}' evaluation failed: {e}. Denying by default."
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@holmes/core/policy.py` around lines 504 - 512, Replace the logger.error calls
inside the exception handlers in the policy evaluation code with
logger.exception so tracebacks are preserved; specifically update the except
blocks that log "Policy rule '{rule.name}' evaluation failed..." (and the other
similar handlers referenced around rule.name and PolicyResult creation) to call
logger.exception with the same message before returning the PolicyResult,
keeping the existing message text and parameters unchanged so the traceback is
included for debugging.

345-365: Annotate SAFE_FUNCTIONS with ClassVar to satisfy Ruff RUF012.

+    from typing import ClassVar
     ...
-    SAFE_FUNCTIONS = {
+    SAFE_FUNCTIONS: ClassVar[dict[str, Any]] = {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@holmes/core/policy.py` around lines 345 - 365, Annotate the SAFE_FUNCTIONS
class variable with ClassVar to satisfy RUF012: import ClassVar (and typing
helpers like Dict, Callable, Any if not present) and change the declaration of
SAFE_FUNCTIONS to include a ClassVar type hint (e.g., SAFE_FUNCTIONS:
ClassVar[Dict[str, Callable[..., Any]]]) inside the Policy class so the linter
recognizes it as a class-level constant.
docs/reference/policy-filtering.md (1)

465-471: Consider removing the "Security Considerations" section.

As per coding guidelines: "Skip 'Security Best Practices' sections in documentation. Assume users understand basics like rotating credentials, using least privilege, and deleting local secrets. These sections add little value." Most of the points here (RBAC defense-in-depth, default deny, input validation) are already covered contextually in the examples above.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/reference/policy-filtering.md` around lines 465 - 471, Remove the entire
"## Security Considerations" section (the header and its bullet list) from the
policy-filtering documentation; delete the block containing the header text
"Security Considerations" and the four bullets about enforcement level, RBAC on
the Holmes ServiceAccount, sandboxed Python expressions, Bash input validation
via the `quote` filter, and the "default: deny" recommendation so the doc omits
the redundant security-best-practices section already covered elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@holmes/core/policy.py`:
- Around line 729-741: _policy.py currently exposes all environment variables
via the static helper _get_env (used by env() in policy expressions), allowing
policies to read secrets; restrict this by adding an allowlist mechanism:
introduce configurable allowed_env_names and allowed_env_prefixes checked inside
_get_env (or route env() calls through a new sanitizer) so only matching names
are returned, and update _get_env to log/raise when access is denied; also add a
clear comment/docstring near _get_env and the env() entrypoint documenting the
trust boundary and how to configure the allowlist._get_env and any env() wrapper
are the symbols to modify._
- Around line 744-791: _http_get (and the sibling http_post) currently allow
requests to arbitrary URLs which creates SSRF risk; add hostname/IP validation
that blocks requests to well-known metadata hosts (e.g., 169.254.169.254,
metadata.google.internal), RFC1918/private ranges, link-local addresses and
localhost before making the requests. Implement this by resolving the target
host to one or more IPs using socket.getaddrinfo (or ipaddress.ip_address) and
checking each IP against private/link-local/loopback networks, and deny the
request (log and return {} or raise a controlled exception) if any resolved IP
is in a blocked range; apply the same validation for direct IP URLs and for
http_post to ensure consistent protection. Ensure the checks run early in
_http_get and http_post and that Authorization handling and error logging remain
unchanged.

---

Duplicate comments:
In `@docs/reference/policy-filtering.md`:
- Around line 453-456: The fenced code block containing the log output starting
with "Policy denied tool 'kubectl_get'..." should include a language identifier
to satisfy markdownlint MD040; change the opening fence from ``` to ```text so
the block becomes a plain-text code block and renders as log output.
- Line 113: The docs overclaim available builtins; change the sentence that
lists "Standard Python functions..." to either enumerate the exact functions
from SAFE_FUNCTIONS or replace it with a direct link/reference to where
SAFE_FUNCTIONS is defined (so users know the exact allowed set). Locate the
mention in the policy-filtering documentation and update the wording to remove
"etc." and point readers to the SAFE_FUNCTIONS constant or its source file (or
paste the exact list) so only the explicitly allowed functions are shown.
- Around line 256-267: The Python example uses the walrus operator (account :=
http_get(...)) which simpleeval cannot parse (raises FeatureNotAvailable);
update the snippet to avoid the walrus by performing the http_get and assigning
its result to account in a separate expression (call
http_get(...).get("results", [{}])[0].get("accountId") into account before the
and-check) or else replace the example with the Bash/Option B alternative; refer
to the python block, the http_get call and the account variable when making the
change and ensure no use of := remains so simpleeval can evaluate it.

In `@holmes/core/policy.py`:
- Line 650: In _render_template remove the inline "import re as re_module" and
replace all uses of re_module with the top-level re import (the file already
imports re); update any references inside the _render_template function to call
re directly and delete the redundant local import to comply with the
module-level import guideline.
- Around line 542-549: The subprocess.run call uses shell=True with a composed
command string in the variable command, which allows command injection when
template variables are user-controlled; fix by either building an args list and
calling subprocess.run with shell=False (pass the command parts as a list) or
ensure every substituted template value is safely quoted using shlex.quote at
the point where the command string is assembled (or apply an automatic quote
filter to template rendering), and keep using the same identifiers (the command
variable and the subprocess.run call) so the fix replaces the unsafe string
invocation with a safely quoted or list-based invocation.
- Around line 490-492: The current logger.info call logs the full params dict
(variable params) which may leak secrets; update the logging in the Policy
decision path (the logger.info that mentions tool_name and message) to log only
parameter keys or a redacted summary (e.g., list(params.keys()) or masked
placeholders) instead of values, preserving tool_name and message but never
serializing param values; ensure you update the specific logger.info invocation
that reports "Policy denied tool" so only safe metadata is emitted.
- Around line 484-486: The shared self._evaluator is mutated concurrently in
_evaluate_python (setting self._evaluator.names then calling
self._evaluator.eval), causing a race when ToolCallingLLM uses
ThreadPoolExecutor; fix by not mutating the shared evaluator: either create a
fresh EvalWithCompoundTypes instance per call (instantiate a new
EvalWithCompoundTypes with the same config, set its names and call its eval)
inside _evaluate_python, or protect all accesses to self._evaluator with a
dedicated threading.Lock around setting names and calling eval; update
_evaluate_python to use one of these approaches and reference the existing
self._evaluator, _evaluate_python, and EvalWithCompoundTypes symbols when making
the change.
- Around line 362-364: SAFE_FUNCTIONS still exposes introspection helpers
(getattr, hasattr, isinstance), which are sandbox escape vectors; remove these
from the SAFE_FUNCTIONS mapping so they cannot be used in untrusted policy
evaluation. Locate the SAFE_FUNCTIONS definition in policy.py and delete or
comment out the entries for "getattr", "hasattr", and "isinstance" (or replace
them with safe, limited wrappers if specific safe behavior is required),
ensuring any code paths that previously relied on
SAFE_FUNCTIONS["getattr"/"hasattr"/"isinstance"] are updated to avoid using
them.
- Around line 477-482: The current construction of names allows rule.vars to
silently overwrite the reserved keys ("tool", "params", "context"); detect any
conflicts between rule.vars.keys() and the reserved keys and fail fast (raise a
clear exception including the conflicting keys and the rule identifier) or
alternatively enforce precedence by merging as {**rule.vars, "tool": tool_name,
"params": params, "context": context} so the explicit values (tool_name, params,
context) cannot be overridden; update the code around the names dict (the names
assignment that uses rule.vars) to implement one of these fixes and include a
helpful error message referencing the rule if you choose the validation
approach.

In `@tests/core/test_policy.py`:
- Around line 45-55: Add a single top-level import for pytest (near the other
module imports) and remove the inline "import pytest" statements from the test
functions; specifically delete the inline imports from test_neither_raises and
test_both_raises (and the other test functions that currently import pytest
inline) so all tests use the module-level pytest import.

---

Nitpick comments:
In `@docs/reference/policy-filtering.md`:
- Around line 465-471: Remove the entire "## Security Considerations" section
(the header and its bullet list) from the policy-filtering documentation; delete
the block containing the header text "Security Considerations" and the four
bullets about enforcement level, RBAC on the Holmes ServiceAccount, sandboxed
Python expressions, Bash input validation via the `quote` filter, and the
"default: deny" recommendation so the doc omits the redundant
security-best-practices section already covered elsewhere.

In `@holmes/core/policy.py`:
- Around line 504-512: Replace the logger.error calls inside the exception
handlers in the policy evaluation code with logger.exception so tracebacks are
preserved; specifically update the except blocks that log "Policy rule
'{rule.name}' evaluation failed..." (and the other similar handlers referenced
around rule.name and PolicyResult creation) to call logger.exception with the
same message before returning the PolicyResult, keeping the existing message
text and parameters unchanged so the traceback is included for debugging.
- Around line 345-365: Annotate the SAFE_FUNCTIONS class variable with ClassVar
to satisfy RUF012: import ClassVar (and typing helpers like Dict, Callable, Any
if not present) and change the declaration of SAFE_FUNCTIONS to include a
ClassVar type hint (e.g., SAFE_FUNCTIONS: ClassVar[Dict[str, Callable[...,
Any]]]) inside the Policy class so the linter recognizes it as a class-level
constant.

Comment thread holmes/core/policy.py
Comment on lines +729 to +741
@staticmethod
def _get_env(name: str, default: str = "") -> str:
"""
Get environment variable value.

Args:
name: Environment variable name
default: Default value if not set

Returns:
Environment variable value or default
"""
return os.environ.get(name, default)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

env() exposes all environment variables to policy expressions.

Policy expressions can access any environment variable via env(), including secrets injected as env vars (e.g., database credentials, API tokens). This is acceptable if policy authors are fully trusted, but worth documenting the trust boundary explicitly. Consider supporting an allowlist of accessible env var names/prefixes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@holmes/core/policy.py` around lines 729 - 741, _policy.py currently exposes
all environment variables via the static helper _get_env (used by env() in
policy expressions), allowing policies to read secrets; restrict this by adding
an allowlist mechanism: introduce configurable allowed_env_names and
allowed_env_prefixes checked inside _get_env (or route env() calls through a new
sanitizer) so only matching names are returned, and update _get_env to log/raise
when access is denied; also add a clear comment/docstring near _get_env and the
env() entrypoint documenting the trust boundary and how to configure the
allowlist._get_env and any env() wrapper are the symbols to modify._

Comment thread holmes/core/policy.py
Comment on lines +744 to +791
def _http_get(
url: str,
params: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None,
auth: Optional[Union[Tuple[str, str], str]] = None,
timeout: int = 10,
) -> Dict[str, Any]:
"""
Make an HTTP GET request and return JSON response.

Args:
url: URL to request
params: Query parameters
headers: Request headers
auth: Authentication tuple (username, password) or bearer token string
timeout: Request timeout in seconds

Returns:
Parsed JSON response as dict, or empty dict on error

Example:
http_get("https://api.example.com/user", params={"email": "user@example.com"})
"""
try:
request_headers = headers or {}
request_auth = None

# Handle auth - tuple for basic auth, string for bearer token
if isinstance(auth, tuple):
request_auth = auth
elif isinstance(auth, str):
request_headers["Authorization"] = f"Bearer {auth}"

response = requests.get(
url,
params=params,
headers=request_headers,
auth=request_auth,
timeout=timeout,
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.warning(f"HTTP GET failed for {url}: {e}")
return {}
except json.JSONDecodeError as e:
logger.warning(f"HTTP GET response not JSON for {url}: {e}")
return {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

SSRF risk: http_get/http_post allow requests to arbitrary URLs including internal endpoints.

Policy expressions can call http_get("http://169.254.169.254/latest/meta-data/...") to access cloud metadata services, internal APIs, or localhost services. Consider:

  1. Adding a URL allowlist or blocklist (e.g., deny RFC 1918 ranges, link-local addresses)
  2. Documenting the trust boundary — policy authors have equivalent access to the process's network
  3. At minimum, blocking well-known metadata endpoints (169.254.169.254, metadata.google.internal, etc.)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@holmes/core/policy.py` around lines 744 - 791, _http_get (and the sibling
http_post) currently allow requests to arbitrary URLs which creates SSRF risk;
add hostname/IP validation that blocks requests to well-known metadata hosts
(e.g., 169.254.169.254, metadata.google.internal), RFC1918/private ranges,
link-local addresses and localhost before making the requests. Implement this by
resolving the target host to one or more IPs using socket.getaddrinfo (or
ipaddress.ip_address) and checking each IP against private/link-local/loopback
networks, and deny the request (log and return {} or raise a controlled
exception) if any resolved IP is in a blocked range; apply the same validation
for direct IP URLs and for http_post to ensure consistent protection. Ensure the
checks run early in _http_get and http_post and that Authorization handling and
error logging remain unchanged.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants