Security Layer Reference

⚠ HERITAGE EXTRACTION — donor AMS security layer (Wave 8 quarantine)

This file extracts the donor AMS security layer from src/security/ and src/middleware/auth*.js (all deleted R53). The injection-detection regexes, the auth/ACL middleware, and the auth-permissions.js ladder are donor accretion. Phase 0 Colibri ships a 5-stage α middleware chain (tool-lock → schema-validate → audit-enter → dispatch → audit-exit) with no JWT, no API keys, no ACL, no rate limiter. Schema validation in Phase 0 is Zod-based at the dispatch boundary, not regex injection scanning. Authentication, authorization, and rate-limiting are Phase 1+ territory (see ../../security/auth.md heritage banner).

Read this file as donor genealogy only.

The AMS security layer has two sub-layers: (1) src/security/ — static analysis utilities (injection detection, sanitization, validation), and (2) src/middleware/auth.js + src/middleware/acl.js — runtime authentication and authorization. For full auth/ACL documentation see middleware-chain-extraction.md.


src/security/audit.js — Threat Detection

detectSqlInjection(input) → {detected, confidence, patterns[], input}

Tests input against 12 regex patterns plus SQL keyword heuristics.

  • Confidence: 0.2 per regex match + 0.1 per SQL keyword, capped at 1.0
  • Keywords checked: SELECT, INSERT, UPDATE, DELETE, DROP, UNION, EXEC, EXECUTE
  • Returns: { detected: boolean, confidence: number (0–1), patterns: string[], input: string (truncated at 100) }

Pattern categories covered:

  • Classic: %27, ', --, #
  • UNION-based: UNION SELECT
  • Tautology: ' OR '1'='1
  • Stacked queries: '; DROP TABLE
  • Execution: exec sp_*
  • DDL: INSERT INTO, DELETE FROM, DROP TABLE, ALTER TABLE

detectPathTraversal(input) → {detected, confidence, patterns[]}

Tests against 10 patterns:

  • ../, ..\, %2e%2e%2f, %2e%2e\, ..\/%2f, %252e%252e%252f, \..\\, \\..\\
  • Returns same shape as detectSqlInjection.

detectCommandInjection(input) → {detected, confidence, patterns[]}

Patterns:

  • Shell metacharacters: ; cmd, & cmd, | cmd, ` cmd`
  • Subshell: $(cmd), `cmd`
  • Common payloads: ; rm , ; cat , ; echo

scanForSecrets(content, options?) → {found, secrets[]}

Scans text for exposed credentials. Excludes matches containing: example, test, fake, mock, dummy (configurable via options.excludePatterns).

Detected secret types: | Type | Severity | Pattern | |——|———-|———| | api_key | critical | *api_key* = "XXXXXXXX" | | secret_key | critical | *secret_key* = "XXXXXXXX" | | private_key | critical | -----BEGIN * PRIVATE KEY----- | | password | high | password = "XXXXXXXX" | | token | high | *token* = "XXXXXXXXXXXXXXXX" | | aws_key | critical | AKIAXXXXXXXXXXXXXXXX | | github_token | critical | ghp_/gho_/ghu_/ghs_/ghr_* | | slack_token | critical | xoxb-*/xoxa-* |


src/security/sanitizer.js — Input Sanitization

escapeHtml(input) → string

Replaces & < > " ' / with HTML entities.

stripDangerousChars(input) → string

Removes < > & " ' / \ characters.

sanitizeForSql(input) → string

