Repository navigation
fix(sdk/javascript): validate the parsed GCP KMS config shape - #1203
Merged
stas-schaller merged 1 commit intoSep 25, 2026
Conversation
stas-schaller
self-requested a review
September 25, 2026 17:11
stas-schaller
force-pushed
the
feature/KSM-1515-gcp-config-shape-validation
branch
from
September 25, 2026 17:43
a90aac8 to
b387ddb
Compare
stas-schaller
merged commit Sep 25, 2026
4fcc027
into
release/storage/javascript/gcp-kms/v1.1.0
3 checks passed
stas-schaller
added a commit
that referenced
this pull request
Sep 29, 2026
…-1534-package-json-engines-overrides Brings the branch up to date with #1200-#1203 and #1199 (it was cut before #1200 merged). Resolves the CHANGELOG conflict by keeping both the KSM-1534 and KSM-1514 entries, and fixes the stale docs Mateo's review flagged: - README.md: Node.js 20 -> 22 (this PR's own fix) - CHANGELOG.md: rescope the "Both entries below" intro to the two entries it actually describes (KSM-1450/1458), not the newly-adjacent KSM-1534 entry - CHANGELOG.md: KSM-1534 entry no longer implies a released version ever declared >=20 (none did; 1.0.0 had no engines field at all) - CHANGELOG.md: KSM-1500 entry drops the now-false "CI already tests on Node 20" claim, since CI tests on 22 as of this same ticket
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.
Summary
loadConfig()parses the config file and never checks the type of the result.JSON.parse()succeeds fornull,0,false,"", an array, and a primitive, none of which is the declaredRecord<string, string>, and each takes a different silently wrong path: falsy scalars leave the file in plaintext while the log claims encryption started; an array makes every later save a no-op that persists nothing; a primitive gets written to disk encrypted, and only the nextsaveString()throws a nativeTypeError. This is the same fail-open family KSM-1455/KSM-1486 closed for a zero-length file, left open for valid but wrong-shaped JSON.What changes
src/GCPKeyValueStore.ts:isValidConfigShape()type guard: rejectsnull, non-objects, and arrays outright, then requires every value in the object to be a string.rejectInvalidConfigShape()throws aGCPKeyValueStorageErrornaming the config path, with the same message shape the existing zero-length checks use.loadConfig(), the plaintext parse and the decrypted parse, call the check as its own statement immediately after their ownJSON.parsehas already returned, not nested inside that parse's owntry/catch. A throw from inside thattrywould be caught by its owncatchinstead, which would misread a bad shape as "must be encrypted, try decrypting" (the plaintext site) or fold it into the generic decrypted-parse failure (the decryption site), losing the specific reason in both cases.CHANGELOG.md gets an entry under the existing v1.1.0
### Securitysection.Why it matters
Three groups of previously-silent failures, all reproduced against the real module:
null,0,false,"": theif (config)guard is falsy for all of these, so the save is skipped,this.configstays{}, andinit()resolves successfully, but the file is left as plaintext on disk, exactly what the encryption step was supposed to prevent.[],[1,2,3]:this.configbecomes an array.JSON.stringifyignores an array replacer when the value itself is an array, so the computed hash matcheslastSavedConfigHashby coincidence, and every latersaveString()/saveStorage()takes the no-op "no changes detected" skip and persists nothing, resolving successfully.123,"a string": both are truthy, so the file is encrypted and overwritten with the primitive first, and only the nextset()call throws a nativeTypeErrorwhen it tries to assign a property on a primitive.Tests
init()rejects when the whole file content is null / 0 / false / ""GCPKeyValueStorageError, file byte-for-byte unchanged, the misleading "starting encryption" log line never firesinit()rejects for an empty or non-empty array, before any writeinit()rejects for a bare number or a quoted string, file untouched, not overwritten firstGCPKeyValueStorageError, not a nativeTypeError{}still bootstraps a fresh first runinit()rejects when the DECRYPTED content is null / 0 / [] / 123Verified by reverting only the source hunk and re-running: exactly the 14 tests above marked "Yes" fail, and only those.
npx tsc --noEmitandnpx eslint 'src/**/*.ts'are both clean.Verified against all four other PRs currently open on this release branch (#1201 KSM-1516, #1202 KSM-1514, #1197 KSM-1534, #1199 KSM-1517): merged all five together in a scratch clone, resolved three trivial same-anchor conflicts (this PR's own new declarations landing next to KSM-1514's in the same spot, and both CHANGELOG entries appending at the same line), and the combined tree passes 176/176.
Follow-ups (not in this PR)
if (jsonError && decryptionError)check a few lines below the decryption parse site is unreachable dead code. Thecatchblock that setsdecryptionError = truealways throws its own error immediately afterward, so the later check can never actually observe that flag as true. Confirmed by a test in this PR that initially asserted the wrong (unreachable) message and had to be corrected.Jira: KSM-1515