Skip to content

Close #11 #12 #13: loopback operator security, mode API cleanup, script walker consolidation#14

Merged
clopca merged 4 commits into
mainfrom
codex/issue-11-12-13-hardening
Feb 16, 2026
Merged

Close #11 #12 #13: loopback operator security, mode API cleanup, script walker consolidation#14
clopca merged 4 commits into
mainfrom
codex/issue-11-12-13-hardening

Conversation

@clopca

@clopca clopca commented Feb 16, 2026

Copy link
Copy Markdown
Owner

Summary

This PR closes follow-up issues #11, #12, and #13 with focused, production-quality changes that prioritize security boundaries and maintainability.

#11 Harden local-only operator route security model

  • Removed header-based route gating for /v1/queue and /v1/queue/process in favor of loopback listener boundary controls.
  • Added loopback hostname guard utility and enforced it at both server startup points:
    • dashboard server startup
    • platform worker HTTP startup
  • Added tests for direct/proxy-like header scenarios and loopback guard behavior.
  • Updated API/dashboard/openapi docs to reflect the security model and threat assumptions (headers are not auth controls).

#12 Reduce dead exports in mode resolver API surface

  • Removed unused resolver export (getDefaultModeConfig).
  • Made resolver source typing internal and decoupled loader cache typing from resolver internals via inferred return types.
  • Preserved mode behavior and existing mode tests.

#13 Consolidate duplicate file-walk utilities in architecture scripts

  • Added shared script utility: scripts/utils/file-walk.ts.
  • Refactored architecture scripts to consume shared utility with configurable extension filters.
  • Added tests for:
    • missing directories
    • nested trees
    • extension filter correctness
    • deterministic sorted output
  • Added short engineering docs note for the shared utility location.

Validation

  • bun run lint
  • bun run typecheck
  • bun run test:repo
  • bun run check:architecture
  • bun run check:boundaries

All passed locally.

Commit structure

  1. fix(http): enforce loopback boundary for operator routes
  2. refactor(scripts): consolidate architecture file-walk utility
  3. refactor(modes): remove dead resolver exports

Checklist


Note

Medium Risk
Modifies the security boundary for operator queue endpoints by removing header-based gating and relying solely on loopback-only server binding; misconfiguration could unintentionally expose operator routes. Other changes are low-risk refactors with added test coverage.

Overview
Security model change: Removes header-based locality checks from operator queue endpoints (/v1/queue, /v1/queue/process) and documents that these routes do not trust Host/X-Forwarded-For; enforcement is now via loopback-only listener binding.

Adds a small loopback hostname guard (assertLoopbackHostname/isLoopbackHostname) and applies it to the dashboard and platform-worker HTTP servers, with new tests validating loopback acceptance and that queue routes ignore header spoofing.

Maintainability refactors: Consolidates architecture scripts’ recursive file walking into scripts/utils/file-walk.ts (deterministic ordering + extension filtering) with tests, and reduces internal workflow mode resolver API surface by removing dead exports and tightening loader cache typing.

Written by Cursor Bugbot for commit caae2fb. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • Security
    • Enforced loopback listener binding (127.0.0.1, ::1, localhost) for operator queue routes; Host and X-Forwarded-For headers are not trusted for authorization.
  • Documentation
    • Updated API docs and OpenAPI summaries to reflect loopback-only binding and the revised security model.

@coderabbitai

coderabbitai Bot commented Feb 16, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR enforces loopback-only server binding checks (replacing header-based locality checks), consolidates file-walking into scripts/utils/file-walk.ts, and reduces the mode resolver public surface. Docs and tests are updated to reflect these changes.

Changes

