0d3cb498a3
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Waiting to run
Test and Publish Multi-arch Docker Image / test (push) Waiting to run
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Blocked by required conditions
Validate Renovate Config / Validate Renovate Configuration (push) Waiting to run
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
114 lines
3.4 KiB
Markdown
114 lines
3.4 KiB
Markdown
# Database Security (SQL Injection Prevention)
|
|
|
|
This codebase uses Drizzle ORM with SQLite. All database queries must use parameterized SQL so user-controlled input never changes query structure.
|
|
|
|
## Quick Rules
|
|
|
|
- Use Drizzle's `sql` tagged template literals for dynamic values.
|
|
- Use `sql.join()` for dynamic lists such as `IN (...)` clauses.
|
|
- Pass `SQL<unknown>` fragments between functions, not strings.
|
|
- Do not build queries with `sql.raw()` or string interpolation.
|
|
- Prefer `json_each()` for user-selected JSON keys. When `json_extract()` is appropriate, bind a path built with the vetted `buildSafeJsonPath` helper in `src/models/eval.ts`.
|
|
|
|
## Required Pattern: Use `sql` Template Strings
|
|
|
|
Use parameterized queries for single values:
|
|
|
|
```typescript
|
|
import { sql } from 'drizzle-orm';
|
|
|
|
const query = sql`SELECT * FROM eval_results WHERE eval_id = ${evalId}`;
|
|
```
|
|
|
|
Use parameterized queries for multiple values:
|
|
|
|
```typescript
|
|
import { sql } from 'drizzle-orm';
|
|
|
|
const query = sql`
|
|
SELECT * FROM eval_results
|
|
WHERE eval_id = ${evalId} AND success = ${1}
|
|
`;
|
|
```
|
|
|
|
Use `sql.join()` for dynamic `IN (...)` lists:
|
|
|
|
```typescript
|
|
import { sql } from 'drizzle-orm';
|
|
|
|
const ids = ['id1', 'id2', 'id3'];
|
|
const query = sql`SELECT * FROM evals WHERE id IN (${sql.join(ids, sql`, `)})`;
|
|
```
|
|
|
|
## Forbidden Pattern: Raw SQL with Dynamic Content
|
|
|
|
Do not interpolate user-controlled values into raw SQL:
|
|
|
|
```typescript
|
|
const query = sql.raw(`SELECT * FROM eval_results WHERE eval_id = '${evalId}'`);
|
|
```
|
|
|
|
Do not build SQL conditions with string concatenation:
|
|
|
|
```typescript
|
|
import { sql } from 'drizzle-orm';
|
|
|
|
const whereClause = `eval_id = '${evalId}'`;
|
|
const query = sql.raw(`SELECT * FROM eval_results WHERE ${whereClause}`);
|
|
```
|
|
|
|
## JSON Paths in SQLite
|
|
|
|
SQLite's `json_extract()` accepts its JSON path as a bound value. Do not use `sql.raw()` to splice user-controlled JSON paths into SQL. Prefer `json_each()` when filtering by dynamic keys, because the key can be compared as a normal parameter:
|
|
|
|
```typescript
|
|
import { sql } from 'drizzle-orm';
|
|
|
|
const query = sql`
|
|
SELECT *
|
|
FROM eval_results
|
|
WHERE EXISTS (
|
|
SELECT 1
|
|
FROM json_each(metadata)
|
|
WHERE json_each.key = ${field} AND json_each.value = ${value}
|
|
)
|
|
`;
|
|
```
|
|
|
|
If you construct a JSON path for `json_extract()`, use `buildSafeJsonPath` from `src/models/eval.ts`, which escapes backslashes and double quotes for JSON path syntax and returns a value to bind:
|
|
|
|
```typescript
|
|
import { sql } from 'drizzle-orm';
|
|
import { buildSafeJsonPath } from '../../src/models/eval';
|
|
|
|
const jsonPath = buildSafeJsonPath(userField);
|
|
const query = sql`
|
|
SELECT * FROM eval_results
|
|
WHERE json_extract(metadata, ${jsonPath}) = ${value}
|
|
`;
|
|
```
|
|
|
|
If you need a new JSON-path helper, match the guarantees in `buildSafeJsonPath` and keep the implementation audited in one shared utility.
|
|
|
|
## Passing SQL Fragments Between Functions
|
|
|
|
When building complex queries, pass `SQL<unknown>` fragments instead of strings:
|
|
|
|
```typescript
|
|
import { type SQL, sql } from 'drizzle-orm';
|
|
|
|
function queryWithFilter(whereSql: SQL<unknown>): Promise<Result[]> {
|
|
const query = sql`SELECT * FROM eval_results WHERE ${whereSql}`;
|
|
return db.all(query);
|
|
}
|
|
|
|
const filter = sql`eval_id = ${evalId} AND success = ${1}`;
|
|
const results = await queryWithFilter(filter);
|
|
```
|
|
|
|
## Key Files with Database Queries
|
|
|
|
- `src/models/eval.ts` - Main eval queries and JSON-path helper
|
|
- `src/util/calculateFilteredMetrics.ts` - Metrics aggregation queries
|
|
- `src/database/index.ts` - Database connection
|