php-agentic/docs/security.md
Snider a2a9423ad6 security: fix SQL injection and add workspace scoping to MCP tools
- Replace orderByRaw with parameterised CASE statements
- Add Task::scopeOrderByPriority() and scopeOrderByStatus()
- Add AgentPlan::scopeOrderByStatus()
- Add workspace validation to StateSet, StateGet, StateList tools
- Add workspace validation to PlanGet, PlanList tools
- Add SecurityTest.php with comprehensive isolation tests

Fixes SEC-002, SEC-003 from security audit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 12:21:01 +00:00

7.6 KiB

title description updated
Security Security considerations and audit notes for core-agentic 2026-01-29

Security Considerations

This document outlines security considerations, known issues, and recommendations for the core-agentic package.

Authentication

API Key Security

Current Implementation:

  • Keys generated with ak_ prefix + 32 random characters
  • Stored as SHA-256 hash (no salt)
  • Key only visible once at creation time
  • Supports expiration dates
  • Supports revocation

Known Issues:

  1. No salt in hash (SEC-001)

    • Risk: Rainbow table attacks possible against common key formats
    • Mitigation: Keys are high-entropy (32 random chars), reducing practical risk
    • Recommendation: Migrate to Argon2id with salt
  2. Key prefix visible in hash display

    • The getMaskedKey() method shows first 6 chars of the hash, not the original key
    • This is safe but potentially confusing for users

Recommendations:

  • Consider key rotation reminders
  • Add key compromise detection (unusual usage patterns)
  • Implement key versioning for smooth rotation

IP Whitelisting

Implementation:

  • Per-key IP restriction toggle
  • Supports IPv4 and IPv6
  • Supports CIDR notation
  • Logged when requests blocked

Validation:

  • Uses filter_var() with FILTER_VALIDATE_IP
  • CIDR prefix validated against IP version limits (0-32 for IPv4, 0-128 for IPv6)
  • Normalises IPs for consistent comparison

Edge Cases Handled:

  • Empty whitelist with restrictions enabled = deny all
  • Invalid IPs/CIDRs rejected during configuration
  • IP version mismatch (IPv4 vs IPv6) handled correctly

Authorisation

Multi-Tenancy

Workspace Scoping:

  • All models use BelongsToWorkspace trait
  • Queries automatically scoped to current workspace context
  • Missing workspace throws MissingWorkspaceContextException

Known Issues:

  1. StateSet tool lacks workspace validation (SEC-003)

    • Risk: Plan lookup by slug without workspace constraint
    • Impact: Could allow cross-tenant state manipulation if slugs collide
    • Fix: Add workspace_id check to plan query
  2. Some tools have soft dependency on workspace

    • SessionStart marks workspace as optional if plan_slug provided
    • Could theoretically allow workspace inference attacks

Permission Model

Scopes:

  • plans.read - List and view plans
  • plans.write - Create, update, archive plans
  • phases.write - Update phase status, manage tasks
  • sessions.read - List and view sessions
  • sessions.write - Start, update, complete sessions
  • tools.read - View tool analytics
  • templates.read - List and view templates
  • templates.instantiate - Create plans from templates

Tool Scope Enforcement:

  • Each tool declares required scopes
  • AgentToolRegistry::execute() validates scopes before execution
  • Missing scope throws RuntimeException

Rate Limiting

Current Implementation

Global Rate Limiting:

  • ForAgentsController: 60 requests/minute per IP
  • Configured via RateLimiter::for('agentic-api')

Per-Key Rate Limiting:

  • Configurable per API key (default: 100/minute)
  • Uses cache-based counter with 60-second TTL
  • Atomic increment via Cache::add() + Cache::increment()

Known Issues:

  1. No per-tool rate limiting (SEC-004)

    • Risk: Single key can call expensive tools unlimited times
    • Impact: Resource exhaustion, cost overrun
    • Fix: Add tool-specific rate limits
  2. Rate limit counter not distributed

    • Multiple app servers may have separate counters
    • Fix: Ensure Redis cache driver in production

