Skip to content

feat: verify upstream identities - #61

Merged
mohanagy merged 3 commits into
developmentfrom
feat/issue-21-identity-verification
Jul 12, 2026
Merged

feat: verify upstream identities#61
mohanagy merged 3 commits into
developmentfrom
feat/issue-21-identity-verification

Conversation

@mohanagy

@mohanagy mohanagy commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add opt-in, bounded upstream identity verification with exact profile/upstream/session scoping.
  • Gate configured write/destructive operations and expose safe management, audit, and doctor status.
  • Add strict config/public type contracts, real STDIO coverage, and identity safety documentation.

Security impact

  • Credentials are neither read from probe responses nor persisted; fingerprints are bounded and allowlisted.
  • Routing and policy resolve before a configured write/destructive identity gate; failed verification blocks the target operation.
  • Management, redaction, and audit surfaces retain only safe identity status, never raw payloads, arguments, or error bodies.
  • Verification uses the already acquired upstream subprocess session with a read/no-input probe; it adds no provider SDKs or dependencies.
  • Probe, acquisition, mismatch, unsupported, expiry, and doctor failures fail safely with typed outcomes.

Validation

  • npm test
  • npm run lint
  • npm run typecheck
  • npm run build
  • npm run smoke:cli
  • npm run check:pack
  • npm run test:package

Implements #21; targeting development intentionally, so the issue will be closed after merge evidence is verified.

Summary by CodeRabbit

  • New Features

    • Added opt-in upstream identity fingerprint verification with configurable probes and expected account details.
    • Protected write and destructive operations can now require successful identity verification.
    • Added explicit identity verification management and status reporting through MCP tools and readiness checks.
    • Added redacted identity results to audits, with in-memory caching and automatic invalidation after upstream lifecycle changes.
  • Documentation

    • Documented configuration, security boundaries, CLI behavior, lifecycle refresh requirements, and public identity-related types.
  • Bug Fixes

    • Added clear failure handling and policy exit codes for mismatches, unsupported probes, and verification failures.

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

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fa2f4d85-2421-4a03-bf32-ef73785fd46f

📥 Commits

Reviewing files that changed from the base of the PR and between 4f27fee and 52c6a8a.

📒 Files selected for processing (1)
  • tests/identity-docs-contract.test.ts
📝 Walkthrough

Walkthrough

Adds opt-in upstream identity fingerprint verification with strict configuration, in-memory caching, session invalidation, protected-operation gating, MCP management tooling, redacted auditing, doctor reporting, public types, documentation, and comprehensive tests.

Changes

Identity verification and configuration

