Skip to content

feat(node-message-broker): analysis authorization — scope binding + capability check#13

Merged
tada5hi merged 3 commits into
masterfrom
feat/analysis-policy
Jun 26, 2026
Merged

feat(node-message-broker): analysis authorization — scope binding + capability check#13
tada5hi merged 3 commits into
masterfrom
feat/analysis-policy

Conversation

@tada5hi

@tada5hi tada5hi commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Node-side authorization for inbound analysis callers (issue #6). The Hub stays analysis-agnostic, so both checks live on the node. Two separable concerns:

  1. Analysis scope — the caller's client must own the path analysis.
  2. Capability (ANALYSIS_SELF_MESSAGE_BROKER_USE) — evaluated against Authup over HTTP, not read from the token's (soon-removed) introspection permissions.

Analysis scope

assertClientOwnsAnalysis(analyses, analysisId, clientId) (in core/analysis/): rejects a caller with no client, and binds the caller to the analysis — the caller's client (from the verified request identity) must equal the analysis's owning client in server-core (analysis.getOne(:id).client_id, via the narrow IAnalysisClientLookup port). A pure rule over one infra-isolating port — no policy class/adapter/DI module.

Capability

A custom HTTP permission binding plugged into Authup's evaluator, so useRequestPermissionChecker(event).check({ name }) resolves the decision against the Hub:

  • AuthupPermissionGateway — wraps POST /permissions/:id/check (authenticated as the node client; the caller's identity rides in the body, which the endpoint honors over the bearer). status === 'success' → grant. Answers are cached for a few seconds (shared, keyed by permission + identity; coalesces in-flight calls, evicts transient failures, ttl<=0 disables).
  • HttpPermissionProvider — implements @authup/access's IPermissionProvider (the engine's one extension seam): grant → policy-less binding, deny → null.
  • Per-request middleware — builds the provider around the verified identity and setRequestPermissionChecker(...), mounted between the authorization middleware and the controllers; skipped in tests.
  • Authup client registered (withAuthupHook() + withAuthupClient()).

Background: the framework hard-codes its permission provider from introspection.permissions with no injection hook, so once that field is removed every real check would deny. The supported path is to override the request permission checker with a custom IPermissionProvider — confirmed by deep-reading @authup/access / @privateaim/server-(http-)kit and the Authup server-core source.

Behavioral note

Registering the Authup client flips mountAuthorizationMiddleware out of fake mode → real token verification for all routes in non-test (previously effectively unauthenticated). Tests stay dry-run/grant-all and the override is skipped, so the suite is network-free.

Scope note

This delivers the authorization building blocks. The route-level check({ name: ANALYSIS_SELF_MESSAGE_BROKER_USE }) + assertClientOwnsAnalysis(...) call sites land with the S5 send/participants endpoints.

Tests (54 passing)

  • scope: allow when client owns analysis; reject unbound caller (and skip lookup), unknown analysis, client↔analysis mismatch
  • gateway: grant on success; deny otherwise; identity mapped into the body; caches within TTL; keyed by permission+identity; transient failure not cached; ttl<=0 disables
  • provider: grant→binding, deny→null, failure propagates

build (tsdown + tsc), eslint, and nx test node-message-broker all clean.

Closes #6

Summary by CodeRabbit

  • New Features
    • Added request-based permission checks for HTTP traffic, with caller identity-aware authorization.
    • Added analysis ownership validation so access can be tied to the owning client.
    • Expanded application startup to include the new auth-related components.
  • Bug Fixes
    • Improved permission decision caching and prevented failed checks from being cached.
    • Made partial and test builds more resilient by allowing optional auth client wiring.
  • Tests
    • Added unit coverage for permission checks, analysis ownership validation, and test doubles.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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
📝 Walkthrough

Walkthrough

The PR adds Authup-backed permission checks, shared authz and analysis lookup contracts, HTTP middleware and application wiring for the new checker, and an analysis ownership policy helper with unit tests.

Changes

Authup authorization wiring

Layer / File(s) Summary
Shared contracts
apps/node-message-broker/src/core/authz/types.ts, apps/node-message-broker/src/core/authz/index.ts, apps/node-message-broker/src/core/analysis/types.ts, apps/node-message-broker/src/core/analysis/index.ts, apps/node-message-broker/src/app/modules/core-client/constants.ts
CallerIdentity, IPermissionCheckGateway, and IAnalysisClientLookup are added, the core index barrels re-export their modules, and CoreClientInjectionKey adds AnalysisClientLookup.
Authup permission gateway
apps/node-message-broker/src/adapters/authz/authup-permission-gateway.ts, apps/node-message-broker/test/unit/adapters/authz/fake-permission-check-client.ts, apps/node-message-broker/test/unit/adapters/authz/authup-permission-gateway.spec.ts
AuthupPermissionGateway calls Authup permission checks with caller identity data, caches results by permission and identity, and the fake client and unit tests cover grant, denial, cache reuse, and retry after errors.
HTTP permission wiring
apps/node-message-broker/src/adapters/authz/http-permission-provider.ts, apps/node-message-broker/src/adapters/http/middleware/permission-checker.ts, apps/node-message-broker/test/unit/adapters/authz/fake-permission-gateway.ts, apps/node-message-broker/test/unit/adapters/authz/http-permission-provider.spec.ts
HttpPermissionProvider delegates permission evaluation to the gateway, and mountPermissionChecker builds a request-scoped checker from verified identity; the fake gateway and unit tests cover grant, denial, and failure paths.
Startup wiring
apps/node-message-broker/src/app/factory.ts, apps/node-message-broker/src/app/modules/core-client/module.ts, apps/node-message-broker/src/app/modules/http/module.ts
createApplication() adds Authup hook and client setup, CoreClientModule registers an analysis client lookup in the container, and HTTPModule accepts an optional authupClient dependency and mounts the permission checker before decorator middleware.
Analysis ownership policy
apps/node-message-broker/src/core/analysis/policy.ts, apps/node-message-broker/test/unit/core/analysis/fake-analysis-client-lookup.ts, apps/node-message-broker/test/unit/core/analysis/policy.spec.ts
assertClientOwnsAnalysis compares the caller client ID with the analysis owner lookup result, and the tests cover matching, missing, unknown, and mismatched ownership cases.

Sequence Diagram(s)

sequenceDiagram
  participant HTTPModule
  participant mountPermissionChecker
  participant HttpPermissionProvider
  participant AuthupPermissionGateway
  participant AuthupClient

  HTTPModule->>mountPermissionChecker: mount checker when authupClient resolves
  mountPermissionChecker->>HttpPermissionProvider: create with CallerIdentity
  HttpPermissionProvider->>AuthupPermissionGateway: holds(permission, identity)
  AuthupPermissionGateway->>AuthupClient: permission.check(permission, { identity })
  AuthupClient-->>AuthupPermissionGateway: status
  AuthupPermissionGateway-->>HttpPermissionProvider: boolean grant
  HttpPermissionProvider-->>mountPermissionChecker: binding or null
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • PrivateAIM/node#12: Extends core-client wiring with a new injection key and lookup registration, which is closely related to this PR’s container and startup changes.

Poem

I hopped through checks with twitchy nose,
Through Authup gates and broker flows.
A client map, a guarded path,
Kept analysis on the right track.
🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements analysis ownership assertion, not the linked issue’s required IAnalysisPolicy.assertMayUse token-capability adapter. Implement the requested analysis policy adapter with assertMayUse(analysisId, token) and capability validation, or update the linked issue if the approach changed.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main theme: analysis authorization via scope binding and capability checks.
Out of Scope Changes check ✅ Passed The added authz adapters, middleware, and tests all support the analysis authorization work described in the PR objectives.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/analysis-policy

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.

@tada5hi tada5hi force-pushed the feat/analysis-policy branch from 593acbd to f14a602 Compare June 25, 2026 12:15
Enforce node-side that an analysis caller is bound to the path analysis. The
Hub stays analysis-agnostic; analysis scope is a node-only rule.

assertClientOwnsAnalysis(analyses, analysisId, clientId) (core/analysis) asserts
the caller's client (from the verified request identity) owns the analysis by
matching it against the analysis's client in server-core (IAnalysisClientLookup,
wrapping analysis.getOne). A missing client or mismatch throws ForbiddenError.

