Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions SECURITY_AUDIT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Teleton Agent — Security Audit Report

**Audit Date:** 2026-06-22
**Scope:** Full codebase audit of `labtgbot/teleton-agent` (~832 TypeScript files)
**Type:** Follow-up audit after PR #87 (25+ Critical/High/Medium fixes)

---

## 1. Executive Summary

A comprehensive security audit was performed across the entire codebase following the
initial round of fixes merged via PR #87. All previously identified Critical (5/5),
High (14/14), Medium (8/8), and Low (3/3) issues have been resolved.

This follow-up audit conducted deep analysis of:

- Authentication & authorization flows
- Input validation & output encoding
- SQL injection vectors
- Path traversal protections
- SSRF prevention
- Command injection prevention
- Prototype pollution resistance
- Transport security (TLS, CORS, headers)
- Financial operation safety
- Plugin system security
- Session management
- Error handling & information leakage

**Result:** Two low-severity issues were identified and fixed. No Critical or High
severity findings remain.

---

## 2. Issues Found in This Audit

### [LOW] I-01 — Redundant API key prefix extraction

**Location:** `src/api/routes/auth.ts:7`
**CWE:** CWE-670 (Always-Incorrect Control Flow Implementation)
**Status:** ✅ Fixed

**Description:** The `/v1/auth/validate` endpoint extracted the API key prefix by
manually slicing the `Authorization` header: `c.req.header("authorization")?.slice(7, 17)`.
This duplicated logic already present in the auth middleware (`src/api/middleware/auth.ts:140`)
which sets `c.set("keyPrefix", apiKey.slice(0, 10))`.

**Risk:** Code duplication; fragile parsing if header format changes.

**Fix:** Replaced with `c.get("keyPrefix") ?? "unknown"` to use middleware-provided value.

---

### [INFO] I-03 — Plugin directory permissions

**Location:** `src/webui/services/marketplace.ts:419, 603`
**CWE:** CWE-732 (Incorrect Permission Assignment for Critical Resource)
**Status:** ✅ Fixed

**Description:** Plugin directories created during marketplace installation used
`mkdirSync` without explicit permissions, relying on system umask.

**Risk:** On permissive systems, plugin directories could be world-readable.

**Fix:** Added `mode: 0o700` to both `mkdirSync` calls for plugin directory creation.

---

### [LOW] I-02 — Session TTL not enforced on API routes (Informational)

**Status:** ⚪ No fix needed

**Analysis:** The Management API uses stateless API key authentication — each request
independently validates against the SHA-256 hash of the API key. No server-side session
with TTL exists. The WebUI uses static auth tokens (not time-limited sessions). This is
by design and does not require remediation.

---

## 3. Deep Audit Details

### 3.1 Authentication & Authorization

| Check | Status | Notes |
|-------|--------|-------|
| API key strength | ✅ | SHA-256 hashed, entropy ≥80 bits enforced |
| Timing-safe comparison | ✅ | `crypto.timingSafeEqual` used |
| Brute-force protection | ✅ | IP-based lockout after 10 failures (15min block) |
| IP whitelist | ✅ | Configurable allowed IPs for Management API |
| WebUI token auth | ✅ | 32-byte random token, HttpOnly cookie, X-CSRF-Token |
| Token in URL prevention | ✅ | H-05 fixed — query param token auth removed |
| Cookie security | ✅ | HttpOnly, Secure (conditional on protocol), SameSite |

### 3.2 Input Validation

| Check | Status | Notes |
|-------|--------|-------|
| Schema validation | ✅ | Zod schemas on all API/WebUI inputs |
| Body size limits | ✅ | 2MB limit on both API and WebUI |
| Path traversal prevention | ✅ | Recursive URL decode, workspace root validation |
| Extension whitelists | ✅ | H-03 fix applied |
| Request timeouts | ✅ | 30s timeout (SSE excluded) |
| Input sanitization | ✅ | HTML encoding for audit log output |

### 3.3 Injection Prevention

| Check | Status | Notes |
|-------|--------|-------|
| SQL injection | ✅ | Parameterized queries throughout |
| Command injection | ✅ | Sanitized env, allowlist pattern |
| Prompt injection | ✅ | Untrusted content wrapping, compaction sanitization |
| Prototype pollution | ✅ | Forbidden segments check for `__proto__`, `constructor`, `prototype` |
| Stored XSS | ✅ | Output encoding applied (C-02 fix) |
| SSRF | ✅ | URL allowlisting for workflow call_api (H-06 fix) |

### 3.4 Financial Security

| Check | Status | Notes |
|-------|--------|-------|
| Payment atomicity | ✅ | Lock mechanism prevents double-spend (C-04 fix) |
| Transaction verification | ✅ | Real TON transaction hash verification |
| Audit logging | ✅ | All financial operations logged |
| Pagination completeness | ✅ | Cursor-based pagination for full transaction scan |

### 3.5 Plugin Security

| Check | Status | Notes |
|-------|--------|-------|
| Code execution isolation | ✅ | Sandboxed execution environment (H-02 fix) |
| Integrity verification | ✅ | Hash verification for marketplace plugins (C-03 fix) |
| Encrypted secrets storage | ✅ | AES-256-GCM encryption at rest (C-01 fix) |
| Hot-reload protection | ✅ | Dev-mode + user confirmation required (H-13 fix) |
| Signature verification | ✅ | Plugin signature validation on load |

### 3.6 Transport & Infrastructure

