fix(backend): stop leaking internal error details in 500 responses - #603
Open
iamwhitehat wants to merge 1 commit into
Open
fix(backend): stop leaking internal error details in 500 responses#603iamwhitehat wants to merge 1 commit into
iamwhitehat wants to merge 1 commit into
Conversation
Controllers were returning error.message directly to clients. When the failing call is a database query, that message carries table names, column names and SQL fragments, giving attackers exactly the schema information needed to aim injection attempts. Adds a shared sendInternalError helper: - production responses return one fixed generic message per call site - the full error is logged server-side with request id and path - development keeps detailed messages for fast debugging Migrated 29 catch blocks across 9 controllers (payment, tax, multiSig, asset, cashFlowForecast, pdfCertificate, bulkImport, contract, contractRegistry). TwoFactorError handling in authController is unchanged: those messages are intentional client-facing errors. Tests: new internalErrorLeak.test.ts drives a real Postgres-shaped schema error through the tax routes and asserts no fragment of it reaches the response body while it does land in server logs; fails on unpatched code, passes with this change. contractRegistry 500 test tightened into a leak regression guard.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #495.
The leak
29 catch blocks across 9 controllers responded with
error.messagedirectly:When the failing call is a database query, that message carries table names,
column names and raw SQL fragments. A request that triggers a Postgres error
hands the attacker a free schema map for aiming injection attempts. Library
errors can also leak file paths and stack details.
Reproduction: send
POST /api/taxes/ruleswith a payload that makes the insertfail. The response body contains
insert into "tax_rules" ... relation "tax_rules" does not exist, including the exact column list from the query.The fix
New shared helper
backend/src/utils/internalError.ts(sendInternalError):ever reaches the client.
an incident report maps straight to the log entry holding the real cause.
where the leak is not exploitable. This mirrors what
app.ts's global handleralready did correctly; controllers now match it.
Migrated catch blocks in: paymentController (7), taxController (6),
multiSigController (5), cashFlowForecastController (4), assetController (3),
pdfCertificateController (3), bulkImportController (1), contractController (1),
contractRegistryController (1).
Not changed:
authController'sTwoFactorErrorpath. Those messages aredeliberate client-facing errors with stable codes; only unexpected errors fall
through to the generic 500.
Also not changed here:
contractUpgradeControllerandfreezeControlleruseerror.messagefor routing decisions or upstream Stellar/Horizon detail on502, which needs its own review against acceptance criterion 4 of the issue;
happy to follow up separately rather than widen this diff.
Testing
New regression suite
internalErrorLeak.test.ts: drives a realPostgres-shaped schema error through the tax routes via supertest and asserts
no fragment (
tax_rules,relation,insert into) appears in the responsebody while the same error does land in server logs.
insert into "tax_rules" ...The existing
contractRegistry.test.ts"returns 500 when service throws" testpreviously asserted the leak as expected behavior (
message: 'Registry load failed'); tightened into a leak regression guard instead.Typecheck clean apart from a pre-existing parse failure in
tenantConfigService.ts(fails identically on clean main). Controllers suite:61 passing before and after; the 21 pre-existing failures on clean main
(employee/search/contractEvent suites) are untouched by this diff.