Skip to content

fix: address aisafe.io security audit findings - #72

Merged
adrgs merged 5 commits into
mainfrom
fix/security-audit-aisafe
Mar 17, 2026
Merged

fix: address aisafe.io security audit findings#72
adrgs merged 5 commits into
mainfrom
fix/security-audit-aisafe

Conversation

@adrgs

@adrgs adrgs commented Mar 17, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes 15 security findings from the aisafe.io audit report. All changes are minimal, targeted, and avoid introducing new dependencies.

Triage

Accepted & Fixed

ID Reported Severity Actual Severity Summary Fix
RQS-004 High High JWT type confusion: share token accepted as session token #[serde(deny_unknown_fields)] on Claims struct
RQS-006 High High DNS amplification via huge TXT records TXT records capped at 512 chars + UDP TC bit truncation
RQS-008 High High O(N*M) cache eviction loop holds write lock Keys collected once outside while loop
RQS-005 High Medium Cross-tenant quota bypass via subdomain extraction Rewrote DNS key parsing to strip base domain properly
RQS-007 Medium Medium Request deletion hits wrong item after eviction Removed fragile index system, scan list by ID
RQS-013 High Medium TCP connections unbounded + no read timeout Semaphore (100 conns/port) + 30s read timeout
RQS-003 High Medium SMTP transaction_log grows unbounded 64KB log cap, 500 cmd limit, 100 recipient limit

Accepted but Overstated Severity

ID Reported Actual Summary Notes
RQS-015 Critical Low Panic on multi-byte UTF-8 Host header Tokio catches task panics — only kills the single request handler, not the server. Still fixed (good practice).
RQS-014 High Low WebSocket loads all history on connect Bounded by max_requests_per_session already. A per-connection memory burst doesn't OOM the server. Capped to last 100 anyway.
RQS-009 High Low request: keys leak in kv_store Real but very slow leak (keys are ~50 bytes each). Fixed by removing index system + recognizing prefix.
RQS-002 High Low/By Design XSS via file upload Serving custom HTML/JS on subdomains IS the core feature. Added sandbox CSP + nosniff as defense-in-depth.
RQS-001 Info Info Timing attack on admin token Not exploitable over network (jitter >> nanosecond differences). Fixed with constant-time compare anyway.
RQS-012 Low Low No DNS/TCP rate limiting Added per-IP DNS rate limiter (100 qps). TCP bounded by semaphore.
RQS-011 Low Low No WebSocket rate limiting Added 30 msg/s per-connection limit.
RQS-010 Low Low No share token rate limiting Added 10 req/60s per-IP limit.

Feedback for aisafe.io

  1. RQS-015 severity is wrong. Rated Critical, but Tokio task panics are isolated — they kill one request handler, not the server. The report claims "continuously abort all Tokio worker tasks" and "severely disrupts overall server stability" which is false. A panic in a spawned task is caught by the Tokio runtime. This is Low severity at most.

  2. RQS-014 impact is overstated. Claims "uncatchable Out-Of-Memory (OOM) panic terminating the application instance across the entire environment." The request list is already bounded by max_requests_per_session. Loading 100 requests × a few KB each is not going to OOM anything.

  3. RQS-002 is by design. RequestRepo is a security research tool — serving custom HTTP responses with user-controlled headers and bodies is the primary feature. The report treats this as a vulnerability. We added CSP sandbox as defense-in-depth, but this isn't really a finding.

  4. Multiple findings describe request-level failures as server-wide crashes. Tokio isolates task panics. A single request failing does not constitute a DoS against the server. The report repeatedly uses language like "entire environment" and "all tenants" for per-task issues.

  5. RQS-001 timing attack is not practical. The report correctly notes this but still files it. Network jitter is orders of magnitude larger than comparison timing differences.

  6. RQS-005 PoC is incorrect. The PoC shows unauthenticated API calls but the DNS endpoint requires JWT auth. The subdomain in the FQDN is constructed server-side from the authenticated session. The actual attack surface is narrower than described.

Performance notes

  • RQS-008 fix is a pure perf improvement (removed redundant allocations under write lock)
  • DNS rate limiter uses Mutex<HashMap> with periodic cleanup — minimal overhead at 100 qps threshold
  • Request deletion is now O(n) scan instead of O(1) index lookup, but n ≤ max_requests_per_session (bounded, typically ~100-500)
  • WebSocket history sends fewer items on connect (100 vs all) — net improvement

Test plan

  • All 79 tests pass (1 ignored: ACME staging, requires real domain)
  • cargo clippy -- -D warnings clean
  • cargo fmt applied
  • Manual test: multi-byte UTF-8 Host header no longer panics
  • Manual test: share token rejected when used as session token
  • Manual test: TXT records >512 chars rejected by API
  • Manual test: WebSocket disconnects at >30 msg/s

🤖 Generated with Claude Code

adrgs and others added 2 commits March 17, 2026 23:43
Fix 15 security issues identified in the aisafe.io audit:

Critical/High:
- Safe UTF-8 byte slicing in subdomain extraction (RQS-015)
- JWT type confusion: deny_unknown_fields on Claims (RQS-004)
- Cache eviction O(N*M) loop: collect keys once outside loop (RQS-008)
- Cross-tenant quota bypass: fix subdomain extraction for DNS keys (RQS-005)
- Cache leak: recognize request: keys in extract_subdomain_from_key (RQS-009)
- Request deletion index desync: scan list by ID instead (RQS-007)
- DNS amplification: TXT record 512-char limit + UDP truncation bit (RQS-006)
- TCP exhaustion: semaphore (100 conns) + 30s read timeout (RQS-013)
- SMTP memory exhaustion: 64KB log cap, 500 cmd limit, 100 rcpt limit (RQS-003)
- WebSocket memory burst: limit initial history to last 100 requests (RQS-014)
- XSS mitigation: sandbox CSP + nosniff on served files (RQS-002)

Low/Info:
- Constant-time admin token comparison (RQS-001)
- DNS per-IP rate limiting at 100 qps (RQS-012)
- WebSocket message rate limiting at 30 msg/s (RQS-011)
- Share token endpoint rate limiting (RQS-010)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract check_rate_limit_generic to deduplicate session/share rate limiters
- Remove stale request index storage from DNS, TCP, and SMTP modules
  (indices were never read after delete_request switched to list scanning)
- Remove test assertion for deleted index system
- Call DNS rate limiter cleanup inline instead of spawning a task

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@adrgs adrgs closed this Mar 17, 2026
@adrgs adrgs reopened this Mar 17, 2026
adrgs and others added 3 commits March 18, 2026 00:04
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cargo-chef's dependency cargo-platform@0.3.2 requires rustc 1.88.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@adrgs
adrgs merged commit 7542f7b into main Mar 17, 2026
5 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.

1 participant