The check is a pure rule over the narrow IAnalysisClientLookup port -- no adapter
class, DI module, or injection key needed. The earlier IAnalysisPolicy /
AnalysisPolicy / AnalysisPolicyModule were dropped as needless ceremony around a
three-line assertion.

The ANALYSIS_SELF_MESSAGE_BROKER_USE capability is enforced separately at the
route via the request permission checker (useRequestPermissionChecker), backed by
Authup -- not read off introspection.permissions (being removed). That route
enforcement lands with the S5 controller.

Closes #6
@tada5hi tada5hi force-pushed the feat/analysis-policy branch from f14a602 to eff7bfc Compare June 25, 2026 12:22

@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: 3

🤖 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 `@apps/node-message-broker/src/adapters/analysis/analysis-policy.ts`:
- Around line 31-38: `assertBelongsToAnalysis` in `AnalysisPolicy` is only
checking `clientId`, which widens authorization to client-level and lets any
analysis under the same client pass. Update the policy to accept and validate
the caller’s analysis identity as well (or derive it from trusted claims), then
compare that verified analysis against `analysisId` instead of only matching
`getClientId(analysisId)` to `clientId`. Keep the existing `ForbiddenError`
paths in place, but make the success condition require the exact analysis to
match.
- Around line 36-38: Wrap the `this.analyses.getClientId(analysisId)` lookup in
`AnalysisPolicy` so any rejection is treated the same as a failed authorization
check and results in `ForbiddenError`, not a raw upstream error. Update the
`isAllowed`/policy method that compares `analysisClientId` and `clientId` to
catch lookup failures and normalize them to the existing `ForbiddenError('The
caller does not belong to this analysis.')`, keeping the failure closed behavior
for `AnalysisPolicy` and `getClientId`.