Layer / File(s) Summary
Configuration contracts and validation
src/config/*, src/identity/identity-types.ts, src/index.ts, docs/config.md, docs/security.md
Adds typed profile/upstream identity configuration, strict schema and runtime validation, bounded probe rules, status types, and public exports.
Runtime verification and lifecycle handling
src/identity/identity-manager.ts, src/upstream/*, tests/identity-manager.test.ts, tests/fixtures/fake-upstream.mjs
Implements safe probing, fingerprint matching, caching, expiry, concurrency coalescing, session-generation invalidation, and failure handling.

MCP enforcement and readiness

Layer / File(s) Summary
MCP routing, enforcement, and audit
src/mcp/server/*, src/audit/*, src/utils/errors.ts, tests/mcp-wrapper.test.ts, tests/operation-pipeline.test.ts
Gates configured write/destructive operations, exposes identity status and verification commands, propagates identity routing context, and records redacted audit evidence.
Doctor reporting and CLI integration
src/cli/*, tests/doctor.test.ts, tests/cli-exit-codes.test.ts
Adds identity readiness checks and maps identity failures to policy exit codes.
Documentation and contract coverage
README.md, CHANGELOG.md, docs/architecture.md, docs/cli.md, docs/library-api.md, tests/identity-docs-contract.test.ts, tests/public-api.test.ts, tests/package-contract.test.ts, tests/config.test.ts
Documents identity behavior and verifies implementation, documentation, configuration validation, and packaged public types remain aligned.

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

Possibly related issues

Possibly related PRs

  • mohanagy/miftah#46: Both modify configuration validation and public contract diagnostics.
  • mohanagy/miftah#53: Both extend typed terminal audit outcomes and audit-trail behavior.
  • mohanagy/miftah#51: Both modify upstream session lifecycle construction and session handling.

Poem

A rabbit checks the upstream gate,
With whiskers tuned to fingerprints straight.
Caches bloom, then softly fade,
Safe audits mark the path we made.
Writes wait till identities align—
Hop, hop, verified design!


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 10.00% 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
Title check ✅ Passed The title matches the main change: adding upstream identity verification.
Description check ✅ Passed The description includes the required Summary, Security impact, and Validation sections and is mostly complete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-21-identity-verification

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
src/cli/doctor.ts (1)

505-568: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Identity verification failures can be mislabeled as tool-discovery failures.

identities.verify(...) (line 537) executes inside the same try block as listTools(session) and is caught by the same catch (line 555), which pushes a DOCTOR_CODES.TOOLS_DISCOVERY failure/warning and calls recordCapabilityFailure(..., "tools", ...). If verify() ever throws instead of returning a non-verified status (current tests only exercise the non-throwing failure path via TEST_FAIL_CALL_TOOL), a healthy tool discovery would be incorrectly reported as failed and the upstream manager's "tools" capability tracking would be corrupted, obscuring the actual (identity) failure.

Isolating identity verification into its own try/catch — pushing unavailableIdentityCheck(target, targetText, "discovery") on error without touching DOCTOR_CODES.TOOLS_DISCOVERY or recordCapabilityFailure("tools", ...) — would make this robust regardless of identities.verify()'s exact throwing contract.

♻️ Suggested refactor
       try {
         const result = await listTools(session);
         runtime.manager.recordCapabilitySuccess(target.profile, "tools", target.upstreamName);
         checks.push(
           check(
             DOCTOR_CODES.TOOLS_DISCOVERY,
             result.truncated ? "warning" : "pass",
             targetText,
             result.truncated
               ? "Tool discovery returned a cursor. Additional tool pages are not currently exposed by the wrapper."
               : `Tool discovery completed with ${result.tools.length} item(s).`,
             result.truncated
               ? "Use only the currently exposed tools until the wrapper supports additional tool pages."
               : noAction()
           )
         );
         const fingerprints = visibleTools.get(target.profile) ?? new Map<string, string>();
         visibleTools.set(target.profile, fingerprints);
         if (recordCollision(checks, target, fingerprints, result.tools, runtime.config.tooling?.collisionStrategy)) {
           incompleteProfiles.add(target.profile);
         }
-        const configuredIdentity = identities.status(target.profile, target.upstreamName);
-        if (configuredIdentity.status === "unconfigured") {
-          checks.push(
-            identityCheck(
-              "skipped",
-              targetText,
-              "No upstream identity verification is configured.",
-              "Configure profile identity verification to validate risky operations."
-            )
-          );
-        } else {
-          const identity = await identities.verify(target.profile, target.upstreamName, session);
-          const required = identityRequired(target);
-          checks.push(
-            identity.status === "verified"
-              ? identityCheck(
-                  "pass",
-                  targetText,
-                  "Configured upstream identity verification completed.",
-                  noAction()
-                )
-              : identityCheck(
-                  required ? "error" : "warning",
-                  targetText,
-                  "Configured upstream identity verification did not complete.",
-                  "Review the configured expected fingerprint and identity probe before relying on risky operations."
-                )
-          );
-        }
       } catch (error) {
         incompleteProfiles.add(target.profile);
         runtime.manager.recordCapabilityFailure(target.profile, "tools", error, target.upstreamName);
         checks.push(
           check(
             DOCTOR_CODES.TOOLS_DISCOVERY,
             discoveryFailureStatus,
             targetText,
             "Tool discovery did not complete.",
             "Review upstream tool discovery before relying on this profile."
-          ),
-          unavailableIdentityCheck(target, targetText, "discovery")
+          )
         );
+        checks.push(unavailableIdentityCheck(target, targetText, "discovery"));
+      }
+
+      try {
+        const configuredIdentity = identities.status(target.profile, target.upstreamName);
+        if (configuredIdentity.status === "unconfigured") {
+          checks.push(
+            identityCheck(
+              "skipped",
+              targetText,
+              "No upstream identity verification is configured.",
+              "Configure profile identity verification to validate risky operations."
+            )
+          );
+        } else {
+          const identity = await identities.verify(target.profile, target.upstreamName, session);
+          const required = identityRequired(target);
+          checks.push(
+            identity.status === "verified"
+              ? identityCheck("pass", targetText, "Configured upstream identity verification completed.", noAction())
+              : identityCheck(
+                  required ? "error" : "warning",
+                  targetText,
+                  "Configured upstream identity verification did not complete.",
+                  "Review the configured expected fingerprint and identity probe before relying on risky operations."
+                )
+          );
+        }
+      } catch {
+        checks.push(unavailableIdentityCheck(target, targetText, "discovery"));
       }
🤖 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/cli/doctor.ts` around lines 505 - 568, Separate the identities.verify
flow from the listTools try/catch in the doctor check. Keep tool discovery
success and failure handling, including recordCapabilityFailure(..., "tools",
...), limited to listTools-related errors; wrap identity verification in its own
try/catch that adds unavailableIdentityCheck(target, targetText, "discovery")
without adding a TOOLS_DISCOVERY failure or recording a tools capability
failure.
🤖 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/schema.ts`:
- Around line 63-139: Keep the identity validation rules synchronized between
identitySchema in schema.ts and the isIdentityConfig runtime guard in
validate-config.ts. Any change to expected-field requirements, requiredForRisk
uniqueness, or JSON/text probe provider and field constraints must be applied
consistently in both validators so they accept and reject the same
configurations.

In `@src/mcp/server/miftah-server.ts`:
- Around line 108-111: In the fields schema construction within the reduce
callback, remove the redundant key-based ternary and assign the `"string"` type
directly. Keep the existing fields iteration and result structure unchanged.

In `@tests/config.test.ts`:
- Line 73: Hoist the static regular expressions used by the tests in
config.test.ts to module scope, including the
profiles.work.identity.requiredForRisk pattern and the probe.provider patterns
at the referenced assertions plus the expected.login pattern. Reuse these
module-level RegExp constants in the corresponding toThrow assertions to satisfy
prefer-static-regex without changing test behavior.

In `@tests/identity-docs-contract.test.ts`:
- Around line 8-18: Resolve the e18e lint failures in
tests/identity-docs-contract.test.ts by hoisting the repeated regex literals
used by unreleasedSection, identityVerificationSection, and the other reported
sections to module scope, then replacing matchAll spread-plus-map expressions
with Array.from(matches, mapper). Preserve all existing matching and mapping
behavior while removing the unnecessary intermediate arrays.

In `@tests/operation-pipeline.test.ts`:
- Around line 57-60: Update the finally cleanup blocks in the affected tests,
including the test around the client and wrapper close calls and the one around
lines 115-118, to remove each mkdtemp directory with recursive, forced removal.
Preserve the existing client and wrapper close operations and follow the cleanup
pattern used by the multi-upstream test.

---

Outside diff comments:
In `@src/cli/doctor.ts`:
- Around line 505-568: Separate the identities.verify flow from the listTools
try/catch in the doctor check. Keep tool discovery success and failure handling,
including recordCapabilityFailure(..., "tools", ...), limited to
listTools-related errors; wrap identity verification in its own try/catch that
adds unavailableIdentityCheck(target, targetText, "discovery") without adding a
TOOLS_DISCOVERY failure or recording a tools capability failure.
🪄 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: f050e60c-ece5-41f0-9522-9a1f5f0c4de6

📥 Commits

Reviewing files that changed from the base of the PR and between fb39071 and 7691bb5.

📒 Files selected for processing (33)
  • CHANGELOG.md
  • README.md
  • docs/architecture.md
  • docs/cli.md
  • docs/config.md
  • docs/library-api.md
  • docs/security.md
  • src/audit/audit-trail.ts
  • src/audit/audit-types.ts
  • src/cli/doctor-report.ts
  • src/cli/doctor.ts
  • src/cli/exit-codes.ts
  • src/config/schema.ts
  • src/config/types.ts
  • src/config/validate-config.ts
  • src/identity/identity-manager.ts
  • src/identity/identity-types.ts
  • src/index.ts
  • src/mcp/server/miftah-server.ts
  • src/mcp/server/operation-pipeline.ts
  • src/upstream/upstream-process-manager.ts
  • src/upstream/upstream-session.ts
  • src/utils/errors.ts
  • tests/cli-exit-codes.test.ts
  • tests/config.test.ts
  • tests/doctor.test.ts
  • tests/fixtures/fake-upstream.mjs
  • tests/identity-docs-contract.test.ts
  • tests/identity-manager.test.ts
  • tests/mcp-wrapper.test.ts
  • tests/operation-pipeline.test.ts
  • tests/package-contract.test.ts
  • tests/public-api.test.ts

Comment thread src/config/schema.ts
Comment thread src/mcp/server/miftah-server.ts
Comment thread tests/config.test.ts Outdated
Comment thread tests/identity-docs-contract.test.ts Outdated
Comment thread tests/operation-pipeline.test.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohanagy

Copy link
Copy Markdown
Owner Author

Resolved the outside-diff doctor finding in 4f27fee: tool discovery and identity verification now have separate error boundaries. A failed identity verification cannot mark tools discovery failed or mutate tools capability health.

@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 (1)
src/cli/doctor.ts (1)

421-443: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Misleading "discovery" reason reused for identity-verification failures after discovery succeeded.

At line 575, the catch fires only when discoveryCompleted is already true (tool discovery succeeded) but identities.verify(...)/identities.status(...) throws. Reusing unavailableIdentityCheck(target, targetText, "discovery") here produces the message "Identity verification was skipped because tool discovery did not complete," which is inaccurate — discovery completed; the identity check itself failed unexpectedly. This undermines the intent of separating tool-discovery and identity-verification error boundaries: the code paths are separated, but the reported reason text isn't.

🐛 Proposed fix: add a dedicated "verification" reason
-    const unavailableIdentityCheck = (target: DoctorTarget, targetText: string, reason: "startup" | "discovery"): DoctorCheck => {
+    const unavailableIdentityCheck = (
+      target: DoctorTarget,
+      targetText: string,
+      reason: "startup" | "discovery" | "verification"
+    ): DoctorCheck => {
       const configured = identities.status(target.profile, target.upstreamName);
       if (configured.status === "unconfigured") {
+        const prerequisite =
+          reason === "startup" ? "upstream startup" : reason === "discovery" ? "tool discovery" : "identity verification";
         return identityCheck(
           "skipped",
           targetText,
-          `Identity verification was skipped because ${reason === "startup" ? "upstream startup" : "tool discovery"} did not complete.`,
-          `Resolve the ${reason === "startup" ? "startup" : "tool discovery"} check before retrying doctor.`
+          `Identity verification was skipped because ${prerequisite} did not complete.`,
+          `Resolve the ${prerequisite} check before retrying doctor.`
         );
       }
       ...
       } catch {
-        checks.push(unavailableIdentityCheck(target, targetText, "discovery"));
+        checks.push(unavailableIdentityCheck(target, targetText, "verification"));
       }

Also applies to: 543-577

🤖 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/cli/doctor.ts` around lines 421 - 443, The unavailableIdentityCheck
helper currently conflates failed identity verification with incomplete tool
discovery. Add a dedicated “verification” reason to its reason type and message
handling, then update the catch path after discoveryCompleted in the identity
verification flow to pass “verification”; preserve the existing “startup” and
“discovery” messages for their respective paths.
🤖 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/identity-docs-contract.test.ts`:
- Line 75: Replace the inline digit-grouping regex in the identityConfig
expectation with the existing digitGroupPattern constant, matching the reuse
already established in the surrounding assertions.
- Line 128: Update the doctor source assertion in the identity documentation
contract test to match the identityCheck("skipped" fragment while tolerating
arbitrary whitespace between tokens, rather than requiring the current
indentation. Preserve validation of the same semantic call and its skipped
argument.

---

Outside diff comments:
In `@src/cli/doctor.ts`:
- Around line 421-443: The unavailableIdentityCheck helper currently conflates
failed identity verification with incomplete tool discovery. Add a dedicated
“verification” reason to its reason type and message handling, then update the
catch path after discoveryCompleted in the identity verification flow to pass
“verification”; preserve the existing “startup” and “discovery” messages for
their respective paths.
🪄 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: ba1649e5-f372-4aa8-b8f5-b1f438950a7f

📥 Commits

Reviewing files that changed from the base of the PR and between 7691bb5 and 4f27fee.

📒 Files selected for processing (8)
  • src/cli/doctor.ts
  • src/config/validate-config.ts
  • src/mcp/server/miftah-server.ts
  • tests/config.test.ts
  • tests/fixtures/fake-upstream.mjs
  • tests/identity-docs-contract.test.ts
  • tests/identity-manager.test.ts
  • tests/operation-pipeline.test.ts

Comment thread tests/identity-docs-contract.test.ts Outdated
Comment thread tests/identity-docs-contract.test.ts Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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