chore: root commit of OWSAP security testing/tightening
All checks were successful
Deploy / deploy (push) Successful in 1m42s
Security Tests / security-non-db (push) Successful in 20s
Security Tests / security-db (push) Successful in 22s

This commit is contained in:
2026-03-01 20:46:47 -06:00
parent 1645896e54
commit 079b8b9492
25 changed files with 1131 additions and 107 deletions

5
.env
View File

@@ -33,3 +33,8 @@ EMAIL_REPLY_TO=support@skymoneybudget.com
UPDATE_NOTICE_VERSION=1
UPDATE_NOTICE_TITLE=SkyMoney Update
UPDATE_NOTICE_BODY=We added email verification and account-delete confirmation
ALLOW_INSECURE_AUTH_FOR_DEV=false
JWT_ISSUER=skymoney-api
JWT_AUDIENCE=skymoney-web
AUTH_MAX_FAILED_ATTEMPTS=5
AUTH_LOCKOUT_WINDOW_MS=900000

View File

@@ -4,6 +4,7 @@ PORT=8080
CORS_ORIGIN=http://localhost:5173
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173,https://skymoneybudget.com
AUTH_DISABLED=false
ALLOW_INSECURE_AUTH_FOR_DEV=false
SEED_DEFAULT_BUDGET=false
ROLLOVER_SCHEDULE_CRON=0 6 * * *
APP_ORIGIN=http://localhost:5173
@@ -21,8 +22,12 @@ ADMIN_DATABASE_URL=postgres://postgres:change-me@127.0.0.1:5432/postgres
# Auth secrets (min 32 chars)
JWT_SECRET=replace-with-32+-chars
JWT_ISSUER=skymoney-api
JWT_AUDIENCE=skymoney-web
COOKIE_SECRET=replace-with-32+-chars
COOKIE_DOMAIN=skymoneybudget.com
AUTH_MAX_FAILED_ATTEMPTS=5
AUTH_LOCKOUT_WINDOW_MS=900000
# Email (verification + delete confirmation)
SMTP_HOST=smtp.example.com

View File

@@ -8,12 +8,21 @@ jobs:
deploy:
runs-on: vps-host
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4.2.2
- name: Supply chain checks (production dependencies)
run: |
set -euo pipefail
cd api
npm ci
npm audit --omit=dev --audit-level=high
cd ../web
npm ci
npm audit --omit=dev --audit-level=high
- name: Build Web
run: |
cd web
npm ci
npm run build
- name: Deploy with Docker Compose

View File

@@ -0,0 +1,54 @@
name: Security Tests
on:
pull_request:
push:
branches: [main]
jobs:
security-non-db:
runs-on: vps-host
steps:
- uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
with:
node-version: "20"
cache: "npm"
cache-dependency-path: api/package-lock.json
- name: Install API dependencies
run: |
cd api
npm ci
- name: Run OWASP security suite (non-DB)
run: |
cd api
SECURITY_DB_TESTS=0 npx vitest run -c vitest.security.config.ts
security-db:
if: ${{ secrets.TEST_DATABASE_URL != '' }}
runs-on: vps-host
steps:
- uses: actions/checkout@v4.2.2
- name: Setup Node
uses: actions/setup-node@v4.2.0
with:
node-version: "20"
cache: "npm"
cache-dependency-path: api/package-lock.json
- name: Install API dependencies
run: |
cd api
npm ci
- name: Run OWASP security suite (DB-backed)
env:
TEST_DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
run: |
cd api
SECURITY_DB_TESTS=1 npx vitest run -c vitest.security.config.ts

View File

@@ -23,6 +23,8 @@
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Content-Security-Policy "frame-ancestors 'none'"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
}

View File

@@ -11,6 +11,8 @@ skymoneybudget.com {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Content-Security-Policy "frame-ancestors 'none'"
Referrer-Policy "strict-origin-when-cross-origin"
}
# Serve static SPA

View File

