Skip to content

Commit 8975ddf

Browse files
Merge pull request #2 from co-cddo/f-fix
F fix
2 parents 507bd07 + 79eeb54 commit 8975ddf

31 files changed

Lines changed: 1473 additions & 162 deletions

CLAUDE.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ The daily cron (2 AM London) enqueues one job per service. Jobs are processed se
3030
- `src/db/migrate.ts` — lightweight migration runner using `schema_migrations` table
3131
- `src/db/seed.ts` — upserts services from `services.json` into the `services` table
3232
- `src/db/queries.ts` — all SQL queries (parameterised, no string interpolation)
33+
- `src/insights/sqlValidator.ts` — AST-based SQL validation (allowlist functions, cap LIMIT, reject non-SELECT)
34+
- `src/server/validateUrl.ts` — HTTPS + gov.uk domain validation for URL overrides
35+
- `src/insights/sanitiseHtml.ts` — sanitise-html wrapper for LLM output
3336

3437
## Running locally
3538

@@ -62,6 +65,8 @@ APP_URL=http://localhost:3000
6265

6366
OAuth2 authorization code flow via Internal Access (`sso.service.security.gov.uk`). Sessions stored in Postgres via `connect-pg-simple` (auto-creates `session` table). 2-hour session expiry matching token lifetime. No refresh tokens.
6467

68+
Session is regenerated on login (`session.regenerate()`) to prevent session fixation. The OIDC `sub` claim is stored as the stable user identifier (email may change).
69+
6570
Public routes: `/`, `/health`, `/auth/*`, `/public/*`, `/assets/*`
6671
Protected routes: `/accessibility`, `/cookies`, `/privacy`, `/insights`, `/services/*`
6772

@@ -75,6 +80,18 @@ Protected routes: `/accessibility`, `/cookies`, `/privacy`, `/insights`, `/servi
7580
- **Migrations** in `src/db/migrations/` — numbered SQL files, applied by `src/db/migrate.ts`
7681
- **Sessions**`session` table auto-created by `connect-pg-simple`
7782

83+
## Security
84+
85+
- **CSRF** — synchroniser token pattern via `csrf-sync`. Tokens delivered in a `<meta>` tag and submitted as `_csrf` body field or `x-csrf-token` header. All POST endpoints are protected.
86+
- **SQL validation** — LLM-generated SQL is parsed with `pgsql-ast-parser` (AST-based). Only single SELECT/WITH statements allowed; function calls checked against an allowlist; LIMIT capped at 100. The read-only pool also enforces `default_transaction_read_only` and `statement_timeout` at DB level.
87+
- **URL validation** — manual URL overrides restricted to HTTPS + `*.gov.uk` domains (`src/server/validateUrl.ts`).
88+
- **Prompt injection** — scraper extracts `innerText` (not `innerHTML`) before sending to Bedrock, so hidden elements, scripts, and HTML comments never reach the LLM.
89+
- **HTML sanitisation** — LLM prose responses are sanitised server-side with `sanitize-html` (strict tag allowlist) before rendering.
90+
- **TLS** — database connections use `ssl: { rejectUnauthorized: true }` in production.
91+
- **Headers**`helmet` adds security headers (CSP, HSTS, X-Frame-Options, etc.).
92+
- **Rate limiting** — Bedrock-spending endpoints are rate-limited per session.
93+
- **Session**`SESSION_SECRET` must be ≥32 chars in production; `httpOnly`, `sameSite: lax`, `secure` (in prod) cookies.
94+
7895
## Package manager
7996

8097
**pnpm** (v10.33.0) — `.nvmrc` specifies Node 20. CI workflows use pnpm.

README.md

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,13 @@ npm run db:seed
5555

5656
## Running locally
5757

58-
### UI only (no worker)
59-
6058
```bash
6159
npm run dev
62-
```
63-
64-
Opens at [http://localhost:3000](http://localhost:3000). This starts only the Express server — no scraping will happen.
65-
66-
### Full app (server + worker)
67-
68-
```bash
60+
npm run dev:watch
6961
npm run build && npm start
7062
```
7163

72-
Starts the Express server and the pg-boss job worker in the same process. The worker runs 3 concurrent scrape loops and a daily cron at 2am London time.
73-
74-
Trigger checks from the Workers page (all services) or from individual service pages (single service).
64+
Opens at [http://localhost:3000](http://localhost:3000)
7565

7666
### Docker Compose
7767

docker-compose.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,25 @@ services:
4040
}]
4141
}
4242
43+
oidc-mock-ready:
44+
image: curlimages/curl:latest
45+
depends_on:
46+
oidc-mock:
47+
condition: service_started
48+
entrypoint: ["sleep", "infinity"]
49+
healthcheck:
50+
test: ["CMD", "curl", "-fsS", "http://oidc-mock:8090/isalive"]
51+
interval: 3s
52+
timeout: 3s
53+
retries: 10
54+
4355
compliance-scraper:
4456
build: .
4557
depends_on:
4658
postgres:
4759
condition: service_healthy
48-
oidc-mock:
49-
condition: service_started
60+
oidc-mock-ready:
61+
condition: service_healthy
5062
env_file:
5163
- path: .env
5264
required: false
@@ -93,6 +105,7 @@ services:
93105
- .:/app
94106
- e2e-node-modules:/app/node_modules
95107
- ./playwright-report:/app/playwright-report
108+
- ./test-results:/app/test-results
96109
command: >
97110
bash -c "npm install -g $$(npm pkg get packageManager | tr -d '\"')
98111
&& pnpm install --frozen-lockfile

package.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,19 @@
2525
"dependencies": {
2626
"@aws-sdk/client-bedrock-runtime": "^3.1037.0",
2727
"connect-pg-simple": "^10.0.0",
28+
"csrf-sync": "4.2.1",
2829
"dotenv": "^17.4.2",
2930
"express": "^4.22.1",
31+
"express-rate-limit": "8.4.1",
3032
"express-session": "^1.19.0",
3133
"govuk-frontend": "^5.14.0",
34+
"helmet": "8.1.0",
3235
"nunjucks": "^3.2.4",
3336
"pg": "^8.20.0",
3437
"pg-boss": "^10.4.2",
35-
"playwright": "^1.59.1"
38+
"pgsql-ast-parser": "12.0.2",
39+
"playwright": "^1.59.1",
40+
"sanitize-html": "2.17.3"
3641
},
3742
"devDependencies": {
3843
"@eslint/js": "^10.0.1",
@@ -43,11 +48,14 @@
4348
"@types/node": "^20.19.39",
4449
"@types/nunjucks": "^3.2.6",
4550
"@types/pg": "^8.20.0",
51+
"@types/sanitize-html": "2.16.1",
52+
"@types/supertest": "7.2.0",
4653
"eslint": "^10.2.1",
4754
"husky": "^9.1.7",
4855
"lint-staged": "^16.4.0",
4956
"nodemon": "^3.1.14",
5057
"prettier": "^3.8.3",
58+
"supertest": "7.2.2",
5159
"ts-node": "^10.9.2",
5260
"typescript": "^5.9.3",
5361
"typescript-eslint": "^8.59.0"

0 commit comments

Comments
 (0)