Skip to content

Fix policy fail-open and redaction false positives - #41

Merged
mohanagy merged 12 commits into
developmentfrom
fix/issue-1-policy-fail-open
Jul 10, 2026
Merged

Fix policy fail-open and redaction false positives#41
mohanagy merged 12 commits into
developmentfrom
fix/issue-1-policy-fail-open

Conversation

@mohanagy

@mohanagy mohanagy commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • fail config validation when a profile references an unknown named policy
  • fail closed at runtime when an explicitly named policy is missing
  • replace broad length-based redaction with deterministic key/exact-value/bearer/provider-token redaction
  • make GitHub preset forward GITHUB_PERSONAL_ACCESS_TOKEN into Docker and pin image tag to v1.1.0
  • align GitHub examples/docs with the generated preset and add regression coverage

Linked issues

Validation

  • npm test
  • npm run typecheck
  • npm run lint
  • npm run build

Summary by CodeRabbit

  • New Features
    • Enhanced the GitHub preset to generate separate work (read/write with confirmation) and personal (read-only) profiles using a pinned upstream image tag and token forwarding.
    • Added explicit policy definitions for the preset’s risk levels.
  • Bug Fixes
    • Fail-closed behavior when a profile references an unknown policy, with a stable POLICY_NOT_FOUND error code.
    • Improved secret/token redaction with smarter key detection and provider-specific token patterns.
  • Documentation
    • Updated README and examples with the pinned image tag, token injection, and a recommended upgrade + smoke-test flow.
  • Tests
    • Expanded coverage for policy resolution, routing denial, preset generation, and redaction behavior.

- validate profile policy references against configured policy names\n- make runtime unknown policy lookup deny by default\n- remove broad 32+ token redaction and keep benign identifiers\n- add regression tests for config, policy engine, MCP path, and redaction

Closes #1

Closes #2

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e8661f8e-940a-4c58-aac1-b7c47d45aaeb

📥 Commits

Reviewing files that changed from the base of the PR and between ad3d01b and 214ed84.

📒 Files selected for processing (1)
  • tests/config.test.ts

📝 Walkthrough

Walkthrough

The PR updates GitHub Docker presets and examples, validates profile policy references, denies unresolved policies at runtime, and replaces generic token redaction with provider-specific and key-based detection.

Changes

GitHub preset credentials