Cohort / File(s) Summary
Documentation & Changelog
CHANGELOG.md, docs/api.md, docs/changelog.md, docs/dashboard.md, docs/engineering/technical-debt-policy.md, docs/openapi.v1.json
Updated wording to specify loopback (127.0.0.1 / ::1 / localhost) binding checks, clarified that Host/X-Forwarded-* headers are not trusted, and added technical-debt note about shared file-walk utility.
Shared File-Walk Utility
scripts/utils/file-walk.ts
New walkFiles() and FileWalkOptions for deterministic recursive file discovery with optional extension filtering and ignored-directory support.
Architecture Scripts Refactoring
scripts/architecture-health-report.ts, scripts/check-architecture-fitness.ts
Replaced ad-hoc directory traversal with calls to the new walkFiles() utility; removed duplicated traversal code and some unused fs imports.
Loopback Host Validation
src/adapters/http/loopback.ts
Added isLoopbackHostname() and assertLoopbackHostname() utilities for validating/enforcing loopback hostnames with contextual error messages.
HTTP Server & Security Model
src/adapters/http/server.ts
Removed header-based/local-host access guard utilities and their runtime checks on /v1/queue and /v1/queue/process, shifting enforcement to server binding validation.
Server Initialization
src/index.ts, src/platform-worker.ts
Added startup assertions using assertLoopbackHostname() and replaced hardcoded host strings with local hostname constants. Also centralized envelope parsing in worker request handling.
Mode System API Reduction
src/modes/resolver.ts, src/modes/loader.ts
Made ModeConfigSource non-exported and removed getDefaultModeConfig() export; adjusted loader internal typing to use ModeResolverV2 return types.
Tests
tests/adapters/http/loopback.test.ts, tests/scripts/file-walk.test.ts, tests/servers/http-server.test.ts
Added loopback hostname tests, file-walk deterministic/extension/ignore tests, and updated server tests to reflect removal of host-header-based authorization checks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I hopped the loopback lanes with care,