Response Headers

Rate limit status exposed via headers:

  • X-RateLimit-Limit - Maximum requests allowed
  • X-RateLimit-Remaining - Requests remaining in window
  • X-RateLimit-Reset - Seconds until reset
  • Retry-After - When rate limited

Input Validation

MCP Tool Inputs

Validation Helpers:

  • requireString() - Type + optional length validation
  • requireInt() - Type + optional min/max validation
  • requireEnum() - Value from allowed set
  • requireArray() - Type validation

Known Issues:

  1. Template variable injection (VAL-001)

    • JSON escaping added but character validation missing
    • Risk: Specially crafted variables could affect template behaviour
    • Recommendation: Add explicit character whitelist
  2. SQL orderByRaw pattern (SEC-002)

    • TaskCommand uses raw SQL for FIELD() ordering
    • Currently safe (hardcoded values) but fragile pattern
    • Recommendation: Use parameterised approach

Content Validation

ContentService validates generated content:

  • Minimum word count (600 words)
  • UK English spelling checks
  • Banned word detection
  • Structure validation (headings required)

Data Protection

Sensitive Data Handling

API Keys:

  • Plaintext only available once (at creation)
  • Hash stored, never logged
  • Excluded from model serialisation via $hidden

Session Data:

  • Work logs may contain sensitive context
  • Artifacts track file paths (not contents)
  • Context summaries could contain user data

Recommendations:

  • Add data retention policies for sessions
  • Consider encrypting context_summary field
  • Audit work_log for sensitive data patterns

Logging

Current Logging:

  • IP restriction blocks logged with key metadata
  • No API key plaintext ever logged
  • No sensitive context logged

Recommendations:

  • Add audit logging for permission changes
  • Log key creation/revocation events
  • Consider structured logging for SIEM integration

Transport Security

Requirements:

  • All endpoints should be HTTPS-only
  • MCP portal at mcp.host.uk.com
  • API endpoints under /api/agent/*

Headers Set:

  • X-Client-IP - For debugging/audit
  • Rate limit headers

Recommendations:

  • Add HSTS headers
  • Consider mTLS for high-security deployments

Dependency Security

External API Calls

AI provider services make external API calls:

  • Anthropic API (Claude)
  • Google AI API (Gemini)
  • OpenAI API

Security Measures:

  • API keys from environment variables only
  • HTTPS connections
  • 300-second timeout
  • Retry with exponential backoff

Recommendations:

  • Consider API key vault integration
  • Add certificate pinning for provider endpoints
  • Monitor for API key exposure in responses

Internal Dependencies

The package depends on:

  • host-uk/core - Event system
  • host-uk/core-tenant - Workspace scoping
  • host-uk/core-mcp - MCP infrastructure

All are internal packages with shared security posture.

Audit Checklist

Pre-Production

  • All SEC-* issues in TODO.md addressed
  • API key hashing upgraded to Argon2id
  • StateSet workspace scoping fixed
  • Per-tool rate limiting implemented
  • Test coverage for auth/permission logic

Regular Audits

  • Review API key usage patterns
  • Check for expired but not revoked keys
  • Audit workspace scope bypass attempts
  • Review rate limit effectiveness
  • Check for unusual tool call patterns

Incident Response

  1. Compromised API Key

    • Immediately revoke via $key->revoke()
    • Check usage history in database
    • Notify affected workspace owner
    • Review all actions taken with key
  2. Cross-Tenant Access

    • Disable affected workspace
    • Audit all data access
    • Review workspace scoping logic
    • Implement additional checks

Security Contacts

For security issues:

  • Create private issue in repository
  • Email security@host.uk.com
  • Do not disclose publicly until patched

Changelog

2026-01-29

  • Initial security documentation
  • Documented known issues SEC-001 through SEC-004
  • Added audit checklist

2026-01-21

  • Rate limiting functional (was stub)
  • Admin routes now require Hades role
  • ForAgentsController rate limited