@@ -17,6 +17,8 @@ server {
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Static web app

View File

@@ -9,7 +9,7 @@ services:
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
- "127.0.0.1:5432:5432"
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app} -d ${POSTGRES_DB:-skymoney}"]
@@ -47,7 +47,7 @@ services:
depends_on:
- postgres
ports:
- "8081:8080"
- "127.0.0.1:8081:8080"
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]

View File

@@ -19,7 +19,11 @@ mkdir -p "$OUT_DIR"
STAMP="$(date +%F_%H%M%S)"
OUT_FILE="${OUT_DIR}/skymoney_${STAMP}.dump"
OUT_BASENAME="$(basename "$OUT_FILE")"
OUT_DIR_ABS="$(cd "$OUT_DIR" && pwd)"
pg_dump "${BACKUP_DATABASE_URL:-$DATABASE_URL}" -Fc -f "$OUT_FILE"
(cd "$OUT_DIR_ABS" && sha256sum "$OUT_BASENAME" > "${OUT_BASENAME}.sha256")
echo "Backup written to: $OUT_FILE"
echo "Checksum written to: ${OUT_FILE}.sha256"

View File

@@ -13,6 +13,26 @@ if [[ -z "${BACKUP_FILE:-}" ]]; then
echo "BACKUP_FILE is required."
exit 1
fi
if [[ ! -f "$BACKUP_FILE" ]]; then
echo "BACKUP_FILE does not exist: $BACKUP_FILE"
exit 1
fi
CHECKSUM_FILE="${BACKUP_FILE}.sha256"
if [[ ! -f "$CHECKSUM_FILE" ]]; then
echo "Missing checksum file: ${CHECKSUM_FILE}"
exit 1
fi
EXPECTED_HASH="$(awk '{print $1; exit}' "$CHECKSUM_FILE")"
if [[ ! "$EXPECTED_HASH" =~ ^[A-Fa-f0-9]{64}$ ]]; then
echo "Invalid checksum format in: ${CHECKSUM_FILE}"
exit 1
fi
ACTUAL_HASH="$(sha256sum "$BACKUP_FILE" | awk '{print $1}')"
if [[ "$ACTUAL_HASH" != "$EXPECTED_HASH" ]]; then
echo "Backup checksum verification failed for: ${BACKUP_FILE}"
exit 1
fi
if [[ -z "${DATABASE_URL:-}" ]]; then
echo "DATABASE_URL is required."
@@ -23,6 +43,11 @@ RESTORE_DB="${RESTORE_DB:-skymoney_restore_test}"
RESTORE_URL="${RESTORE_DATABASE_URL:-}"
ADMIN_URL="${ADMIN_DATABASE_URL:-$DATABASE_URL}"
if [[ ! "$RESTORE_DB" =~ ^[A-Za-z0-9_]+$ ]]; then
echo "RESTORE_DB must match ^[A-Za-z0-9_]+$"
exit 1
fi
if [[ -z "$RESTORE_URL" ]]; then
echo "RESTORE_DATABASE_URL is required (example: postgresql://user:pass@host:5432/${RESTORE_DB})."
exit 1

View 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`

View 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`.

View File

@@ -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.

View 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)

View 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.

View 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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View 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.

View 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.