No tricksy headers find me there.
Files now march in tidy rows,
Old exports pruned — the garden grows.
A tiny rabbit nods, delighted — fair!

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning 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 (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main focus: hardening loopback-based operator security, reducing mode resolver API surface, and consolidating script file-walking utilities—all three major changes addressed in the PR.
Description check ✅ Passed The description comprehensively covers all three issues with clear implementation details, includes validation results, commit structure, and a checklist confirming all work is complete. However, the quality gates section of the template is not filled in.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/issue-11-12-13-hardening

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

❤️ Share

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

@clopca

clopca commented Feb 16, 2026

Copy link
Copy Markdown
Owner Author

@cursor review

@greptile-apps

greptile-apps Bot commented Feb 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR consolidates three focused refactoring tasks with security hardening as the primary driver.

#11 Loopback Security Hardening: Removed header-based access control for /v1/queue and /v1/queue/process operator routes. The security model now relies exclusively on loopback binding enforcement at server startup via assertLoopbackHostname(). This is a more robust security boundary since headers can be spoofed, while network-level isolation cannot. Both dashboard and platform worker servers now enforce 127.0.0.1 binding before starting. Tests were added to verify header values don't affect access, and documentation was updated to reflect the new threat model.

#12 Mode Resolver API Cleanup: Made ModeConfigSource interface internal by removing its export from resolver.ts. Removed the unused getDefaultModeConfig export. The loader now uses inferred return types instead of directly importing the source type, decoupling the public API from internal implementation details while maintaining identical runtime behavior.

#13 Script Consolidation: Created shared scripts/utils/file-walk.ts utility to eliminate duplicate file-walking logic across architecture scripts. The utility provides configurable extension filtering and deterministic sorted output. Both architecture-health-report.ts and check-architecture-fitness.ts now use this shared implementation, reducing code duplication and maintenance burden.

Confidence Score: 5/5

  • This PR is safe to merge with no identified issues
  • All three changes are well-implemented with appropriate test coverage. The security hardening improves the threat model by replacing spoofable headers with network-level isolation. The mode resolver cleanup removes dead code without affecting behavior. The script consolidation follows DRY principles with comprehensive tests. All quality gates passed locally.
  • No files require special attention

Important Files Changed

Filename Overview
src/adapters/http/loopback.ts New loopback hostname guard utility with normalization and assertion functions for enforcing local-only binding
src/adapters/http/server.ts Removed 36 lines of header-based access control logic for /v1/queue endpoints, delegating security to loopback binding
src/index.ts Added loopback hostname assertion at dashboard server startup (line 294) to enforce 127.0.0.1 binding
src/platform-worker.ts Added loopback hostname assertion at platform worker HTTP startup (line 257) to enforce 127.0.0.1 binding
tests/adapters/http/loopback.test.ts New tests for loopback hostname validation covering valid/invalid hostnames and error throwing
tests/servers/http-server.test.ts Updated queue endpoint tests to verify header values don't affect access (lines 290-309)
src/modes/resolver.ts Made ModeConfigSource interface internal and removed unused getDefaultModeConfig export
scripts/utils/file-walk.ts New shared file-walking utility with extension filtering and deterministic sorted output

Flowchart

flowchart TD
    A[Server Startup] --> B{Dashboard Enabled?}
    B -->|Yes| C[assertLoopbackHostname]
    C --> D[Bind to 127.0.0.1 only]
    D --> E[Dashboard Server Running]
    
    F[Platform Worker Startup] --> G{HTTP Port Specified?}
    G -->|Yes| H[assertLoopbackHostname]
    H --> I[Bind to 127.0.0.1 only]
    I --> J[Worker HTTP Running]
    
    E --> K[Queue endpoints accessible]
    J --> L[Events endpoint accessible]
    
    K -.->|No header checks| M[Loopback binding is security boundary]
    L -.->|No header checks| M
    
    M --> N[Network isolation prevents remote access]
    
    style C fill:#90EE90
    style H fill:#90EE90
    style M fill:#FFD700
    style N fill:#87CEEB
Loading

Last reviewed commit: 40d1c61

@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.

🧹 Nitpick comments (4)
src/platform-worker.ts (1)

276-282: Pre-existing: parseEnvelope(body) is called twice for the same request body.

Line 277 calls handleEnvelope(parseEnvelope(body)) and line 278 calls parseEnvelope(body) again to check the command. Consider storing the envelope in a local variable to avoid the redundant parse.

♻️ Suggested fix
+				const envelope = parseEnvelope(body);
 				try {
-					const response = await handleEnvelope(parseEnvelope(body));
-					if ((parseEnvelope(body).command ?? "event") === "shutdown") {
+					const response = await handleEnvelope(envelope);
+					if ((envelope.command ?? "event") === "shutdown") {
 						setTimeout(() => {
 							void shutdown();
 						}, 0);
src/index.ts (1)

314-314: Nit: log message hardcodes 127.0.0.1 instead of using dashboardHostname.

For consistency with the new constant, consider using dashboardHostname in the log string.

♻️ Suggested fix
-			console.log(`[open-mem] Dashboard available at http://127.0.0.1:${port}`);
+			console.log(`[open-mem] Dashboard available at http://${dashboardHostname}:${port}`);
scripts/utils/file-walk.ts (1)

13-14: Unnecessary Set→Array conversion; use the Set directly.

extensionSet is created for deduplication but then immediately spread back into an array. You can use extensionSet directly with .has() combined with path.extname(), which is both clearer and avoids the intermediate allocation.

♻️ Suggested simplification
+import { existsSync, readdirSync, statSync } from "node:fs";
+import { extname, join } from "node:path";
+
 export function walkFiles(rootDir: string, options: FileWalkOptions = {}): string[] {
 	if (!existsSync(rootDir)) return [];
 
 	const { extensions, ignoredDirNames = [] } = options;
 	const extensionSet = extensions ? new Set(extensions) : null;
-	const extensionList = extensionSet ? [...extensionSet] : null;
 	const ignoredDirs = new Set(ignoredDirNames);
 	const files: string[] = [];
 
 	const walk = (dir: string): void => {
 		const entries = readdirSync(dir).sort((a, b) => a.localeCompare(b));
 		for (const entry of entries) {
 			if (ignoredDirs.has(entry)) continue;
 			const fullPath = join(dir, entry);
 			const stat = statSync(fullPath);
 			if (stat.isDirectory()) {
 				walk(fullPath);
 				continue;
 			}
-			if (!extensionList || extensionList.some((ext) => fullPath.endsWith(ext))) {
+			if (!extensionSet || extensionSet.has(extname(fullPath))) {
 				files.push(fullPath);
 			}
 		}
 	};

Note: this assumes extensions are passed in .ext format (e.g., ".ts"), which is already the convention in all call sites.

tests/scripts/file-walk.test.ts (1)

21-55: Consider adding a test for ignoredDirNames.

The test suite covers missing directories, extension filtering, and sort order, but the ignoredDirNames option is untested. A quick test creating a node_modules-like directory and verifying it's skipped would round out coverage.

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@clopca clopca merged commit 1f334a9 into main Feb 16, 2026
2 of 3 checks passed
@clopca clopca deleted the codex/issue-11-12-13-hardening branch February 16, 2026 13:38
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