In
`@apps/node-message-broker/test/unit/adapters/analysis/analysis-policy.spec.ts`:
- Around line 30-50: The unit tests in analysis-policy.spec.ts only match
rejection text, so they can pass for the wrong error type; update the
`policy.assertBelongsToAnalysis` assertions in these cases to verify the promise
rejects with `ForbiddenError` as well as the expected message. Use the existing
`setup()` helper and `analysisPolicy`/`policy.assertBelongsToAnalysis` test
cases to keep the denial contract explicit for the “not bound to a client,”
“unknown analysis,” and “does not own the analysis” 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c24d287-34d7-4b32-a832-8e8a47167aa8

📥 Commits

Reviewing files that changed from the base of the PR and between f61a33d and f14a602.

📒 Files selected for processing (12)
  • apps/node-message-broker/src/adapters/analysis/analysis-policy.ts
  • apps/node-message-broker/src/adapters/analysis/index.ts
  • apps/node-message-broker/src/app/builder.ts
  • apps/node-message-broker/src/app/factory.ts
  • apps/node-message-broker/src/app/modules/analysis-policy/constants.ts
  • apps/node-message-broker/src/app/modules/analysis-policy/index.ts
  • apps/node-message-broker/src/app/modules/analysis-policy/module.ts
  • apps/node-message-broker/src/app/modules/core-client/constants.ts
  • apps/node-message-broker/src/app/modules/core-client/module.ts
  • apps/node-message-broker/src/core/analysis/types.ts
  • apps/node-message-broker/test/unit/adapters/analysis/analysis-policy.spec.ts
  • apps/node-message-broker/test/unit/adapters/analysis/fake-analysis-client-lookup.ts

