Skip to content

fix(backend): validate Stellar publicKey format in user validator - #1148

Open
Fury03 wants to merge 2 commits into
LabsCrypt:mainfrom
Fury03:fix/1098-validate-stellar-public-key
Open

fix(backend): validate Stellar publicKey format in user validator#1148
Fury03 wants to merge 2 commits into
LabsCrypt:mainfrom
Fury03:fix/1098-validate-stellar-public-key

Conversation

@Fury03

@Fury03 Fury03 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Closes #1098

Problem

registerUserSchema checked publicKey with z.string().min(50, ...).regex(/^G[A-Z2-7]{55}$/, ...). The regex was right, but the schema had two problems:

  • The redundant min(50) produced a second, less specific issue ('Invalid Stellar public key') for every short input, so a reporter got two overlapping messages for one mistake.
  • Neither message said what a valid key looks like, and a missing or non-string publicKey fell back to Zod's generic invalid_type text.

The format rule was also duplicated as an inline literal in three places in user.controller.ts, with no single source of truth.

Changes

All in backend/src/validators/user.validator.ts:

  • Export STELLAR_PUBLIC_KEY_REGEX and STELLAR_PUBLIC_KEY_ERROR as the canonical format rule and message.
  • Add a reusable stellarPublicKeySchema and use it for registerUserSchema.publicKey. Dropped min(50), which the regex already subsumes.
  • The failure message is now descriptive: Invalid Stellar public key format: expected 56 base32 characters starting with "G". A missing or non-string publicKey gets publicKey is required and must be a string.

A malformed key now fails in registerUserSchema.parse inside registerUser, which forwards the ZodError to errorHandler, returning 400 with { error: 'Validation Error', details: [{ path: 'publicKey', message: ... }] } — before any Prisma lookup or write.

Tests

New backend/tests/user.validator.test.ts — 13 cases:

  • Accepts a well-formed key.
  • Rejects malformed keys: 'not-a-stellar-key', too short, too long, wrong version prefix (S...), lowercase, a character outside the base32 alphabet (1), empty string, and whitespace-padded.
  • Rejects undefined, a number, and null with the type message.
  • Asserts exactly one issue is raised for a malformed key (the old schema raised two) and that .parse throws a ZodError carrying the descriptive message — the input the error middleware turns into the 400. ZodError → 400 Validation Error is already covered by tests/error.middleware.test.ts, so the test deliberately avoids importing the middleware and stays a dependency-free unit test.

Verified locally:

tests/user.validator.test.ts                    13 passed
tests/user.controller.test.ts + stream.validator 17 passed
npx tsc --noEmit                                clean for both changed files

(The full suite needs prisma generate, which couldn't reach binaries.prisma.sh from my environment — the pre-existing tsc errors in sse.controller.ts, soroban-indexer.service.ts, and others are untouched by this PR.)

Notes

  • Checksum verification is out of scope per the issue, so this stays a format check. stellarPublicKeySchema's doc comment says so explicitly, so nobody mistakes a match for proof the key is real. StrKey.isValidEd25519PublicKey would have pulled in CRC16 validation as a side effect; the issue allows a regex, so I used one.
  • Applying stellarPublicKeySchema to the other publicKey call sites (the inline checks in user.controller.ts, and other controllers) is left to the separately tracked issue.

@Fury03

Fury03 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Note on the failing CI checks

The red checks here are pre-existing on main and unrelated to this PR. This PR adds 13 tests, all of which pass in CI.

The new tests pass. Comparing this PR's Backend npm test job against #1149 — a Markdown-only PR off the same base, used as a baseline:

#1149 (docs only):  6 failed | 41 passed (47 files)    14 failed | 271 passed | 3 skipped (288)
#1148 (this PR):    6 failed | 42 passed (48 files)    14 failed | 284 passed | 3 skipped (301)

Same 6 failing files and same 14 failing tests, with exactly +1 passing file and +13 passing tests — that delta is tests/user.validator.test.ts in full. It introduces no failures.

The pre-existing failures. main's own CI run — 30494417974, the merge commit for #1130, which is the base of this branch — fails with the identical set:

Job Cause on main
Backend CI ~20 tsc errors, mostly IndexerStateRow.createdAt missing in src/lib/indexer-state.ts, plus soroban-indexer.service.ts, soroban-event-worker.ts, admin-rate-limiter.middleware.ts, tests/stream.test.ts, tests/stream.validator.test.ts
Frontend CI eslint parsing errors in frontend/src/components/dashboard/dashboard-view.tsx, frontend/src/hooks/useIncomingStreams.test.ts, frontend/src/lib/dashboard.ts
Soroban Contracts CI cargo fmt diffs at contracts/stream_contract/src/test.rs:433 and :2709
Backend Docker Image CI fails during the backend build, same tsc errors
Backend npm test the 14 tests listed above

None of those files are touched here — this PR changes backend/src/validators/user.validator.ts and adds backend/tests/user.validator.test.ts. tsc --noEmit reports no errors for either file; every remaining error is in the pre-existing list.

Happy to open a separate PR fixing the main breakage if that would be useful, but it would touch files well outside this issue's scope, so I have kept it out of here.

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.

[Backend] backend/src/validators/user.validator.ts does not validate publicKey format before it reaches the controller

1 participant