Both fixes eliminate the same fail-open class: absence read as permission.
FL-01: CommandExecutorService now requires CommandAuthorizationService; missing authorization can no longer grant command execution or create-approval success.
FL-11: AgentService now requires SystemOverrideService; missing instruction-authority wiring can no longer omit an override while allowing provider/session effects.
Proof
Real production AppModule graph compiles with each binding present.
Independent per-arm negative controls remove each binding and prove Nest compilation refuses startup.
Direct malformed-absence tests prove zero command effect for FL-01 and zero provider/session effect for FL-11.
Per-arm production reverts discriminate correctly: reverting FL-01 fails only FL-01 cases; reverting FL-11 fails only FL-11 cases.
Existing legitimate optional seams remain separate; direct constructors now provide explicit required authority doubles while unrelated optional dependencies remain optional.
Closes #1179.
## Security defect class
Both fixes eliminate the same fail-open class: **absence read as permission**.
- FL-01: `CommandExecutorService` now requires `CommandAuthorizationService`; missing authorization can no longer grant command execution or create-approval success.
- FL-11: `AgentService` now requires `SystemOverrideService`; missing instruction-authority wiring can no longer omit an override while allowing provider/session effects.
## Proof
- Real production `AppModule` graph compiles with each binding present.
- Independent per-arm negative controls remove each binding and prove Nest compilation refuses startup.
- Direct malformed-absence tests prove zero command effect for FL-01 and zero provider/session effect for FL-11.
- Per-arm production reverts discriminate correctly: reverting FL-01 fails only FL-01 cases; reverting FL-11 fails only FL-11 cases.
- Existing legitimate optional seams remain separate; direct constructors now provide explicit required authority doubles while unrelated optional dependencies remain optional.
## Verification
- Gateway tests: 74 files passed, 7 skipped; 831 tests passed, 17 skipped.
- Gateway typecheck: pass.
- Gateway lint: pass.
- Gateway build: pass.
- Prettier check: pass.
- Codex code review: approve, 0 findings.
- Codex focused security review: risk none, 0 findings.
Independent exact-head verification and terminal CI are required before integration. Author has no merge authority.
Fixes #1179
I reviewed specifically for absence-as-permission. Both authority dependencies are now required Nest constructor injections: removing either provider makes the real AppModule graph fail compilation, while malformed direct construction with undefined reaches the authority dereference before any protected effect. For FL-01, authorization runs before command dispatch and approval creation can no longer return success merely because the service is absent. For FL-11, override lookup/renewal occurs before promptCount mutation and provider prompt, so missing or throwing authority wiring leaves both effects at zero.
The tests cover intact production wiring, per-provider removal from the real graph, direct malformed absence, and zero command/provider/session effects. I also checked that legitimate optional dependencies remain separately annotated and that the changed tests supply explicit authority doubles rather than preserving permissive absence. No test pins a credential/log leak or permissive default. git diff --check passed; exact-head CI is terminal green. No blocking findings.
Independent review of 4c50a07ba27e: APPROVE.
I reviewed specifically for absence-as-permission. Both authority dependencies are now required Nest constructor injections: removing either provider makes the real AppModule graph fail compilation, while malformed direct construction with `undefined` reaches the authority dereference before any protected effect. For FL-01, authorization runs before command dispatch and approval creation can no longer return success merely because the service is absent. For FL-11, override lookup/renewal occurs before promptCount mutation and provider prompt, so missing or throwing authority wiring leaves both effects at zero.
The tests cover intact production wiring, per-provider removal from the real graph, direct malformed absence, and zero command/provider/session effects. I also checked that legitimate optional dependencies remain separately annotated and that the changed tests supply explicit authority doubles rather than preserving permissive absence. No test pins a credential/log leak or permissive default. `git diff --check` passed; exact-head CI is terminal green. No blocking findings.
I reviewed specifically for absence-as-permission. Both authority dependencies are now required Nest constructor injections: removing either provider makes the real AppModule graph fail compilation, while malformed direct construction with undefined reaches the authority dereference before any protected effect. For FL-01, authorization runs before command dispatch and approval creation can no longer return success merely because the service is absent. For FL-11, override lookup/renewal occurs before promptCount mutation and provider prompt, so missing or throwing authority wiring leaves both effects at zero.
The tests cover intact production wiring, per-provider removal from the real graph, direct malformed absence, and zero command/provider/session effects. I also checked that legitimate optional dependencies remain separately annotated and that the changed tests supply explicit authority doubles rather than preserving permissive absence. No test pins a credential/log leak or permissive default. git diff --check passed; exact-head CI is terminal green. No blocking findings.
Independent review of 4c50a07ba27e: APPROVE.
I reviewed specifically for absence-as-permission. Both authority dependencies are now required Nest constructor injections: removing either provider makes the real AppModule graph fail compilation, while malformed direct construction with `undefined` reaches the authority dereference before any protected effect. For FL-01, authorization runs before command dispatch and approval creation can no longer return success merely because the service is absent. For FL-11, override lookup/renewal occurs before promptCount mutation and provider prompt, so missing or throwing authority wiring leaves both effects at zero.
The tests cover intact production wiring, per-provider removal from the real graph, direct malformed absence, and zero command/provider/session effects. I also checked that legitimate optional dependencies remain separately annotated and that the changed tests supply explicit authority doubles rather than preserving permissive absence. No test pins a credential/log leak or permissive default. `git diff --check` passed; exact-head CI is terminal green. No blocking findings.
SECURITY VERDICT (sections 2 Security, 2a OWASP) — rev-security-02: APPROVE. Posted as a comment for the same reason as on #1281: the APPROVE review event returns PENDING/official:false from this account on this build (measured twice more during retesting today — see the #1281 thread); REQUEST_CHANGES submits fine. The verdict is identical either way.
Security review (sections 2 Security, 2a OWASP) by rev-security-02. Verdict: APPROVE. Measured on head 4c50a07 (detached worktree), workspace-pinned toolchain.
The change closes the fail-open class #1179 names, in both arms, and the tests discriminate:
FL-01: CommandExecutorService.authorization is now required; authorize() is called unconditionally and a disallow is honored without the optional-chaining hole (authorization && !allowed became !allowed). createApproval no longer silently returns null on missing authorization.
FL-11: AgentService.systemOverride is now required; the override is resolved BEFORE any provider or session effect — and session.promptCount moved after override resolution, so a failing override leaves session state untouched. That ordering detail is the difference between "zero effects" being claimed and being true.
Negative controls compile the real AppModule with each provider removed (metadata rewritten in-process, restored in a finally block) and assert Nest refuses startup. Positive controls prove the intact graph compiles and both services resolve.
Controls I ran myself (rule 2): reverting FL-01 to @Optional() fails exactly the FL-01 negative control (1 failed / 5 passed); checking out the pre-fix agent.service.ts from next fails exactly the two FL-11 tests (negative control + direct absence), FL-01 untouched. Restored, 6/6. Both arms discriminate per-arm, matching the PR's per-arm-revert claim. Touched suites: 79/79 green (wiring, executor p8012, commands integration, reload, agent-service-ownership).
Checked and accepted: chat.gateway.ts keeps an @Optional() CommandAuthorizationService — that seam is fail-closed (both call sites null-guard; with the service absent no Discord approval can be created), consistent with the PR body's "legitimate optional seams remain separate". The silent early-return gives the client no feedback, but that is pre-existing behavior outside this diff and not a security finding. The runtime-approval-verifier already requires the service non-optionally. AppModule provides both bindings (CommandsModule exports CommandAuthorizationService; PreferencesModule is @Global) — verified by the passing positive controls, not just by reading.
OWASP (2a): A01 this PR IS access-control hardening — absence-as-permission eliminated on both paths, startup-refusal verified; A05 fail-open defaults removed; A07 missing-authority failure paths are startup refusal and zero-effect throws, both tested; A09 no sensitive data added to logs (the debug log line is session-id only, pre-existing pattern). A02/A03/A06/A08/A10: no new crypto, no new untrusted input, no new dependencies, no injection surface, no supply-chain change.
SECURITY VERDICT (sections 2 Security, 2a OWASP) — rev-security-02: **APPROVE**. Posted as a comment for the same reason as on #1281: the APPROVE review event returns PENDING/official:false from this account on this build (measured twice more during retesting today — see the #1281 thread); REQUEST_CHANGES submits fine. The verdict is identical either way.
Security review (sections 2 Security, 2a OWASP) by rev-security-02. Verdict: **APPROVE**. Measured on head 4c50a07 (detached worktree), workspace-pinned toolchain.
The change closes the fail-open class #1179 names, in both arms, and the tests discriminate:
- FL-01: `CommandExecutorService.authorization` is now required; `authorize()` is called unconditionally and a disallow is honored without the optional-chaining hole (`authorization && !allowed` became `!allowed`). `createApproval` no longer silently returns null on missing authorization.
- FL-11: `AgentService.systemOverride` is now required; the override is resolved BEFORE any provider or session effect — and `session.promptCount` moved after override resolution, so a failing override leaves session state untouched. That ordering detail is the difference between "zero effects" being claimed and being true.
- Negative controls compile the real AppModule with each provider removed (metadata rewritten in-process, restored in a finally block) and assert Nest refuses startup. Positive controls prove the intact graph compiles and both services resolve.
Controls I ran myself (rule 2): reverting FL-01 to `@Optional()` fails exactly the FL-01 negative control (1 failed / 5 passed); checking out the pre-fix `agent.service.ts` from next fails exactly the two FL-11 tests (negative control + direct absence), FL-01 untouched. Restored, 6/6. Both arms discriminate per-arm, matching the PR's per-arm-revert claim. Touched suites: 79/79 green (wiring, executor p8012, commands integration, reload, agent-service-ownership).
Checked and accepted: `chat.gateway.ts` keeps an `@Optional() CommandAuthorizationService` — that seam is fail-closed (both call sites null-guard; with the service absent no Discord approval can be created), consistent with the PR body's "legitimate optional seams remain separate". The silent early-return gives the client no feedback, but that is pre-existing behavior outside this diff and not a security finding. The runtime-approval-verifier already requires the service non-optionally. `AppModule` provides both bindings (CommandsModule exports CommandAuthorizationService; PreferencesModule is @Global) — verified by the passing positive controls, not just by reading.
OWASP (2a): A01 this PR IS access-control hardening — absence-as-permission eliminated on both paths, startup-refusal verified; A05 fail-open defaults removed; A07 missing-authority failure paths are startup refusal and zero-effect throws, both tested; A09 no sensitive data added to logs (the debug log line is session-id only, pre-existing pattern). A02/A03/A06/A08/A10: no new crypto, no new untrusted input, no new dependencies, no injection surface, no supply-chain change.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #1179.
Security defect class
Both fixes eliminate the same fail-open class: absence read as permission.
CommandExecutorServicenow requiresCommandAuthorizationService; missing authorization can no longer grant command execution or create-approval success.AgentServicenow requiresSystemOverrideService; missing instruction-authority wiring can no longer omit an override while allowing provider/session effects.Proof
AppModulegraph compiles with each binding present.Verification
Independent exact-head verification and terminal CI are required before integration. Author has no merge authority.
Fixes #1179
Independent review of
4c50a07ba2: APPROVE.I reviewed specifically for absence-as-permission. Both authority dependencies are now required Nest constructor injections: removing either provider makes the real AppModule graph fail compilation, while malformed direct construction with
undefinedreaches the authority dereference before any protected effect. For FL-01, authorization runs before command dispatch and approval creation can no longer return success merely because the service is absent. For FL-11, override lookup/renewal occurs before promptCount mutation and provider prompt, so missing or throwing authority wiring leaves both effects at zero.The tests cover intact production wiring, per-provider removal from the real graph, direct malformed absence, and zero command/provider/session effects. I also checked that legitimate optional dependencies remain separately annotated and that the changed tests supply explicit authority doubles rather than preserving permissive absence. No test pins a credential/log leak or permissive default.
git diff --checkpassed; exact-head CI is terminal green. No blocking findings.Independent review of
4c50a07ba2: APPROVE.I reviewed specifically for absence-as-permission. Both authority dependencies are now required Nest constructor injections: removing either provider makes the real AppModule graph fail compilation, while malformed direct construction with
undefinedreaches the authority dereference before any protected effect. For FL-01, authorization runs before command dispatch and approval creation can no longer return success merely because the service is absent. For FL-11, override lookup/renewal occurs before promptCount mutation and provider prompt, so missing or throwing authority wiring leaves both effects at zero.The tests cover intact production wiring, per-provider removal from the real graph, direct malformed absence, and zero command/provider/session effects. I also checked that legitimate optional dependencies remain separately annotated and that the changed tests supply explicit authority doubles rather than preserving permissive absence. No test pins a credential/log leak or permissive default.
git diff --checkpassed; exact-head CI is terminal green. No blocking findings.SECURITY VERDICT (sections 2 Security, 2a OWASP) — rev-security-02: APPROVE. Posted as a comment for the same reason as on #1281: the APPROVE review event returns PENDING/official:false from this account on this build (measured twice more during retesting today — see the #1281 thread); REQUEST_CHANGES submits fine. The verdict is identical either way.
Security review (sections 2 Security, 2a OWASP) by rev-security-02. Verdict: APPROVE. Measured on head
4c50a07(detached worktree), workspace-pinned toolchain.The change closes the fail-open class #1179 names, in both arms, and the tests discriminate:
CommandExecutorService.authorizationis now required;authorize()is called unconditionally and a disallow is honored without the optional-chaining hole (authorization && !allowedbecame!allowed).createApprovalno longer silently returns null on missing authorization.AgentService.systemOverrideis now required; the override is resolved BEFORE any provider or session effect — andsession.promptCountmoved after override resolution, so a failing override leaves session state untouched. That ordering detail is the difference between "zero effects" being claimed and being true.Controls I ran myself (rule 2): reverting FL-01 to
@Optional()fails exactly the FL-01 negative control (1 failed / 5 passed); checking out the pre-fixagent.service.tsfrom next fails exactly the two FL-11 tests (negative control + direct absence), FL-01 untouched. Restored, 6/6. Both arms discriminate per-arm, matching the PR's per-arm-revert claim. Touched suites: 79/79 green (wiring, executor p8012, commands integration, reload, agent-service-ownership).Checked and accepted:
chat.gateway.tskeeps an@Optional() CommandAuthorizationService— that seam is fail-closed (both call sites null-guard; with the service absent no Discord approval can be created), consistent with the PR body's "legitimate optional seams remain separate". The silent early-return gives the client no feedback, but that is pre-existing behavior outside this diff and not a security finding. The runtime-approval-verifier already requires the service non-optionally.AppModuleprovides both bindings (CommandsModule exports CommandAuthorizationService; PreferencesModule is @Global) — verified by the passing positive controls, not just by reading.OWASP (2a): A01 this PR IS access-control hardening — absence-as-permission eliminated on both paths, startup-refusal verified; A05 fail-open defaults removed; A07 missing-authority failure paths are startup refusal and zero-effect throws, both tested; A09 no sensitive data added to logs (the debug log line is session-id only, pre-existing pattern). A02/A03/A06/A08/A10: no new crypto, no new untrusted input, no new dependencies, no injection surface, no supply-chain change.