95 lines
5.2 KiB
Markdown
95 lines
5.2 KiB
Markdown
# Source-Sink Flow Analysis Summary
|
|
|
|
**Generated**: 2026-05-28
|
|
**Phase**: L3 (SAST — Greppable Fallback)
|
|
**Target**: Kordant monorepo (web/, browser-ext/)
|
|
|
|
---
|
|
|
|
## Scan Overview
|
|
|
|
| Metric | Value |
|
|
|--------|-------|
|
|
| Files scanned | 730 |
|
|
| Candidate files | 218 |
|
|
| Candidate matches | 1412 |
|
|
| Draft findings produced | 12 |
|
|
| Keep (enriched) | 10 |
|
|
| Drop (enriched) | 2 |
|
|
|
|
## Candidate Classes Breakdown
|
|
|
|
| Class | Matches | High-Score Examples | Enriched | Kept | Dropped |
|
|
|-------|---------|---------------------|----------|------|---------|
|
|
| `raw-sql-query` | 611 | admin.ts, billing.ts, blog.ts (`.query()` calls) | N/A | 0 | 0 |
|
|
| `path-traversal-file-access` | 638 | blog/[slug].tsx, ext_bench.py, api.js (`.join()`) | 2 | 1 | 1 |
|
|
| `hidden-control-channel` | 42 | middleware.ts (CORS origin), trpc.ts (auth headers) | 4 | 3 | 1 |
|
|
| `command-execution` | 55 | test files, benchmarks (subprocess.Popen, exec.Command) | N/A | 0 | 0 |
|
|
| `dynamic-code-execution` | 12 | honker-bun/raw.exec(), test_sqlite_versions.py | N/A | 0 | 0 |
|
|
| `secret-literal` | 9 | billing.test.ts, auth routes (password error messages) | N/A | 0 | 0 |
|
|
| `unsafe-html-or-template` | 17 | blog/[slug].tsx (innerHTML), auth test files | 1 | 1 | 0 |
|
|
| `ssrf-capable-request` | 10 | billing/return.tsx (fetch), scan.engine.ts (external API) | 1 | 1 | 0 |
|
|
| `webhook-without-obvious-signature` | 6 | stripe/webhook.ts | 1 | 1 | 0 |
|
|
| `open-redirect` | 2 | blog/index.tsx, app.tsx | 1 | 1 | 0 |
|
|
| `weak-token-or-crypto` | 5 | PasswordInput.tsx (Math.random) | 1 | 0 | 1 |
|
|
| `public-entrypoint` | 5 | extensionRouter procedures | N/A | 0 | 0 |
|
|
|
|
## Key Filtering Decisions
|
|
|
|
### Dropped: `raw-sql-query` (611 matches)
|
|
- **Reason**: False positives — 99%+ are tRPC `.query()` method calls (not raw SQL), not Drizzle `sql<>` tag usage. The tRPC router `.query()` method is a framework method, not a SQL execution sink.
|
|
- **Exception**: The one real `sql<>` usage in admin.ts:47 was separately assessed as p4-012 (low severity, latent risk).
|
|
|
|
### Dropped: `command-execution` (55 matches)
|
|
- **Reason**: All matches are in test files (`test_*.py`, `*_test.go`, `*_spec.rb`) or benchmark scripts. These are development-time subprocess calls, not production attack surface.
|
|
|
|
### Dropped: `dynamic-code-execution` (12 matches)
|
|
- **Reason**: All matches are SQLite raw SQL execution methods (`raw.exec()`, `exec()`) in the honker package or test files. These are database operations, not code execution sinks.
|
|
|
|
### Dropped: `secret-literal` (9 matches)
|
|
- **Reason**: All matches are test data (`billing.test.ts`, `notification.service.test.ts`) or password validation error messages (`login.tsx`, `signup.tsx` — `"Password is required"` is not a secret).
|
|
|
|
### Dropped: `path-traversal-file-access` — 1 kept, 1 dropped
|
|
- **Kept (p4-005)**: `voiceprint/storage.ts` — `userId` not validated before `path.join()`, enabling arbitrary file write
|
|
- **Dropped**: `blog/[slug].tsx` — false positives from `.join("")` string concatenation (not filesystem path joins)
|
|
|
|
### Dropped: `weak-token-or-crypto` — 0 kept, 1 dropped
|
|
- **Dropped**: `PasswordInput.tsx` — `Math.random()` is used for generating HTML input element IDs, not for CSRF tokens or cryptographic purposes. The id is only used for `<label for=...>` association. Not a security issue.
|
|
|
|
## DFD/CFD Coverage
|
|
|
|
| DFD/CFD Slice | Covered by Findings | Notes |
|
|
|---------------|---------------------|-------|
|
|
| DFD-1: tRPC → Drizzle ORM | Partial (p4-012) | CVE-2026-39356 surface noted but no active injection found |
|
|
| DFD-2: VoicePrint Pipeline | Full (p4-005) | Path traversal in audio storage |
|
|
| DFD-3: Browser Ext → tRPC | Partial (p4-008) | superjson vulnerability in extension only |
|
|
| DFD-4: WebSocket Alerts | Full (p4-007, p4-011) | JWT leak + no Origin validation |
|
|
| DFD-5: Stripe Webhook | Full (p4-006) | Unsafe type coercion |
|
|
| CFD-1: Auth Flow | Partial (p4-011) | JWT in query param + no Origin check |
|
|
| CFD-2: Authz Flow | Full (p4-001) | Unvalidated role mutation |
|
|
| CFD-3: Rate Limiting | Full (p4-009) | Substring-based path matching |
|
|
|
|
## Custom Analysis Targets (Domain Attack Research)
|
|
|
|
| Target | File | Finding |
|
|
|--------|------|---------|
|
|
| CORS env var trust | `web/src/middleware.ts` | p4-003 |
|
|
| XSS via markdown rendering | `web/src/routes/blog/[slug].tsx` | p4-004 |
|
|
| Puppeteer SSRF | `web/src/server/services/reports/generator.ts` | p4-002 |
|
|
| Stripe webhook type safety | `web/src/server/services/billing.service.ts` | p4-006 |
|
|
| Return URL open redirect | `web/src/server/api/schemas/billing.ts` | p4-010 |
|
|
| superjson CVE | `browser-ext/package.json` | p4-008 |
|
|
| Rate limit bypass | `web/src/server/api/utils.ts` | p4-009 |
|
|
| WebSocket Origin check | `web/src/server/websocket.ts` | p4-011 |
|
|
| JWT in WS query param | `web/src/server/websocket.ts` | p4-007 |
|
|
| Admin role mutation | `web/src/server/api/routers/admin.ts` | p4-001, p4-012 |
|
|
| Audio path traversal | `web/src/server/services/voiceprint/storage.ts` | p4-005 |
|
|
|
|
## Agentic Actions Audit
|
|
|
|
Analyzed 2 GitHub Actions workflow files:
|
|
- `.github/workflows/ci.yml` — No AI agent actions found
|
|
- `.github/workflows/deploy.yml` — No AI agent actions found
|
|
|
|
**Result**: 0 findings. Standard CI/CD workflows with no Claude Code, Gemini CLI, OpenAI Codex, or GitHub AI Inference integrations.
|