fix(SDK-7250): make the config debug dump safe, lossless and opt-out-able - #130
Open
AakashHotchandani wants to merge 1 commit into
Open
fix(SDK-7250): make the config debug dump safe, lossless and opt-out-able#130AakashHotchandani wants to merge 1 commit into
AakashHotchandani wants to merge 1 commit into
Conversation
…able Investigated replacing the wdio.conf file capture (#128) with a richer log dump, by stringifying hook functions into `_config data:`. That specific goal is NOT achievable -- see below -- but the surrounding hardening is worth having on its own. What this does: - serializeConfigForLog() replaces `JSON.parse(JSON.stringify(config))`. It keeps RegExp values instead of collapsing them to `{}`, returns '[Circular]' instead of throwing on a circular config, and never throws at all -- the previous call sits in the service constructor with no try/catch, so a circular reference from a plugin or reporter took the constructor down. - Credential scrubbing now covers compound key names. The previous exact-name list (`user`, `username`, `key`, `accesskey`, `password`) could not see `clientSecret`, `client_secret`, `CLIENT_SECRET` or `AWS_SECRET_ACCESS_KEY`, all of which were being written to a log that is uploaded. Basic-auth URLs and inline PEM blocks in string values are scrubbed too. - `_options data:` and `webdriver capabilities data:` went through raw JSON.stringify with no object-level redaction at all; both now use the same serializer. - Adds `disableAutoCaptureLogs` (and BROWSERSTACK_DISABLE_AUTO_CAPTURE_LOGS), honoured in uploadLogs itself so the detached cleanup rescue is covered, since opting out is exactly what leaves logsUploaded false and arms that rescue. Why the original goal is impossible: @wdio/config ConfigParser.addService does `hook.bind(service)` on every hook, including hooks defined in the user's own config file. Per ECMAScript, a bound function has no source text -- `toString()` returns `function () { [native code] }`. Verified on a real run: every hook in the dump reads `["function () { [native code] }"]`. No serializer can recover hook bodies from the config object, so the log cannot substitute for reading the config file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
7 tasks
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.
What is this about?
I set out to build the alternative to #128 that came up in review: instead of capturing the user's
wdio.conffile, make the config dump the service already writes intobstack-wdio-service.loglossless — stringify the hook functions — and drop the file-capture path entirely. That would have deleted the whole config-resolution ladder.That specific goal turns out to be impossible. The hardening I built along the way is worth having regardless, so this PR is that hardening, and the finding is written up below so nobody re-attempts it.
Why the log can never replace reading the config file
@wdio/config'sConfigParser.addServicebinds every hook, including hooks defined in the user's own config file:Per ECMAScript, a bound function has no source text.
Function.prototype.toString()on one returns:Verified on a real run — every hook in the dump reads
["function () { [native code] }"], and a marker string placed inside abefore()hook never appears in the log. No serializer can get it back; the information is gone before we ever see the object.This is worth knowing because a unit test does not catch it: a plain
function () {…}in a test fixture stringifies perfectly. It only shows up against a real merged config. So #128's file capture stands as the only way to see hook bodies.Details
What this PR does keep:
1.
serializeConfigForLog()replacesJSON.parse(JSON.stringify(config)).RegExpvalue{}"/\\.e2e\\.ts$/""[Circular]""[unserializable: …]"The throw matters more than it looks: that call sits in the service constructor with no enclosing
try/catch, so a circular reference from a plugin or reporter takes the constructor down.2. Credential scrubbing now covers compound key names. The previous list was exact-name only:
so
clientSecret,client_secret,CLIENT_SECRETandAWS_SECRET_ACCESS_KEYwere all written verbatim into a log that gets uploaded. Basic-auth URLs (https://user:pass@host) and inline PEM blocks in string values are scrubbed too. Lookalikes (hotkey,keyword,my_secretary,https://example.com:8080/path) are deliberately left alone.3.
_options data:andwebdriver capabilities data:had no object-level redaction at all — rawJSON.stringify. Both now go through the same serializer.4. Adds
disableAutoCaptureLogs(plusBROWSERSTACK_DISABLE_AUTO_CAPTURE_LOGS), honoured insideuploadLogsitself rather than only at the call site — the detached cleanup rescue incleanup.tscalls it with no options object, and opting out is precisely what leaveslogsUploadedfalse and arms that rescue.Relationship to #128
Not a replacement — the two are independent and complementary:
They can land in either order. If both land, the redaction helpers should be de-duplicated into one module — happy to do that as a follow-up on whichever merges second.
How Has This Been Tested?
17 new unit tests; suite is 48 files / 1091 tests green, lint and typecheck clean.
Dev-tested on a real BrowserStack session with a config carrying every case — hooks containing secrets, a one-line hook, a
RegExpvalue, compound/SCREAMING secret keys, a basic-auth URL and a PEM block. Build3k8ayl4lhhhv2qvoqgxcwqcczku6o6griggkykuq, log upload succeeded.Scanning the entire uploaded service log:
and
RegExpsurvived as"/\\.e2e\\.js$/", withaccessibilityand lookalike keys still readable.One honest caveat on that table: the two "inside hook" rows are clean because the hook source never reached the log at all (bound functions), not because scrubbing caught them. If hook bodies were ever recoverable, that scrubbing path is tested by unit test but has no live coverage.
Types of changes
Checklist: