Skip to content

harden: file delivery, submit metadata, client IP, status idempotency, answer validation#58

Merged
Musiker15 merged 1 commit into
mainfrom
harden/security-cluster
Jun 20, 2026
Merged

harden: file delivery, submit metadata, client IP, status idempotency, answer validation#58
Musiker15 merged 1 commit into
mainfrom
harden/security-cluster

Conversation

@Musiker15

Copy link
Copy Markdown
Member

Security & correctness hardening from the multi-agent code review (PR A of the follow-up cluster).

Changes

File delivery & upload metadata (High)

  • GET /api/files/[id] now serves attachment + application/octet-stream (+ explicit nosniff), never inline — a user-uploaded HTML/SVG can no longer render same-origin.
  • New headObject() in lib/s3.ts. On submit, each file reference is verified to exist in object storage and the server-stored size/MIME is recorded — the client-supplied descriptor is no longer trusted.

Client IP behind the proxy (Med)

  • clientIp takes the last X-Forwarded-For hop (the one our trusted Apache proxy appends) instead of the first, so a client can't spoof its rate-limit bucket. Loose IPv4/IPv6 shape check rejects junk.

Idempotent status transitions (Med)

  • New shared changeSubmissionStatus helper in @msk-forms/db: reads current status, writes guarded by that exact value (updateMany), and records the event + outbox notification in one transaction — race-safe and idempotent.
  • Used by both the web review route and the bot's Accept/Reject buttons (removes the duplicated transaction logic).

Server-side answer validation (Med)

  • buildAnswerSchema now enforces the full field.validation contract: number min/max, string min/max length, regex pattern, email/url formats, and choice-option membership — not just required.
  • Exported LAYOUT_FIELD_TYPES / isLayoutField from shared (consumed in follow-up dedup PR).

Tests

  • Rewrote client-ip tests (spoofed left entry, IPv6, malformed last hop).
  • Added buildAnswerSchema tests covering every new constraint.

Validation

pnpm typecheck, pnpm lint, pnpm test (32 tests), pnpm build — all green.

…, answer validation

Security & correctness hardening from the multi-agent review:

- Serve uploaded files as `attachment` + `application/octet-stream` (never
  inline) so a user-uploaded HTML/SVG can't render same-origin; add explicit
  nosniff on the route too.
- On submit, verify each file reference against object storage via a new
  `headObject` and record the server-stored size/MIME instead of trusting the
  client-supplied descriptor.
- Take the LAST `X-Forwarded-For` hop (the one our trusted Apache proxy
  appends) so a client can't spoof its rate-limit identity; loosely validate
  the IP shape.
- Add a shared, idempotent, race-safe `changeSubmissionStatus` helper in the db
  package (guarded `updateMany` + event + outbox in one transaction) and use it
  from both the web review route and the bot's Accept/Reject buttons.
- Enforce the full `field.validation` contract server-side in
  `buildAnswerSchema` (min/max, lengths, pattern, email/url formats, option
  membership), not just `required`. Export `LAYOUT_FIELD_TYPES`/`isLayoutField`.

Adds tests for the new client-IP and answer-validation behavior.
@Musiker15 Musiker15 merged commit e7381b9 into main Jun 20, 2026
3 checks passed
@Musiker15 Musiker15 deleted the harden/security-cluster branch June 20, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant