chore: root commit of OWSAP security testing/tightening
This commit is contained in:
62
tests-results-for-OWASP/A01-Broken-Access-Control.md
Normal file
62
tests-results-for-OWASP/A01-Broken-Access-Control.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# A01: Broken Access Control
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings
|
||||
|
||||
1. Cross-account delete risk in `/account/confirm-delete`.
|
||||
2. `AUTH_DISABLED` mode allows header-based impersonation (`x-user-id`) and must be tightly controlled.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. `/account/confirm-delete` is now session-bound:
|
||||
- Lookup uses `req.userId`.
|
||||
- Request `email` must match session user's email.
|
||||
- Mismatch returns `403`.
|
||||
|
||||
2. Insecure auth guard added:
|
||||
- `AUTH_DISABLED=true` now requires `ALLOW_INSECURE_AUTH_FOR_DEV=true` unless `NODE_ENV=test`.
|
||||
|
||||
3. `/admin/rollover` hardened:
|
||||
- Still requires `AUTH_DISABLED=true`.
|
||||
- Now also requires request IP to be internal/private.
|
||||
|
||||
## Test coverage
|
||||
|
||||
1. `api/tests/access-control.account-delete.test.ts`
|
||||
- Verifies cross-account deletion attempt is denied (`403`).
|
||||
- Verifies victim account is not deleted.
|
||||
|
||||
2. `api/tests/auth.routes.test.ts`
|
||||
- Verifies protected route rejects unauthenticated access.
|
||||
- Verifies spoofed `x-user-id` is rejected when auth is enabled.
|
||||
- Verifies login/session/logout behavior with CSRF handling.
|
||||
- Verifies login lockout behavior.
|
||||
|
||||
3. `api/tests/access-control.admin-rollover.test.ts`
|
||||
- Verifies unauthenticated access to `/admin/rollover` is denied when `AUTH_DISABLED=false`.
|
||||
- Verifies authenticated access still receives `403` when `AUTH_DISABLED=false`.
|
||||
- Verifies `/admin/rollover` rejects non-internal client IP when `AUTH_DISABLED=true`.
|
||||
- Verifies `/admin/rollover` allows internal client IP when `AUTH_DISABLED=true`.
|
||||
|
||||
## Run commands
|
||||
|
||||
From `api/`:
|
||||
|
||||
```bash
|
||||
npm test -- tests/auth.routes.test.ts tests/access-control.account-delete.test.ts tests/access-control.admin-rollover.test.ts
|
||||
```
|
||||
|
||||
## Expected results
|
||||
|
||||
1. All three test files pass.
|
||||
2. No access granted to protected routes without valid auth cookie/JWT.
|
||||
3. Spoofed `x-user-id` does not bypass auth when `AUTH_DISABLED=false`.
|
||||
4. Cross-account delete attempt fails with `403`.
|
||||
5. `/admin/rollover` remains inaccessible from public/non-internal clients.
|
||||
|
||||
## Production configuration requirements
|
||||
|
||||
1. `AUTH_DISABLED=false`
|
||||
2. `ALLOW_INSECURE_AUTH_FOR_DEV=false`
|
||||
3. Strong `JWT_SECRET` and `COOKIE_SECRET`
|
||||
89
tests-results-for-OWASP/A02-Security-Misconfiguration.md
Normal file
89
tests-results-for-OWASP/A02-Security-Misconfiguration.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# A02: Security Misconfiguration
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. SMTP transport debug logging enabled in all environments.
|
||||
2. Production CORS had a fail-open branch when configured origins were empty.
|
||||
3. Missing explicit anti-framing headers at edge.
|
||||
4. Docker compose exposed Postgres/API on all host interfaces.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. SMTP transport hardening in API:
|
||||
- `requireTLS` now respects config (`SMTP_REQUIRE_TLS`).
|
||||
- SMTP debug/logger are disabled in production (`!isProd` only).
|
||||
|
||||
2. CORS production behavior tightened:
|
||||
- Removed fail-open branch for empty configured origins.
|
||||
- Production now allows only explicitly configured origins.
|
||||
|
||||
3. Edge header hardening:
|
||||
- Added `X-Frame-Options: DENY`.
|
||||
- Added `Content-Security-Policy: frame-ancestors 'none'`.
|
||||
|
||||
4. Compose exposure reduction:
|
||||
- Bound Postgres and API ports to localhost only.
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `api/src/server.ts`
|
||||
2. `Caddyfile.prod`
|
||||
3. `Caddyfile.dev`
|
||||
4. `deploy/nginx/skymoneybudget.com.conf`
|
||||
5. `docker-compose.yml`
|
||||
|
||||
## Verification
|
||||
|
||||
### Automated security regression tests
|
||||
|
||||
Command (A01 regression):
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npm test -- auth.routes.test.ts access-control.account-delete.test.ts
|
||||
```
|
||||
|
||||
Verified output (provided from host run):
|
||||
|
||||
- Test Files: `2 passed (2)`
|
||||
- Tests: `6 passed (6)`
|
||||
- Start time: `16:39:35`
|
||||
|
||||
Command (A02 dedicated suite):
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest --run -c vitest.security.config.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `1 passed (1)`
|
||||
- Tests: `5 passed (5)`
|
||||
- Suite: `tests/security-misconfiguration.test.ts`
|
||||
|
||||
Coverage in dedicated suite:
|
||||
|
||||
1. Production CORS allowlist enforcement (allowed origin accepted, denied origin does not receive allow headers).
|
||||
2. SMTP production mailer options disable debug/logger.
|
||||
3. Runtime CORS preflight headers validated for allowed origins (`allow-origin`, `allow-credentials`, `vary`).
|
||||
4. Edge config files contain anti-framing headers (`X-Frame-Options`, `frame-ancestors` CSP).
|
||||
5. `docker-compose.yml` binds Postgres/API ports to localhost only.
|
||||
|
||||
### Expected operational checks after deploy
|
||||
|
||||
1. Unauthenticated `GET /dashboard` returns `401`.
|
||||
2. Spoofed `x-user-id` does not bypass auth when `AUTH_DISABLED=false`.
|
||||
3. `/admin/rollover` remains inaccessible from public network.
|
||||
4. Response headers include anti-framing policy.
|
||||
|
||||
## Residual notes
|
||||
|
||||
1. Keep production env pinned to:
|
||||
- `AUTH_DISABLED=false`
|
||||
- `ALLOW_INSECURE_AUTH_FOR_DEV=false`
|
||||
|
||||
2. Keep CORS origins explicitly configured in production:
|
||||
- `CORS_ORIGIN` and/or `CORS_ORIGINS`.
|
||||
@@ -0,0 +1,77 @@
|
||||
# A03: Software Supply Chain Failures
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. Production dependency vulnerabilities were present in both API and web lockfiles.
|
||||
2. Deploy pipeline had no explicit dependency vulnerability gate.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Dependency remediation:
|
||||
- Ran `npm audit fix` in `api` and `web`.
|
||||
- Revalidated production dependencies are clean with `npm audit --omit=dev`.
|
||||
|
||||
2. Pipeline hardening:
|
||||
- Added supply-chain check step in deploy workflow:
|
||||
- `npm ci` + `npm audit --omit=dev --audit-level=high` for API and web.
|
||||
- Updated checkout action from broad major tag to explicit release tag `v4.2.2`.
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `.gitea/workflows/deploy.yml`
|
||||
2. `api/package-lock.json`
|
||||
3. `web/package-lock.json`
|
||||
4. `api/tests/software-supply-chain-failures.test.ts`
|
||||
5. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
### Production dependency vulnerability scans
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npm audit --omit=dev --audit-level=high
|
||||
cd ../web
|
||||
npm audit --omit=dev --audit-level=high
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- `found 0 vulnerabilities` (api)
|
||||
- `found 0 vulnerabilities` (web)
|
||||
|
||||
### Workflow policy verification (automated)
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest run -c vitest.security.config.ts tests/software-supply-chain-failures.test.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `1 passed (1)`
|
||||
- Tests: `2 passed (2)`
|
||||
|
||||
Coverage in policy suite:
|
||||
|
||||
1. Deploy workflow includes dependency gate step for API and web.
|
||||
2. Workflow requires `npm ci` and `npm audit --omit=dev --audit-level=high` for both projects.
|
||||
3. `actions/checkout` remains pinned to an explicit release tag.
|
||||
|
||||
## Residual risks (not yet fully eliminated)
|
||||
|
||||
1. Base image tags are still mutable (`node:20-bookworm-slim`, `postgres:15`) and not digest-pinned.
|
||||
2. `actions/checkout` is pinned to a release tag, not a full commit SHA.
|
||||
3. No artifact signing/attestation verification (e.g., cosign/SLSA) in current deploy pipeline.
|
||||
|
||||
## Recommended next hardening steps
|
||||
|
||||
1. Pin container images by immutable digest in `Dockerfile`/`docker-compose.yml`.
|
||||
2. Pin workflow actions to full commit SHAs.
|
||||
3. Add SBOM generation and signature/attestation verification before deploy.
|
||||
71
tests-results-for-OWASP/A04-Cryptographic-Failures.md
Normal file
71
tests-results-for-OWASP/A04-Cryptographic-Failures.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# A04: Cryptographic Failures
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. Production origin could be configured as non-HTTPS.
|
||||
2. JWT configuration did not explicitly constrain issuer/audience/algorithm.
|
||||
3. Missing explicit env contract for JWT issuer/audience values.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Production HTTPS origin enforcement:
|
||||
- Added production guard requiring `APP_ORIGIN` to start with `https://`.
|
||||
|
||||
2. JWT hardening in Fastify:
|
||||
- Explicit signing algorithm set to `HS256`.
|
||||
- JWT signing includes configured `iss` and `aud`.
|
||||
- JWT verification enforces:
|
||||
- allowed algorithm (`HS256`)
|
||||
- allowed issuer
|
||||
- allowed audience
|
||||
|
||||
3. Env schema expanded:
|
||||
- Added `JWT_ISSUER` and `JWT_AUDIENCE` with secure defaults:
|
||||
- `skymoney-api`
|
||||
- `skymoney-web`
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `api/src/env.ts`
|
||||
2. `api/src/server.ts`
|
||||
3. `.env.example`
|
||||
4. `api/tests/cryptographic-failures.test.ts`
|
||||
5. `api/tests/cryptographic-failures.runtime.test.ts`
|
||||
6. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
Commands:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest run -c vitest.security.config.ts tests/cryptographic-failures.test.ts tests/cryptographic-failures.runtime.test.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `2 passed (2)`
|
||||
- Tests: `7 passed (7)`
|
||||
|
||||
Dedicated A04 checks in `cryptographic-failures.test.ts`:
|
||||
|
||||
1. Production env rejects non-HTTPS `APP_ORIGIN`.
|
||||
2. JWT issuer/audience defaults resolve as expected.
|
||||
3. Fastify JWT plugin is registered with explicit algorithm + issuer + audience constraints.
|
||||
|
||||
Runtime adversarial checks in `cryptographic-failures.runtime.test.ts`:
|
||||
|
||||
1. Token with invalid issuer is rejected (`401`).
|
||||
2. Token with invalid audience is rejected (`401`).
|
||||
3. Unsigned token (`alg=none`) is rejected (`401`).
|
||||
4. Token with valid signature + expected issuer/audience is accepted on protected auth refresh route.
|
||||
|
||||
## Required production env values
|
||||
|
||||
1. `APP_ORIGIN=https://...`
|
||||
2. `JWT_SECRET` strong 32+ chars
|
||||
3. `COOKIE_SECRET` strong 32+ chars
|
||||
4. `JWT_ISSUER=skymoney-api` (or your approved value)
|
||||
5. `JWT_AUDIENCE=skymoney-web` (or your approved value)
|
||||
50
tests-results-for-OWASP/A05-Injection.md
Normal file
50
tests-results-for-OWASP/A05-Injection.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# A05: Injection
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. API route used unsafe Prisma raw SQL helper (`$queryRawUnsafe`) for `/health/db`.
|
||||
2. Restore script accepted unvalidated external inputs that could be abused for command/SQL injection scenarios during operational use.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Replaced unsafe raw SQL helper:
|
||||
- `app.prisma.$queryRawUnsafe("SELECT now() as now")`
|
||||
- replaced with tagged, parameter-safe:
|
||||
- `app.prisma.$queryRaw\`SELECT now() as now\``
|
||||
|
||||
2. Hardened `scripts/restore.sh` input handling:
|
||||
- Added required file existence check for `BACKUP_FILE`.
|
||||
- Added strict identifier validation for `RESTORE_DB` (`^[A-Za-z0-9_]+$`).
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `api/src/server.ts`
|
||||
2. `scripts/restore.sh`
|
||||
3. `api/tests/injection-safety.test.ts`
|
||||
4. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest run -c vitest.security.config.ts tests/injection-safety.test.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `1 passed (1)`
|
||||
- Tests: `2 passed (2)`
|
||||
|
||||
Dedicated A05 checks in `injection-safety.test.ts`:
|
||||
|
||||
1. Verifies no usage of `$queryRawUnsafe` / `$executeRawUnsafe` across API source files.
|
||||
2. Executes `scripts/restore.sh` with adversarial `RESTORE_DB` input and verifies restore is rejected before DB commands execute.
|
||||
|
||||
## Residual notes
|
||||
|
||||
1. Main API query paths use Prisma query builder + zod input schemas (reduces SQL injection risk).
|
||||
2. Operational scripts should remain restricted to trusted operators and environments.
|
||||
54
tests-results-for-OWASP/A06-Insecure-Design.md
Normal file
54
tests-results-for-OWASP/A06-Insecure-Design.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# A06: Insecure Design
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. Sensitive email-token workflows did not enforce a cooldown between repeated code requests.
|
||||
2. Verification and account-deletion flows needed tighter, route-specific throttling to reduce brute-force and abuse risk.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Added explicit email-token cooldown guard in API:
|
||||
- New helper `assertEmailTokenCooldown(userId, type, cooldownMs)`.
|
||||
- Throws structured `429` error with code `EMAIL_TOKEN_COOLDOWN`.
|
||||
- Sets `Retry-After` header when cooldown is active.
|
||||
|
||||
2. Applied cooldown checks to both token issuance paths:
|
||||
- `/auth/verify/resend` for signup verification codes.
|
||||
- `/account/delete-request` for account deletion confirmation codes.
|
||||
|
||||
3. Split and applied stricter rate-limit profiles for sensitive auth/account routes:
|
||||
- `authRateLimit` on `/auth/register` and `/auth/login`.
|
||||
- `codeVerificationRateLimit` on `/auth/verify` and `/account/confirm-delete`.
|
||||
- `codeIssueRateLimit` on `/auth/verify/resend` and `/account/delete-request`.
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `api/src/server.ts`
|
||||
2. `api/tests/insecure-design.test.ts`
|
||||
3. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest --run -c vitest.security.config.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `4 passed (4)`
|
||||
- Tests: `10 passed (10)`
|
||||
|
||||
Dedicated A06 checks in `insecure-design.test.ts`:
|
||||
|
||||
1. Runtime verification resend endpoint enforces cooldown (`/auth/register` issues token, then immediate `/auth/verify/resend` is blocked with `429` + `Retry-After`).
|
||||
2. Runtime verification delete-request endpoint enforces cooldown (`/account/delete-request` second attempt returns `429` + `Retry-After`).
|
||||
3. Runtime verification repeated invalid `/auth/verify` requests trigger route throttling (`429`).
|
||||
|
||||
## Residual notes
|
||||
|
||||
1. A06 runtime tests require PostgreSQL availability for user/token persistence paths.
|
||||
@@ -0,0 +1,70 @@
|
||||
# A07: Identification and Authentication Failures
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. No explicit account lockout after repeated failed login attempts (brute-force risk).
|
||||
2. Password policy for registration and password updates was too weak (length-only).
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Added login lockout controls:
|
||||
- Tracks failed login attempts per normalized email in server memory.
|
||||
- Locks login for a configurable window after threshold failures.
|
||||
- Returns `429` with code `LOGIN_LOCKED` and `Retry-After` header during lockout.
|
||||
|
||||
2. Added strong password policy:
|
||||
- Minimum length `12`.
|
||||
- Requires lowercase, uppercase, number, and symbol.
|
||||
- Applied to:
|
||||
- `/auth/register` password.
|
||||
- `/me/password` new password.
|
||||
|
||||
3. Added auth hardening configuration:
|
||||
- `AUTH_MAX_FAILED_ATTEMPTS` (default: `5`)
|
||||
- `AUTH_LOCKOUT_WINDOW_MS` (default: `900000`, 15 minutes)
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `api/src/server.ts`
|
||||
2. `api/src/env.ts`
|
||||
3. `.env.example`
|
||||
4. `api/tests/auth.routes.test.ts`
|
||||
5. `api/tests/identification-auth-failures.test.ts`
|
||||
6. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
Dedicated security suite command (executed):
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest --run -c vitest.security.config.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `5 passed (5)`
|
||||
- Tests: `12 passed (12)`
|
||||
|
||||
Dedicated A07 checks in `identification-auth-failures.test.ts`:
|
||||
|
||||
1. Runtime checks weak password rejection for registration and `/me/password` update flow.
|
||||
2. Runtime checks lockout threshold/window behavior with configured `AUTH_MAX_FAILED_ATTEMPTS` and verifies `LOGIN_LOCKED` response + `Retry-After`.
|
||||
|
||||
Runtime auth flow checks added in `auth.routes.test.ts`:
|
||||
|
||||
1. Rejects weak passwords on registration.
|
||||
2. Locks login after repeated failed attempts.
|
||||
|
||||
Run this in an environment with PostgreSQL running to verify runtime behavior:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npm test -- tests/auth.routes.test.ts tests/identification-auth-failures.test.ts
|
||||
```
|
||||
|
||||
## Residual notes
|
||||
|
||||
1. Current lockout state is in-memory per API instance; for horizontally scaled production, move lockout tracking to a shared store (Redis/DB) for consistent enforcement across instances.
|
||||
@@ -0,0 +1,49 @@
|
||||
# A08: Software and Data Integrity Failures
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. Backup/restore workflow did not verify backup artifact integrity before restoring.
|
||||
2. Restores could proceed with tampered/corrupted dump files, risking silent data corruption.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Added checksum artifact generation during backups:
|
||||
- `scripts/backup.sh` now generates a SHA-256 checksum file next to each dump (`.sha256`).
|
||||
|
||||
2. Added checksum verification before restore:
|
||||
- `scripts/restore.sh` now requires `${BACKUP_FILE}.sha256`.
|
||||
- Validates checksum format (64 hex chars).
|
||||
- Computes runtime SHA-256 of backup file and blocks restore on mismatch.
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `scripts/backup.sh`
|
||||
2. `scripts/restore.sh`
|
||||
3. `api/tests/software-data-integrity-failures.test.ts`
|
||||
4. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest run -c vitest.security.config.ts tests/software-data-integrity-failures.test.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `1 passed (1)`
|
||||
- Tests: `2 passed (2)`
|
||||
|
||||
Dedicated A08 checks in `software-data-integrity-failures.test.ts`:
|
||||
|
||||
1. Executes `scripts/backup.sh` with stubbed `pg_dump` and verifies dump + `.sha256` artifact generation.
|
||||
2. Executes `scripts/restore.sh` with tampered checksum and verifies restore is blocked before DB commands are invoked.
|
||||
|
||||
## Residual notes
|
||||
|
||||
1. This secures backup artifact integrity in operational scripts.
|
||||
2. For CI/CD artifact integrity hardening, next step is attestation/signature verification for deployed build artifacts.
|
||||
@@ -0,0 +1,59 @@
|
||||
# A09: Security Logging and Monitoring Failures
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. Security-sensitive auth/account outcomes were not consistently logged as structured audit events.
|
||||
2. Incident triage required better request correlation for failed auth/CSRF and account-deletion attempts.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Added centralized structured security logging helper in API:
|
||||
- `logSecurityEvent(req, event, outcome, details)`
|
||||
- Includes request correlation fields (`requestId`, `ip`, `userAgent`).
|
||||
|
||||
2. Added audit logging for critical security events:
|
||||
- `auth.unauthenticated_request` (JWT auth failure)
|
||||
- `csrf.validation` (CSRF check failure)
|
||||
- `auth.register` success/blocked
|
||||
- `auth.login` success/failure/blocked (including lockout cases)
|
||||
- `auth.logout` success
|
||||
- `auth.verify` success/failure
|
||||
- `auth.verify_resend` success/failure/blocked
|
||||
- `account.delete_request` success/failure/blocked
|
||||
- `account.confirm_delete` success/failure/blocked
|
||||
|
||||
3. Reduced sensitive data exposure in logs:
|
||||
- Added email fingerprinting (`sha256` prefix) for event context instead of plain-text credentials.
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `api/src/server.ts`
|
||||
2. `api/tests/security-logging-monitoring-failures.test.ts`
|
||||
3. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest run -c vitest.security.config.ts tests/security-logging-monitoring-failures.test.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `1 passed (1)`
|
||||
- Tests: `2 passed (2)`
|
||||
|
||||
Dedicated A09 checks in `security-logging-monitoring-failures.test.ts`:
|
||||
|
||||
1. Runtime check emits structured `auth.unauthenticated_request` security event for protected-route access failures.
|
||||
2. Runtime check emits structured `csrf.validation` security event for CSRF failures.
|
||||
3. Validates correlation fields (`requestId`, `ip`, `outcome`) are present in emitted security events.
|
||||
|
||||
## Residual notes
|
||||
|
||||
1. Event logs are currently emitted through app logs; ensure production log shipping/alerting (e.g., SIEM rules on repeated `auth.login` failure/blocked events).
|
||||
2. Next step for A09 maturity is alert thresholds and automated incident notifications.
|
||||
50
tests-results-for-OWASP/A10-Server-Side-Request-Forgery.md
Normal file
50
tests-results-for-OWASP/A10-Server-Side-Request-Forgery.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# A10: Server-Side Request Forgery (SSRF)
|
||||
|
||||
Last updated: March 1, 2026
|
||||
|
||||
## Findings addressed
|
||||
|
||||
1. Production `APP_ORIGIN` previously enforced HTTPS but did not explicitly block localhost/private-network targets.
|
||||
2. SSRF posture needed explicit verification that API runtime code does not introduce generic outbound HTTP clients for user-influenced targets.
|
||||
|
||||
## Fixes implemented
|
||||
|
||||
1. Hardened production `APP_ORIGIN` validation in env parsing:
|
||||
- Requires valid URL format.
|
||||
- Rejects localhost/private-network hosts:
|
||||
- `localhost`, `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`, `::1`, `0.0.0.0`, `.local`.
|
||||
|
||||
2. Added dedicated A10 verification tests:
|
||||
- Rejects private/loopback `APP_ORIGIN` in production mode.
|
||||
- Asserts API server source (`api/src/server.ts`) does not use generic outbound HTTP request clients (`fetch`, `axios`, `http.request`, `https.request`).
|
||||
|
||||
## Files changed
|
||||
|
||||
1. `api/src/env.ts`
|
||||
2. `api/tests/server-side-request-forgery.test.ts`
|
||||
3. `api/vitest.security.config.ts`
|
||||
|
||||
## Verification
|
||||
|
||||
Command:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npx vitest run -c vitest.security.config.ts tests/server-side-request-forgery.test.ts
|
||||
```
|
||||
|
||||
Verified output:
|
||||
|
||||
- Test Files: `1 passed (1)`
|
||||
- Tests: `3 passed (3)`
|
||||
|
||||
Dedicated A10 checks in `server-side-request-forgery.test.ts`:
|
||||
|
||||
1. Asserts production env parsing rejects multiple private/localhost `APP_ORIGIN` variants.
|
||||
2. Asserts production env parsing accepts public HTTPS `APP_ORIGIN`.
|
||||
3. Asserts API source code has no generic outbound HTTP client usage (`fetch`, `axios`, `http.request`, `https.request`) outside test scripts.
|
||||
|
||||
## Residual notes
|
||||
|
||||
1. Current API architecture has minimal outbound HTTP surface (primarily SMTP transport).
|
||||
2. If future features add URL fetch/proxy/webhook integrations, enforce strict destination allowlists and network egress controls at implementation time.
|
||||
40
tests-results-for-OWASP/README.md
Normal file
40
tests-results-for-OWASP/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# OWASP Test Results
|
||||
|
||||
Last updated: March 2, 2026
|
||||
|
||||
This directory is the source of truth for SkyMoney OWASP validation work.
|
||||
|
||||
## Purpose
|
||||
|
||||
- Track implemented security tests and hardening changes.
|
||||
- Define exact pre-deploy and post-deploy verification steps.
|
||||
- Keep release evidence (commands, outputs, timestamps, pass/fail).
|
||||
|
||||
## Files
|
||||
|
||||
- `A01-Broken-Access-Control.md`: Findings, fixes, and verification for OWASP A01.
|
||||
- `A02-Security-Misconfiguration.md`: Findings, fixes, and dedicated verification suite for OWASP A02.
|
||||
- `A03-Software-Supply-Chain-Failures.md`: Dependency and pipeline supply-chain findings/fixes/verification.
|
||||
- `A04-Cryptographic-Failures.md`: Crypto/session token hardening findings/fixes/verification.
|
||||
- `A05-Injection.md`: Injection sink remediation and script input hardening verification.
|
||||
- `A06-Insecure-Design.md`: Abuse-resistance design hardening (cooldowns + tighter route throttling).
|
||||
- `A07-Identification-and-Authentication-Failures.md`: Login lockout and strong-password policy hardening.
|
||||
- `A08-Software-and-Data-Integrity-Failures.md`: Backup/restore checksum integrity controls.
|
||||
- `A09-Security-Logging-and-Monitoring-Failures.md`: Structured security event auditing for auth/account flows.
|
||||
- `A10-Server-Side-Request-Forgery.md`: SSRF hardening and outbound-request surface validation.
|
||||
- `post-deployment-verification-checklist.md`: Production smoke checks after each deploy.
|
||||
- `evidence-log-template.md`: Copy/paste template for recording each verification run.
|
||||
- `residual-risk-backlog.md`: Open non-blocking hardening items tracked release-to-release.
|
||||
|
||||
## Current status
|
||||
|
||||
1. A01 complete: implemented and tested.
|
||||
2. A02 complete: implemented and tested.
|
||||
3. A03 complete (initial hardening): implemented and tested.
|
||||
4. A04 complete: implemented and tested.
|
||||
5. A05 complete: implemented and tested.
|
||||
6. A06 complete: implemented and tested.
|
||||
7. A07 complete: implemented and tested.
|
||||
8. A08 complete: implemented and tested.
|
||||
9. A09 complete: implemented and tested.
|
||||
10. A10 complete: implemented and tested.
|
||||
70
tests-results-for-OWASP/evidence-log-template.md
Normal file
70
tests-results-for-OWASP/evidence-log-template.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# OWASP Verification Evidence Log Template
|
||||
|
||||
## Run metadata
|
||||
|
||||
- Date:
|
||||
- Environment: `local` | `staging` | `production`
|
||||
- App/API version (git SHA):
|
||||
- Operator:
|
||||
|
||||
## Environment flags
|
||||
|
||||
- `NODE_ENV`:
|
||||
- `AUTH_DISABLED`:
|
||||
- `ALLOW_INSECURE_AUTH_FOR_DEV`:
|
||||
|
||||
## Commands executed
|
||||
|
||||
1.
|
||||
```bash
|
||||
# command
|
||||
```
|
||||
Output summary:
|
||||
|
||||
2.
|
||||
```bash
|
||||
# command
|
||||
```
|
||||
Output summary:
|
||||
|
||||
3.
|
||||
```bash
|
||||
# command
|
||||
```
|
||||
Output summary:
|
||||
|
||||
## Results
|
||||
|
||||
- A01 protected route unauthenticated check: `pass` | `fail`
|
||||
- A01 spoofed header check: `pass` | `fail`
|
||||
- A01 admin rollover exposure check: `pass` | `fail`
|
||||
- A01 automated suite (`auth` + `account-delete` + `admin-rollover`): `pass` | `fail`
|
||||
- A02 dedicated suite (`security-misconfiguration`): `pass` | `fail`
|
||||
- A03 dedicated suite (`software-supply-chain-failures`): `pass` | `fail`
|
||||
- A04 dedicated suites (`cryptographic-failures*`): `pass` | `fail`
|
||||
- A05 dedicated suite (`injection-safety`): `pass` | `fail`
|
||||
- A06 dedicated suite (`insecure-design`): `pass` | `fail`
|
||||
- A07 dedicated suites (`auth.routes` + `identification-auth-failures`): `pass` | `fail`
|
||||
- A08 dedicated suite (`software-data-integrity-failures`): `pass` | `fail`
|
||||
- A09 dedicated suite (`security-logging-monitoring-failures`): `pass` | `fail`
|
||||
- A10 dedicated suite (`server-side-request-forgery`): `pass` | `fail`
|
||||
- Non-DB security suite (`SECURITY_DB_TESTS=0`): `pass` | `fail`
|
||||
- DB security suite (`SECURITY_DB_TESTS=1`): `pass` | `fail`
|
||||
|
||||
## Findings
|
||||
|
||||
- New issues observed:
|
||||
- Regressions observed:
|
||||
- Follow-up tickets:
|
||||
|
||||
## Residual Risk Review
|
||||
|
||||
- Reviewed `residual-risk-backlog.md`: `yes` | `no`
|
||||
- Items accepted for this release:
|
||||
- Items escalated/blocked:
|
||||
|
||||
## Sign-off
|
||||
|
||||
- Security reviewer:
|
||||
- Engineering owner:
|
||||
- Decision: `approved` | `blocked`
|
||||
@@ -0,0 +1,107 @@
|
||||
# Post-Deployment Verification Checklist
|
||||
|
||||
Use this after every deploy (staging and production).
|
||||
|
||||
## Preconditions
|
||||
|
||||
1. Deployment completed successfully.
|
||||
2. Migrations completed successfully.
|
||||
3. Correct environment flags:
|
||||
- `AUTH_DISABLED=false`
|
||||
- `ALLOW_INSECURE_AUTH_FOR_DEV=false`
|
||||
4. Test DB preflight (for DB-backed suites):
|
||||
- `TEST_DATABASE_URL` points to a reachable PostgreSQL instance.
|
||||
- Example quick check:
|
||||
```bash
|
||||
echo "$TEST_DATABASE_URL"
|
||||
```
|
||||
Expected:
|
||||
- single valid URL value
|
||||
- host/port match the intended test database (for local runs usually `127.0.0.1:5432`)
|
||||
|
||||
## A01 smoke checks
|
||||
|
||||
Replace `${API_BASE}` with your deployed API base URL.
|
||||
|
||||
### 1) Protected route requires auth
|
||||
|
||||
```bash
|
||||
curl -i "${API_BASE}/dashboard"
|
||||
```
|
||||
|
||||
Expected:
|
||||
- HTTP `401`
|
||||
- response body includes `UNAUTHENTICATED`
|
||||
|
||||
### 2) Spoofed identity header is ignored
|
||||
|
||||
```bash
|
||||
curl -i -H "x-user-id: spoofed-user-id" "${API_BASE}/dashboard"
|
||||
```
|
||||
|
||||
Expected:
|
||||
- HTTP `401`
|
||||
|
||||
### 3) Admin rollover is not publicly callable
|
||||
|
||||
```bash
|
||||
curl -i -X POST "${API_BASE}/admin/rollover" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"dryRun":true}'
|
||||
```
|
||||
|
||||
Expected:
|
||||
- HTTP `403`
|
||||
|
||||
## A09 smoke checks
|
||||
|
||||
### 4) Security events are emitted for failed auth attempts
|
||||
|
||||
Trigger a failed login attempt:
|
||||
|
||||
```bash
|
||||
curl -i -X POST "${API_BASE}/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"nonexistent@example.com","password":"WrongPass123!"}'
|
||||
```
|
||||
|
||||
Expected:
|
||||
- HTTP `401`
|
||||
- API logs include a structured `securityEvent` for `auth.login` with `outcome=failure`
|
||||
- log entry includes `requestId`
|
||||
|
||||
## A10 smoke checks
|
||||
|
||||
### 5) Production origin configuration is public and non-local
|
||||
|
||||
Verify production env/config:
|
||||
|
||||
- `APP_ORIGIN` uses public HTTPS host (not localhost/private IP ranges)
|
||||
|
||||
Expected:
|
||||
- API boots successfully with production env validation.
|
||||
|
||||
## Automated regression checks
|
||||
|
||||
Run in CI against a prod-like environment:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
npm test -- tests/auth.routes.test.ts tests/access-control.account-delete.test.ts tests/access-control.admin-rollover.test.ts
|
||||
SECURITY_DB_TESTS=0 npx vitest run -c vitest.security.config.ts
|
||||
SECURITY_DB_TESTS=1 npx vitest run -c vitest.security.config.ts
|
||||
```
|
||||
|
||||
Expected:
|
||||
- all tests pass
|
||||
|
||||
Note:
|
||||
- A06/A07 runtime suites require PostgreSQL availability.
|
||||
- `SECURITY_DB_TESTS=0` runs non-DB security controls only.
|
||||
- `SECURITY_DB_TESTS=1` includes DB-backed A06/A07 suites.
|
||||
|
||||
## Sign-off
|
||||
|
||||
1. Record outputs in `evidence-log-template.md`.
|
||||
2. Review open residual risks in `residual-risk-backlog.md`.
|
||||
3. Mark release security check as pass/fail.
|
||||
26
tests-results-for-OWASP/residual-risk-backlog.md
Normal file
26
tests-results-for-OWASP/residual-risk-backlog.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# OWASP Residual Risk Backlog
|
||||
|
||||
Last updated: March 2, 2026
|
||||
|
||||
Use this file to track non-blocking hardening items that remain after automated controls pass.
|
||||
|
||||
## Open items
|
||||
|
||||
| ID | OWASP | Residual risk | Status |
|
||||
|---|---|---|---|
|
||||
| RR-001 | A01 | Add explicit authorization integration tests for all future admin-only routes (deny-by-default coverage expansion). | Open |
|
||||
| RR-002 | A02 | Add runtime CSP and full security-header verification from deployed edge stack (not only config checks). | Open |
|
||||
| RR-003 | A03 | Add stronger supply-chain provenance controls (digest pinning, SLSA attestations, artifact signing). | Open |
|
||||
| RR-004 | A04 | Add key rotation runbook validation and automated stale-key detection checks. | Open |
|
||||
| RR-005 | A05 | Add static taint analysis or Semgrep policy bundle in CI for command/SQL injection sinks. | Open |
|
||||
| RR-006 | A06 | Add abuse-case tests for account recovery and verification flows under distributed-IP pressure. | Open |
|
||||
| RR-007 | A07 | Add MFA/WebAuthn roadmap tests once MFA is implemented; currently password+lockout only. | Open |
|
||||
| RR-008 | A08 | Add signed backup manifests and restore provenance verification for operational artifacts. | Open |
|
||||
| RR-009 | A09 | Add alerting pipeline assertions (SIEM/webhook delivery) in pre-prod smoke tests. | Open |
|
||||
| RR-010 | A10 | Add egress firewall enforcement tests to complement application-layer SSRF guards. | Open |
|
||||
|
||||
## Close criteria
|
||||
|
||||
1. A concrete control is implemented and validated by an automated test or policy gate.
|
||||
2. Evidence is attached in `evidence-log-template.md`.
|
||||
3. Owning team marks item as Closed with date and link to implementation PR.
|
||||
Reference in New Issue
Block a user