Comment on lines +31 to +38
async assertBelongsToAnalysis(analysisId: string, clientId: string | undefined): Promise<void> {
if (!clientId) {
throw new ForbiddenError('The caller is not bound to a client.');
}

const analysisClientId = await this.analyses.getClientId(analysisId);
if (!analysisClientId || analysisClientId !== clientId) {
throw new ForbiddenError('The caller does not belong to this analysis.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Analysis-level isolation was widened to client-level isolation.

Line 31 only accepts clientId, and Lines 36-37 authorize solely by matching the analysis owner client. That makes two analyses under the same client interchangeable here, so this policy cannot enforce the stated requirement to reject a caller whose analysis does not match :id. Pass the verified analysis identity into the policy (or resolve it from verified claims) and compare that directly.

🤖 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 `@apps/node-message-broker/src/adapters/analysis/analysis-policy.ts` around
lines 31 - 38, `assertBelongsToAnalysis` in `AnalysisPolicy` is only checking
`clientId`, which widens authorization to client-level and lets any analysis
under the same client pass. Update the policy to accept and validate the
caller’s analysis identity as well (or derive it from trusted claims), then
compare that verified analysis against `analysisId` instead of only matching
`getClientId(analysisId)` to `clientId`. Keep the existing `ForbiddenError`
paths in place, but make the success condition require the exact analysis to
match.

Comment on lines +36 to +38
const analysisClientId = await this.analyses.getClientId(analysisId);
if (!analysisClientId || analysisClientId !== clientId) {
throw new ForbiddenError('The caller does not belong to this analysis.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Normalize lookup failures to ForbiddenError.

If this.analyses.getClientId(analysisId) rejects, the raw upstream error escapes instead of the policy failing closed with ForbiddenError. That turns a dependency hiccup into a 500 on an authorization path.

Suggested fix
-        const analysisClientId = await this.analyses.getClientId(analysisId);
+        let analysisClientId: string | null;
+        try {
+            analysisClientId = await this.analyses.getClientId(analysisId);
+        } catch {
+            throw new ForbiddenError('The caller does not belong to this analysis.');
+        }
+
         if (!analysisClientId || analysisClientId !== clientId) {
             throw new ForbiddenError('The caller does not belong to this analysis.');
         }
📝 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
const analysisClientId = await this.analyses.getClientId(analysisId);
if (!analysisClientId || analysisClientId !== clientId) {
throw new ForbiddenError('The caller does not belong to this analysis.');
let analysisClientId: string | null;
try {
analysisClientId = await this.analyses.getClientId(analysisId);
} catch {
throw new ForbiddenError('The caller does not belong to this analysis.');
}
if (!analysisClientId || analysisClientId !== clientId) {
throw new ForbiddenError('The caller does not belong to this analysis.');
}
🤖 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 `@apps/node-message-broker/src/adapters/analysis/analysis-policy.ts` around
lines 36 - 38, Wrap the `this.analyses.getClientId(analysisId)` lookup in
`AnalysisPolicy` so any rejection is treated the same as a failed authorization
check and results in `ForbiddenError`, not a raw upstream error. Update the
`isAllowed`/policy method that compares `analysisClientId` and `clientId` to
catch lookup failures and normalize them to the existing `ForbiddenError('The
caller does not belong to this analysis.')`, keeping the failure closed behavior
for `AnalysisPolicy` and `getClientId`.

Comment thread apps/node-message-broker/test/unit/core/analysis/policy.spec.ts Outdated
Add the capability half of analysis authorization: the node asks Authup whether
the caller holds a named permission over HTTP, instead of reading it off the
token's (soon-removed) introspection permissions.

- AuthupPermissionGateway wraps POST /permissions/:id/check (authenticated as the
  node client, caller identity in the body); status==='success' is a grant. Answers
  are cached briefly (shared, keyed by permission+identity; coalesces in-flight,
  evicts transient failures; ttl<=0 disables).
- HttpPermissionProvider plugs @authup/access's IPermissionProvider seam (grant ->
  policy-less binding, deny -> null), the engine's one extension point.
- A per-request middleware overrides the request permission checker so
  useRequestPermissionChecker().check({ name }) evaluates through that provider;
  mounted between the authorization middleware and the controllers, skipped in tests.
- Register the Authup client (withAuthupHook + withAuthupClient) so token
  verification is real and the gateway has a client.

The route-level check({ name: ANALYSIS_SELF_MESSAGE_BROKER_USE }) call lands with
the S5 endpoints.
@tada5hi tada5hi changed the title feat(node-message-broker): add analysis policy adapter feat(node-message-broker): analysis authorization — scope binding + capability check Jun 25, 2026

@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

♻️ Duplicate comments (1)
apps/node-message-broker/test/unit/core/analysis/policy.spec.ts (1)

23-40: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the rejection type, not just the message.

The denial cases match only on error text, so they'd still pass for a generic Error (or a leaked downstream error) carrying the same message. Since ForbiddenError is the contract this policy enforces, also assert the rejection type.

💚 Example for one case
+import { ForbiddenError } from '`@ebec/http`';
@@
-        await expect(assertClientOwnsAnalysis(analyses, 'a1', undefined)).rejects.toThrow(/not bound to a client/i);
+        await expect(assertClientOwnsAnalysis(analyses, 'a1', undefined)).rejects.toBeInstanceOf(ForbiddenError);
+        await expect(assertClientOwnsAnalysis(analyses, 'a1', undefined)).rejects.toThrow(/not bound to a client/i);
🤖 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 `@apps/node-message-broker/test/unit/core/analysis/policy.spec.ts` around lines
23 - 40, The policy tests for assertClientOwnsAnalysis currently only verify the
rejection message, so they can pass for the wrong error type. Update the
denial-case assertions in policy.spec.ts to also check that
assertClientOwnsAnalysis rejects with ForbiddenError, using the same test cases
around assertClientOwnsAnalysis and FakeAnalysisClientLookup so the contract is
validated by type as well as message.
🤖 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 `@apps/node-message-broker/src/app/modules/http/module.ts`:
- Around line 84-87: Remove the test-environment bypass in module.ts so the
AuthupPermissionGateway is always mounted whenever authupResult.success is true,
using mountPermissionChecker and the AuthupPermissionGateway adapter without the
isTestEnvironment guard. If client resolution is not reliable in production,
fail startup explicitly instead of silently skipping permission gateway setup,
so the authorization path is consistently enforced outside tests.

---

Duplicate comments:
In `@apps/node-message-broker/test/unit/core/analysis/policy.spec.ts`:
- Around line 23-40: The policy tests for assertClientOwnsAnalysis currently
only verify the rejection message, so they can pass for the wrong error type.
Update the denial-case assertions in policy.spec.ts to also check that
assertClientOwnsAnalysis rejects with ForbiddenError, using the same test cases
around assertClientOwnsAnalysis and FakeAnalysisClientLookup so the contract is
validated by type as well as message.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e13ca52c-fc34-4b86-a56e-28e096c4dab3

📥 Commits

Reviewing files that changed from the base of the PR and between f14a602 and 897a465.

📒 Files selected for processing (19)
  • apps/node-message-broker/src/adapters/authz/authup-permission-gateway.ts
  • apps/node-message-broker/src/adapters/authz/http-permission-provider.ts
  • apps/node-message-broker/src/adapters/authz/index.ts
  • apps/node-message-broker/src/adapters/http/middleware/permission-checker.ts
  • apps/node-message-broker/src/app/factory.ts
  • apps/node-message-broker/src/app/modules/core-client/constants.ts
  • apps/node-message-broker/src/app/modules/core-client/module.ts
  • apps/node-message-broker/src/app/modules/http/module.ts
  • apps/node-message-broker/src/core/analysis/index.ts
  • apps/node-message-broker/src/core/analysis/policy.ts
  • apps/node-message-broker/src/core/analysis/types.ts
  • apps/node-message-broker/src/core/authz/index.ts
  • apps/node-message-broker/src/core/authz/types.ts
  • apps/node-message-broker/test/unit/adapters/authz/authup-permission-gateway.spec.ts
  • apps/node-message-broker/test/unit/adapters/authz/fake-permission-check-client.ts
  • apps/node-message-broker/test/unit/adapters/authz/fake-permission-gateway.ts
  • apps/node-message-broker/test/unit/adapters/authz/http-permission-provider.spec.ts
  • apps/node-message-broker/test/unit/core/analysis/fake-analysis-client-lookup.ts
  • apps/node-message-broker/test/unit/core/analysis/policy.spec.ts
✅ Files skipped from review due to trivial changes (4)
  • apps/node-message-broker/test/unit/adapters/authz/authup-permission-gateway.spec.ts
  • apps/node-message-broker/src/core/analysis/index.ts
  • apps/node-message-broker/test/unit/adapters/authz/fake-permission-check-client.ts
  • apps/node-message-broker/src/core/authz/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/node-message-broker/src/app/modules/core-client/constants.ts
  • apps/node-message-broker/src/app/modules/core-client/module.ts

Comment on lines +84 to +87
if (authupResult.success && !isTestEnvironment) {
const gateway = new AuthupPermissionGateway({ client: authupResult.data });
mountPermissionChecker(app, gateway);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how withAuthupClient registers AuthupClientInjectionKey and whether it can fail silently.
rg -nP -C4 'withAuthupClient|AuthupClientInjectionKey' apps/node-message-broker/src

Repository: PrivateAIM/node

Length of output: 2575


🏁 Script executed:

#!/bin/bash
# Check how mountMiddlewares is called and if authorization is always included.
sed -n '40,70p' apps/node-message-broker/src/app/modules/http/module.ts

Repository: PrivateAIM/node

Length of output: 1613


🏁 Script executed:

#!/bin/bash
# Find the mountMiddlewares function implementation to see how it handles undefined authupClient.
rg -n "function mountMiddlewares|^export.*mountMiddlewares" apps/node-message-broker/src --type ts

Repository: PrivateAIM/node

Length of output: 153


🏁 Script executed:

#!/bin/bash
# Search for mountMiddlewares definition (it might be named differently or in a different file)
rg -n "mountMiddlewares" apps/node-message-broker/src --type ts -A 2 --context 5

Repository: PrivateAIM/node

Length of output: 2448


🏁 Script executed:

#!/bin/bash
# Search in the node_modules or the privateaim package to find the implementation of mountMiddlewares.
# Since it's imported from `@privateaim/server-http-kit`, check if that local package exists in the repo.
fd "server-http-kit" --type d || fd "server-http" --type d

Repository: PrivateAIM/node

Length of output: 153


🏁 Script executed:

#!/bin/bash
# Look for tsconfig paths or check root for the internal packages
ls -la packages/ 2>/dev/null || ls -la apps/

Repository: PrivateAIM/node

Length of output: 319


🏁 Script executed:

#!/bin/bash
# Try to find where the internal libs are located (maybe in root)
find . -maxdepth 2 -type d -name "*server*" 2>/dev/null | head -10
# Also search for the token verifier to understand the "fallback" logic
rg "createAuthupTokenVerifier" apps/node-message-broker/src --type ts -A 5

Repository: PrivateAIM/node

Length of output: 1381


🏁 Script executed:

#!/bin/bash
# Search for files containing "mountMiddlewares" to see if there's a local implementation or if we can infer behavior.
# Also check imports from '`@privateaim/server-kit`' to see what's available.
rg "export.*mountMiddlewares" --type ts

Repository: PrivateAIM/node

Length of output: 153


Remove the isTestEnvironment gate for the AuthupPermissionGateway mount to enforce strict authorization in production.

If authupClient fails to resolve in a production environment, the current logic skips mounting the new permission checker, leaving the application reliant solely on the basic token verifier. This bypasses the required permission gateway logic defined by the AuthupPermissionGateway adapter. The authorization configuration passed to mountMiddlewares accepts an undefined client, which likely defaults to a legacy mode or fails to enrich permissions, violating the principle that the new permission system must be mandatory outside of test environments. Remove the conditional check && !isTestEnvironment (or check else throw) to ensure AuthupPermissionGateway is always mounted when a valid client is resolved, or fail the startup process if the client resolution logic is unreliable.

🤖 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 `@apps/node-message-broker/src/app/modules/http/module.ts` around lines 84 -
87, Remove the test-environment bypass in module.ts so the
AuthupPermissionGateway is always mounted whenever authupResult.success is true,
using mountPermissionChecker and the AuthupPermissionGateway adapter without the
isTestEnvironment guard. If client resolution is not reliable in production,
fail startup explicitly instead of silently skipping permission gateway setup,
so the authorization path is consistently enforced outside tests.

@tada5hi

tada5hi commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews paused.

…nials

Denial tests now assert the rejection is a ForbiddenError (not just the message),
so a generic or leaked downstream error with matching text cannot pass them.

Also document the 1:1 analysis<->client invariant on assertClientOwnsAnalysis:
each analysis has a dedicated client, so matching the caller's client to the
analysis owner is exact analysis-level isolation.
@tada5hi tada5hi merged commit fb1fa78 into master Jun 26, 2026
7 checks passed
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.

S3 — Analysis policy adapter (capability check)

1 participant