Layer / File(s) Summary
GitHub preset wiring and examples
src/config/presets.ts, examples/*.miftah.json, README.md, docs/examples/github.md, tests/presets.test.ts
GitHub configurations forward profile-specific tokens into Docker, pin the image to v1.5.0, define work and personal profiles, and document upgrade validation steps.

Policy reference safety

Layer / File(s) Summary
Policy reference validation
src/utils/errors.ts, src/config/schema.ts, src/config/validate-config.ts, tests/config.test.ts, README.md
Profile policy names are checked against configured policies and unresolved references produce POLICY_NOT_FOUND.
Fail-closed runtime evaluation
src/policy/policy-engine.ts, tests/routing-policy.test.ts, tests/mcp-wrapper.test.ts
Runtime evaluation denies explicitly named missing policies, including destructive MCP calls.

Secret redaction

Layer / File(s) Summary
Secret classification and redaction patterns
src/secrets/redact.ts, tests/secrets.test.ts
Redaction uses normalized secret-key detection, bearer credentials, and provider-specific token patterns while preserving benign identifiers.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPWrapper
  participant PolicyEngine
  participant ToolCall
  MCPWrapper->>PolicyEngine: evaluate missing policy for write risk
  PolicyEngine->>MCPWrapper: return deny decision
  MCPWrapper->>ToolCall: return POLICY_BLOCKED error
Loading

Poem

Binky, the tokens now safely flow,
Pinned images tell us where to go.
Unknown policies meet a firm “deny,”
Safe IDs hop past redaction’s eye.
The rabbit approves with a cheerful leap!


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Docstring Coverage ❌ Error Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main security changes around policy fail-open and redaction fixes, though it omits the GitHub preset work.
Linked Issues check ✅ Passed The PR satisfies #1-#3 by validating named policies, failing closed at runtime, improving redaction, and updating the GitHub preset/docs.
Out of Scope Changes check ✅ Passed All changes align with the linked issues; the refactor and tests support the policy, redaction, and GitHub preset work.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-1-policy-fail-open

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

- pin github-mcp-server image to v1.1.0 in generated preset and examples\n- pass -e GITHUB_PERSONAL_ACCESS_TOKEN to docker run\n- generate work/personal profile token references in github preset\n- add preset regression test for pinned image and token forwarding

Closes #3

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/examples/github.md (1)

29-38: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Doc example is out of sync: missing requireConfirmation on safe-write.

presets.ts and examples/github.miftah.json both set requireConfirmation: ["write"] on the safe-write policy, but this doc snippet omits it. A user copying this example verbatim would lose the write-confirmation guardrail.

📝 Suggested fix
     "safe-write": {
       "allowRisk": ["read", "write"],
-      "denyRisk": ["destructive"]
+      "denyRisk": ["destructive"],
+      "requireConfirmation": ["write"]
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/examples/github.md` around lines 29 - 38, Update the safe-write policy
example to include requireConfirmation: ["write"], matching the configuration in
presets.ts and examples/github.miftah.json while preserving the existing
allowRisk and denyRisk settings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/config/presets.ts`:
- Around line 5-49: Refactor presetConfig to eliminate repeated preset ===
"github" branching by extracting preset-specific configuration into dedicated
builders or a preset map. Centralize construction of upstream, profiles,
description, defaultProfile, and policies, while preserving existing GitHub,
Sentry, and generic behavior. Use presetConfig and the related MiftahConfig
fields as the primary integration points.

In `@src/config/validate-config.ts`:
- Around line 11-15: Replace the substring-based classification in the config
validation error handler with structured issue metadata: have each validation
issue carry its explicit error code (for default-profile, policy, or schema
failures), then derive the MiftahErrorCode from that field rather than parsing
the joined message. Update the relevant schema/validation issue construction and
the handler around the code variable so user-provided names cannot affect
classification.

In `@src/secrets/redact.ts`:
- Around line 6-14: Update isSecretKey so secretKeyTerms recognizes common
plural and variant forms, including credentials, tokens, secrets, and passwords,
or normalize each key part to its singular equivalent before exact matching.
Preserve the existing API-key and private-key detection while ensuring names
such as GOOGLE_APPLICATION_CREDENTIALS are classified as secret keys.

In `@tests/presets.test.ts`:
- Around line 10-12: Update the docker argument assertion in the presets test to
use an exact array equality matcher instead of expect.arrayContaining,
preserving and validating the required order with the image as the final
argument.
- Around line 4-23: Extend the existing preset test for presetConfig("github",
"github") to assert that config.policies defines both "safe-write" and
"readonly", matching the policy references used by the work and personal
profiles. Keep the assertions focused on policy-key presence so missing or
renamed definitions fail the regression test.

---

Outside diff comments:
In `@docs/examples/github.md`:
- Around line 29-38: Update the safe-write policy example to include
requireConfirmation: ["write"], matching the configuration in presets.ts and
examples/github.miftah.json while preserving the existing allowRisk and denyRisk
settings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ffa83133-d046-4636-8472-8b45b01fb3b3

📥 Commits

Reviewing files that changed from the base of the PR and between b971f75 and 72a4382.

📒 Files selected for processing (15)
  • README.md
  • docs/examples/github.md
  • examples/github.miftah.json
  • examples/multi-upstream.miftah.json
  • src/config/presets.ts
  • src/config/schema.ts
  • src/config/validate-config.ts
  • src/policy/policy-engine.ts
  • src/secrets/redact.ts
  • src/utils/errors.ts
  • tests/config.test.ts
  • tests/mcp-wrapper.test.ts
  • tests/presets.test.ts
  • tests/routing-policy.test.ts
  • tests/secrets.test.ts

Comment thread src/config/presets.ts Outdated
Comment thread src/config/validate-config.ts Outdated
Comment thread src/secrets/redact.ts Outdated
Comment thread tests/presets.test.ts
Comment thread tests/presets.test.ts Outdated
mohanagy and others added 3 commits July 10, 2026 22:41
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/policy/policy-engine.ts (1)

25-29: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fail-closed check is bypassed by an explicit empty-string policyName.

policyName ? this.policies[policyName] : undefined and the subsequent if (policyName && !policy) both use truthy checks, so evaluate("", toolName) falls through to !policy{ action: "allow", ... } instead of denying. This mirrors the same gap in src/config/schema.ts's new policy-reference loop (line ~130); tightening the schema (policy: z.string().min(1).optional()) would prevent "" from ever reaching this method, closing both gaps at the source.

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

In `@src/policy/policy-engine.ts` around lines 25 - 29, Empty-string policy names
bypass fail-closed behavior in evaluate and schema validation. Update the policy
reference schema to use a non-empty optional string, and in
PolicyEngine.evaluate use an explicit undefined check rather than truthy checks
when resolving policyName and denying unknown policies.
src/config/schema.ts (1)

128-138: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Empty-string policy: "" bypasses this validation.

profile.policy && !policyNames.has(...) treats "" as "no policy set", so an explicit empty-string policy reference silently skips validation here and, correspondingly, defaults to "allow" at runtime in PolicyEngine.evaluate (see src/policy/policy-engine.ts lines 27-29) — undermining the fail-closed guarantee for what is arguably an "explicitly named" (if malformed) policy reference.

🛡️ Proposed fix: reject empty policy names at the schema level
 const profileSchema = z.object({
   ...
-  policy: z.string().optional(),
+  policy: z.string().min(1).optional(),
   ...
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/schema.ts` around lines 128 - 138, Reject empty-string policy
references during schema validation: update the profile-policy check in the
profiles validation loop to distinguish an omitted policy from an explicitly
provided empty string, and add a custom issue for the latter using the existing
policy error conventions. Ensure PolicyEngine.evaluate receives only valid or
absent policy names so empty references cannot default to allow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/config/presets.ts`:
- Line 4: Update the GITHUB_MCP_IMAGE constant in presets.ts from
ghcr.io/github/github-mcp-server:v1.1.0 to v1.5.0, unless a documented
compatibility constraint requires retaining the older version.

In `@src/secrets/redact.ts`:
- Line 25: Hoist the /[^a-z0-9]+/ regex into a module-level constant alongside
camelCaseBoundaryPattern, then update isSecretKey to use that constant when
splitting normalized keys instead of creating the literal per call.

---

Outside diff comments:
In `@src/config/schema.ts`:
- Around line 128-138: Reject empty-string policy references during schema
validation: update the profile-policy check in the profiles validation loop to
distinguish an omitted policy from an explicitly provided empty string, and add
a custom issue for the latter using the existing policy error conventions.
Ensure PolicyEngine.evaluate receives only valid or absent policy names so empty
references cannot default to allow.

In `@src/policy/policy-engine.ts`:
- Around line 25-29: Empty-string policy names bypass fail-closed behavior in
evaluate and schema validation. Update the policy reference schema to use a
non-empty optional string, and in PolicyEngine.evaluate use an explicit
undefined check rather than truthy checks when resolving policyName and denying
unknown policies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9a6adc9d-756c-4a0d-9824-2de6d9ad2653

📥 Commits

Reviewing files that changed from the base of the PR and between 72a4382 and cb81d4f.

📒 Files selected for processing (10)
  • docs/examples/github.md
  • src/config/presets.ts
  • src/config/schema.ts
  • src/config/validate-config.ts
  • src/policy/policy-engine.ts
  • src/secrets/redact.ts
  • src/utils/errors.ts
  • tests/config.test.ts
  • tests/presets.test.ts
  • tests/secrets.test.ts

Comment thread src/config/presets.ts Outdated
Comment thread src/secrets/redact.ts Outdated
- pin GitHub MCP Docker preset to verified v1.5.0 release\n- keep generated docs and examples aligned with the preset\n- avoid recompiling secret-key split regex on every redaction

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@README.md`:
- Around line 99-100: Update the README wording in the GitHub preset upgrade
guidance from “that release notes” to “the release notes.”
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9c1e791a-446c-4185-a5a4-9b0cf10c9b83

📥 Commits

Reviewing files that changed from the base of the PR and between cb81d4f and 0901352.

📒 Files selected for processing (7)
  • README.md
  • docs/examples/github.md
  • examples/github.miftah.json
  • examples/multi-upstream.miftah.json
  • src/config/presets.ts
  • src/secrets/redact.ts
  • tests/presets.test.ts

Comment thread README.md Outdated
mohanagy and others added 4 commits July 11, 2026 00:15
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@tests/config.test.ts`:
- Around line 58-72: Hoist the duplicated /POLICY_NOT_FOUND/ regex into a
module-level constant, then use that shared constant in both affected toThrow
assertions, including the tests around validateConfig.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7241ae16-31a3-4c62-853f-968b8b676c3e

📥 Commits

Reviewing files that changed from the base of the PR and between 0901352 and ad3d01b.

📒 Files selected for processing (5)
  • README.md
  • src/config/schema.ts
  • src/policy/policy-engine.ts
  • tests/config.test.ts
  • tests/routing-policy.test.ts

Comment thread tests/config.test.ts Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/config.test.ts (1)

45-75: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that errors retain profile and policy names.

These tests verify POLICY_NOT_FOUND and the typed error code, but they do not lock in the required message context. Add assertions for the affected profile name and supplied policy value so future changes cannot silently remove that diagnostic information.

Also applies to: 77-96

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

In `@tests/config.test.ts` around lines 45 - 75, Extend both profile validation
tests around validateConfig to assert the thrown error message includes the
affected profile name "work" and the supplied policy values "missing-policy" and
"" respectively, while retaining the existing policyNotFoundPattern and typed
error-code assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/config.test.ts`:
- Around line 45-75: Extend both profile validation tests around validateConfig
to assert the thrown error message includes the affected profile name "work" and
the supplied policy values "missing-policy" and "" respectively, while retaining
the existing policyNotFoundPattern and typed error-code assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e3c9c1d3-6a30-45cd-9d6a-e5a470233b2f

📥 Commits

Reviewing files that changed from the base of the PR and between ad3d01b and f1210dd.

📒 Files selected for processing (1)
  • tests/config.test.ts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

1 participant