Escapes for use in SQL string contexts (use only when parameterized queries not possible):

  1. Removes null bytes
  2. Doubles single quotes: '''
  3. Escapes backslashes: \\\

sanitizePath(path, options?) → {success, value, error?}

  1. Detects path traversal via detectPathTraversal()
  2. Normalizes path separators (\/)
  3. Removes null bytes and control characters (\x00–\x1F, \x7F–\x9F)
  4. Validates allowed characters: /^[a-zA-Z0-9_\-\.\/]+$/
  5. Options: { basePath, allowedChars } Returns { success: false, error: "..." } on violations.

sanitizeJson(input, options?) → {success, value, error?}

Parses and re-serializes JSON, with optional max-depth and allowed-key filtering.

sanitizeObject(obj, schema?) → object

Recursively sanitizes object values via schema-defined rules.


src/security/validator.js — Input Validation Framework

Built on Zod. Used throughout domain handlers.

ValidationError

class ValidationError extends Error {
  name = "ValidationError"
  code = "VALIDATION_ERROR"
  details: ZodIssue[]
}

SecurityError

class SecurityError extends Error {
  name = "SecurityError"
  code = "SECURITY_VIOLATION"
  threatType: string
  confidence: number
}

sanitizedString(options) → ZodSchema

Creates a string schema with optional: min, max, pattern, email, url, trim, nonempty. Does NOT automatically run security scans — those must be added via .refine().

validateId(id, options?) → {valid, value?, error?, security?}

Validates ID strings:

  • Type check: must be string
  • Length: default max 100 chars
  • Pattern: default /^[a-zA-Z0-9_-]+$/
  • Path traversal check: fails if confidence > 0.5

validateFilePath(filePath, options?) → {valid, value?, error?, details?}

  • Calls detectPathTraversal()
  • Null byte check
  • Extension whitelist/blacklist: default blocked = .exe .bat .cmd .sh .dll .so
  • Options: { allowedExtensions[], blockedExtensions[] }

validateJson(content, options?) → {valid, value?, error?}

Parses JSON string. Returns { valid: true, value: parsedObject } or error.


Authentication Summary (src/middleware/auth.js)

JWT-based, algorithm: HS256, library: jose.

Auth modes (controlled by AMS_AUTH_MODE):

  • trust (default): no auth required
  • token: validate if token present
  • hybrid: prefer token, accept unauthenticated
  • required: reject all requests without valid token

Token claims:

{
  "sub": "userId",
  "iss": "ams-server",
  "aud": "ams-mcp-tools",
  "iat": 1234567890,
  "exp": 1234571490,
  "jti": "tok-<32-hex-chars>",
  "ams": {
    "role": "admin",
    "perms": ["task:create", "roadmap:write"],
    "scope": "project:my-project",
    "session": "session-uuid",
    "name": "My Token"
  }
}

Secret management priority: env var → secret file → auto-generate to ~/.ams/auth-secret (mode 0600). Revocation: in-memory Map, 7-day TTL per entry, interval cleanup.


ACL Summary (src/middleware/acl.js)

Project-scoped RBAC. Graceful degradation: when AMS_USER_ID not set, all tools pass through.

Role levels: owner=4 > admin=3 > member=2 > viewer=1

Enforcement flow:

  1. Check AMS_ACL_ENABLED (= !!AMS_USER_ID)
  2. Look up TOOL_PERMISSIONS[toolName] — defaults to 'member' for unknown tools
  3. null = tool requires no project context → return early
  4. Resolve project via resolveProjectContext() (4-step cascade)
  5. Lookup user role via dbGetUserRole(projectId, userId)
  6. Compare ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole]

Error messages:

  • "ACL: Tool 'X' requires a project context. Set AMS_CURRENT_PROJECT env var, use unified_set_project, or pass project_id parameter."
  • "ACL: Access denied for 'X'. Requires Y role, user has Z"

Token Permission Map Summary (src/middleware/auth-permissions.js)

Parallel permission system for token-based auth (complementary to ACL roles). Permission format: resource:action[:scope] with wildcard *.

Resource categories and typical permissions: | Resource | Read | Write | Admin | |———-|——|——-|——-| | roadmap | roadmap:list, roadmap:get, roadmap:read | roadmap:write, roadmap:register | roadmap:admin, roadmap:export | | task | task:list, task:read | task:create, task:update, task:delete, task:write | — | | audit | audit:read | audit:write | — | | thought | thought:read | thought:write | — | | merkle | merkle:read | merkle:write | — | | memory | memory:read | memory:write | memory:admin | | rag | rag:read | rag:write | rag:admin | | gsd | gsd:read | gsd:write | gsd:admin | | context | context:read | context:write | — | | analysis | analysis:read | — | analysis:admin |

Wildcard *:* grants all permissions (superadmin).


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.