View 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`

View File

@@ -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.

View 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.

244
web/package-lock.json generated
View File

@@ -1114,9 +1114,9 @@
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz",
"integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"cpu": [
"arm"
],
@@ -1127,9 +1127,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz",
"integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"cpu": [
"arm64"
],
@@ -1140,9 +1140,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz",
"integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"cpu": [
"arm64"
],
@@ -1153,9 +1153,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz",
"integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"cpu": [
"x64"
],
@@ -1166,9 +1166,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz",
"integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"cpu": [
"arm64"
],
@@ -1179,9 +1179,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz",
"integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"cpu": [
"x64"
],
@@ -1192,9 +1192,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz",
"integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"cpu": [
"arm"
],
@@ -1205,9 +1205,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz",
"integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"cpu": [
"arm"
],
@@ -1218,9 +1218,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz",
"integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"cpu": [
"arm64"
],
@@ -1231,9 +1231,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz",
"integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"cpu": [
"arm64"
],
@@ -1244,9 +1244,22 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz",
"integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"cpu": [
"loong64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
"cpu": [
"loong64"
],
@@ -1257,9 +1270,22 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz",
"integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"cpu": [
"ppc64"
],
@@ -1270,9 +1296,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz",
"integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"cpu": [
"riscv64"
],
@@ -1283,9 +1309,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz",
"integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"cpu": [
"riscv64"
],
@@ -1296,9 +1322,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz",
"integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"cpu": [
"s390x"
],
@@ -1309,9 +1335,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz",
"integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"cpu": [
"x64"
],
@@ -1322,9 +1348,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz",
"integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"cpu": [
"x64"
],
@@ -1334,10 +1360,23 @@
"linux"
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz",
"integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
"cpu": [
"arm64"
],
@@ -1348,9 +1387,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz",
"integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"cpu": [
"arm64"
],
@@ -1361,9 +1400,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz",
"integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"cpu": [
"ia32"
],
@@ -1374,9 +1413,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz",
"integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
"cpu": [
"x64"
],
@@ -1387,9 +1426,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz",
"integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"cpu": [
"x64"
],
@@ -2081,13 +2120,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -2282,9 +2321,9 @@
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3785,9 +3824,9 @@
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4196,9 +4235,9 @@
}
},
"node_modules/rollup": {
"version": "4.53.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz",
"integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
@@ -4211,28 +4250,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.53.2",
"@rollup/rollup-android-arm64": "4.53.2",
"@rollup/rollup-darwin-arm64": "4.53.2",
"@rollup/rollup-darwin-x64": "4.53.2",
"@rollup/rollup-freebsd-arm64": "4.53.2",
"@rollup/rollup-freebsd-x64": "4.53.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.53.2",
"@rollup/rollup-linux-arm-musleabihf": "4.53.2",
"@rollup/rollup-linux-arm64-gnu": "4.53.2",
"@rollup/rollup-linux-arm64-musl": "4.53.2",
"@rollup/rollup-linux-loong64-gnu": "4.53.2",
"@rollup/rollup-linux-ppc64-gnu": "4.53.2",
"@rollup/rollup-linux-riscv64-gnu": "4.53.2",
"@rollup/rollup-linux-riscv64-musl": "4.53.2",
"@rollup/rollup-linux-s390x-gnu": "4.53.2",
"@rollup/rollup-linux-x64-gnu": "4.53.2",
"@rollup/rollup-linux-x64-musl": "4.53.2",
"@rollup/rollup-openharmony-arm64": "4.53.2",
"@rollup/rollup-win32-arm64-msvc": "4.53.2",
"@rollup/rollup-win32-ia32-msvc": "4.53.2",
"@rollup/rollup-win32-x64-gnu": "4.53.2",
"@rollup/rollup-win32-x64-msvc": "4.53.2",
"@rollup/rollup-android-arm-eabi": "4.59.0",
"@rollup/rollup-android-arm64": "4.59.0",
"@rollup/rollup-darwin-arm64": "4.59.0",
"@rollup/rollup-darwin-x64": "4.59.0",
"@rollup/rollup-freebsd-arm64": "4.59.0",
"@rollup/rollup-freebsd-x64": "4.59.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
"@rollup/rollup-linux-arm64-musl": "4.59.0",
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
"@rollup/rollup-linux-loong64-musl": "4.59.0",
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
"@rollup/rollup-linux-x64-gnu": "4.59.0",
"@rollup/rollup-linux-x64-musl": "4.59.0",
"@rollup/rollup-openbsd-x64": "4.59.0",
"@rollup/rollup-openharmony-arm64": "4.59.0",
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
"@rollup/rollup-win32-x64-gnu": "4.59.0",
"@rollup/rollup-win32-x64-msvc": "4.59.0",
"fsevents": "~2.3.2"
}
},