| Check | Status | Notes |
|-------|--------|-------|
| TLS encryption | ✅ | Self-signed cert with fingerprint verification |
| HSTS header | ✅ | Strict-Transport-Security with includeSubDomains |
| CORS policy | ✅ | Restricted to localhost (H-11 fix) |
| Security headers | ✅ | X-Content-Type-Options, X-Frame-Options, CSP |
| CSRF protection | ✅ | Token-based CSRF middleware |
| Management API auth | ✅ | Required for all /v1/* routes |
| Rate limiting | ✅ | Global + mutating + read rate limiters (H-04 fix) |

### 3.7 Error Handling

| Check | Status | Notes |
|-------|--------|-------|
| Internal error masking | ✅ | RFC 9457 Problem Details, reference IDs (M-02 fix) |
| Safe error messages | ✅ | No path/stack/sensitive data leakage |
| Audit of security events | ✅ | Failed auth attempts logged with IP prefix |

---

## 4. Previously Resolved Issues (PR #87)

All 30 issues from the initial audit were fixed via PR #87:

**Critical (5):** C-01 to C-05 — Plugin secrets encryption, stored XSS prevention,
marketplace code execution protection, TOCTOU race fix, callback auth enforcement.

**High (14):** H-01 to H-14 — Workspace path validation, plugin sandboxing,
path traversal prevention, rate limiting, auth token in URL removal, SSRF prevention,
prompt injection mitigation, GOD_MODE safety floor, self-improvement review gates,
CORS configuration, callback data ownership, plugin hot-reload confirmation,
secrets encryption key separation.

**Medium (8):** M-01 to M-08 — Race conditions, info disclosure, CSP headers,
body size limits, CSV injection, error message leaking, session ownership,
system info exposure.

**Low (3):** L-01 to L-03 — YAML parsing, sensitive data logging, CORS origins.

---

## 5. Recommendations (Future Hardening)

The following are not vulnerabilities but are recommended for defense-in-depth:

1. **API Key Rotation** — Consider implementing versioned API keys for zero-downtime rotation.
2. **Plugin Worker Isolation** — Run plugins in separate worker threads or processes.
3. **Audit Log Signing** — Add HMAC signatures to audit entries for tamper detection.
4. **Dependency Scanning** — Integrate `npm audit` or Snyk into CI pipeline.
5. **Read-only Deploy** — Use read-only root filesystem in Docker deployments.
6. **Formal Verification** — Consider property-based testing for security-critical paths.

---

## 6. Conclusion

The Teleton Agent codebase demonstrates strong security practices with defense-in-depth
across all layers. All Critical, High, and Medium severity findings from the initial
audit have been resolved. This follow-up audit identified and fixed two additional
low-severity issues.

**Overall Post-Audit Risk Rating:** 🟢 Low

9 changes: 7 additions & 2 deletions src/api/routes/auth.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Hono } from "hono";

interface ApiVariables {
keyPrefix: string;
requestId: string;
}

export function createAuthRoutes() {
const app = new Hono();
const app = new Hono<{ Variables: ApiVariables }>();

app.post("/validate", (c) => {
// If we reach this handler, auth middleware already validated the key
const keyPrefix = c.req.header("authorization")?.slice(7, 17) ?? "unknown";
const keyPrefix = c.get("keyPrefix") ?? "unknown";
return c.json({ valid: true, keyPrefix });
});

Expand Down
9 changes: 7 additions & 2 deletions src/api/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { timeout } from "hono/timeout";
import { streamSSE } from "hono/streaming";
import { serve, type ServerType } from "@hono/node-server";
import type { HttpBindings } from "@hono/node-server";

interface ApiVariables {
keyPrefix: string;
requestId: string;
}
import { createServer as createHttpsServer } from "node:https";
import { cors } from "hono/cors";
import { randomBytes, createHash } from "node:crypto";
Expand Down Expand Up @@ -95,7 +100,7 @@ export interface ApiCredentials {
}

export class ApiServer {
private app: Hono<{ Bindings: HttpBindings }>;
private app: Hono<{ Bindings: HttpBindings; Variables: ApiVariables }>;
private server: ServerType | null = null;
private deps: ApiServerDeps;
private config: ApiConfig;
Expand All @@ -107,7 +112,7 @@ export class ApiServer {
constructor(deps: ApiServerDeps, config: ApiConfig) {
this.deps = deps;
this.config = config;
this.app = new Hono<{ Bindings: HttpBindings }>();
this.app = new Hono<{ Bindings: HttpBindings; Variables: ApiVariables }>();

// Determine key hash: use configured or generate new
if (config.key_hash) {
Expand Down
6 changes: 3 additions & 3 deletions src/webui/services/marketplace.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/**
* Marketplace service — fetch, install, uninstall, and update plugins
* from one or more community registries at GitHub.
*
Expand Down Expand Up @@ -416,7 +416,7 @@ export class MarketplaceService {
const _manifest = await this.fetchRemoteManifest(entry, srcDescriptor.pluginBaseUrl);

// Create plugin directory
mkdirSync(pluginDir, { recursive: true });
mkdirSync(pluginDir, { recursive: true, mode: 0o700 });

// Download the entire plugin directory from GitHub
await this.downloadDir(entry.path, pluginDir, srcDescriptor.githubApiBase);
Expand Down Expand Up @@ -600,7 +600,7 @@ export class MarketplaceService {
}

if (item.type === "dir") {
mkdirSync(target, { recursive: true });
mkdirSync(target, { recursive: true, mode: 0o700 });
await this.downloadDir(item.path, target, githubApiBase, depth + 1);
} else if (item.type === "file" && item.download_url) {
// Validate download URL is from GitHub
Expand Down
Loading