fix: P1/P7/P8 — version unification, FME config wiring, debug log redaction#108
Conversation
…daction P1: MCP server metadata now reads version from package.json via getVersion() instead of hardcoded "2.0.0" — single source of truth for CLI and MCP clients. P7: resolveProductBaseUrl() now uses HARNESS_FME_BASE_URL from config (defaults to https://api.split.io) instead of a dead hardcoded const, enabling override for self-managed/staging environments. P8: Debug-level request/response body logging in harness-client.ts now runs through redactSensitiveFields() by default, redacting values for keys matching token, secret, password, apiKey, credential, etc. Set HARNESS_LOG_UNSAFE_BODIES=true to bypass for local debugging. Also includes deferred spec 002 (TransportOverrides) for future reference. Co-authored-by: Cursor <cursoragent@cursor.com>
|
|
…, depth limit - Parse failure fallback: apply inline secret regex instead of returning raw truncated input that could leak secrets. - Add missing key patterns: access_token, refresh_token, id_token, credentials (plural), session_token, cookie. - Depth limit: return [REDACTED] for objects beyond depth 10 instead of leaking nested content. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Stale comment
Important findings:
src/config.ts:121— the new FME base URL override bypasses the repo's HTTPS config-validation convention.HARNESS_FME_BASE_URLis accepted as any URL, but onlyHARNESS_BASE_URLis gated byHARNESS_ALLOW_HTTP, so feature-flag requests can now sendAuthorization: Bearer ${HARNESS_API_KEY}over plain HTTP when misconfigured.src/utils/redact.ts:37-54— the new non-JSON redaction fallback still leaks whitespace-delimited secrets. Inputs such asauthorization: Bearer <token>or quoted secrets containing spaces are only partially scrubbed (or missed entirely), andharness-client.tsnow relies on this helper for every debug body log path.Sent by Cursor Automation: Sunil On Demand Architecture Review
There was a problem hiding this comment.
Stale comment
Important —
src/utils/redact.ts:37-39,tests/utils/redact.test.ts:135-145:INLINE_SECRET_PATTERNis still narrower thanSENSITIVE_KEY_PATTERN. On the parse-failure path, non-JSON bodies using keys such assshKey/ssh_key,secretKey/secret_key,webhook, orencryptedare returned almost verbatim, so the new default redaction inHarnessClientcan still leak those values into debug logs. The fallback test only coverstoken, so this leak path would pass the new suite unchanged.Important —
tests/utils/redact.test.ts:1-147,src/client/harness-client.ts:242-244,src/client/harness-client.ts:270-272,src/client/harness-client.ts:319-320,src/config.ts:78: the added suite only proves the helper functions in isolation. It never exercises the threeHarnessClientlogging call sites or theHARNESS_LOG_UNSAFE_BODIESescape hatch that this PR introduced. That means the headline behavior change here — redact request / error / response bodies by default, bypass only when explicitly enabled — still has no material regression coverage at the integration point that can actually break.Important —
src/index.ts:54-75,CLAUDE.md:504-508: the current serverinstructionsblock is still out of compliance with the anti-bloat rules. It contains per-resource PR/pipeline examples, explicit PR resource inventory, schema guidance, ordinal-selection heuristics, and pipeline-version-specific guidance, whileCLAUDE.mdsays the global block should contain only universal patterns and move resource/feature-specific guidance into structured metadata (description,executeHint,inputExpansions,bodySchema, etc.). Even if this was inherited from the stacked base, the branch state still does not meet Sunil's instruction-budget standard.Sent by Cursor Automation: Sunil On Demand Architecture Review
There was a problem hiding this comment.
Stale comment
Important findings:
src/config.ts:77,src/config.ts:120-121—HARNESS_FME_BASE_URLis now configurable, but it does not share the HTTPS /HARNESS_ALLOW_HTTPvalidation that already protectsHARNESS_BASE_URL. Because the FME path sendsAuthorization: Bearer ${HARNESS_API_KEY}, a misconfiguredhttp://...override would put the API key on the wire in plaintext.src/utils/redact.ts:37-54,tests/utils/redact.test.ts:135-145— the new parse-failure redaction fallback is still narrower than the canonical sensitive-key matcher and it truncates values at whitespace. Malformed or non-JSON bodies can therefore still leak secrets in exactly the debug logging path this PR is enabling by default.Open questions / assumptions:
- I could not independently run
pnpm typecheckorpnpm testin this runner becausenode/pnpmare not available onPATH, so this review is source-based.- I scoped findings to issues introduced by this PR and did not re-raise inherited branch-state debt outside the touched behavior.
Change summary:
- Server version is now sourced from
package.jsonviagetVersion().- FME base URL became configurable.
- Debug request / response body logging now routes through
redactJsonString()by default.Sent by Cursor Automation: Sunil On Demand Architecture Review
- Sync INLINE_SECRET_PATTERN keys with SENSITIVE_KEY_PATTERN via shared constant — no more divergent key lists to maintain. - Fix value capture to handle multi-word values (Bearer tokens, quoted secrets with spaces) by matching quoted strings or everything until the next delimiter. - Add HTTPS/HARNESS_ALLOW_HTTP guard for HARNESS_FME_BASE_URL in config transform — FME requests carry auth tokens and must not downgrade to plaintext. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Stale comment
Important finding:
src/utils/redact.ts:1-3,src/utils/redact.ts:41-58— the new non-JSON redaction path still misses some common secret-key spellings, so plaintext/YAML debug logs can leak values in cases the helper is supposed to protect. The canonical matcher accepts mixed underscore forms likeclient_secret, but it does not match kebab-case variants such asprivate-key, and the inline scrubber inherits the same gap. I verified this against the current regex:private-key=AAAis returned unchanged, whileclient-secret=BBBonly redacts because the genericsecretalternative happens to match that substring. SinceHarnessClientnow runs all request / response / error bodies throughredactJsonString()by default, malformed or YAML payloads usingprivate-key:(a realistic PEM field spelling) can still hit logs with sensitive material intact.Open questions / assumptions:
- I scoped this review to the requested commit range and the changed files. I did not re-raise the inherited
src/index.tsinstructions-budget issue because the touched lines there only swap ingetVersion()and the current oversized instructions block predates this PR.- I could not run
pnpm testorpnpm typecheckin this runner becausenodeandpnpmare not available onPATH, so the verification notes below are based on source inspection plus small standalone regex probes withpython3.Change summary:
- The earlier FME transport-safety concern appears fixed:
ConfigSchemanow applies the same HTTPS /HARNESS_ALLOW_HTTPgate toHARNESS_FME_BASE_URL, andtests/config.test.tscovers reject / allow / https cases.- The earlier non-JSON parse-failure redaction concern also appears materially fixed for the reported cases: the inline fallback now shares the same key list as the structured matcher and correctly redacts bearer tokens, quoted multi-word secrets,
webhook,encrypted,secret_key, andssh_keyin the added tests.- Remaining verification gap: the new
HarnessClientlogging integration (HARNESS_LOG_UNSAFE_BODIESoff by default, bypass when true) still has no direct regression test at the call sites insrc/client/harness-client.ts.Verification notes:
- Reviewed the diff for
specs/002-endpoint-spec-transport-overrides.md,src/client/harness-client.ts,src/config.ts,src/index.ts,src/utils/cli.ts,src/utils/redact.ts,tests/config.test.ts, andtests/utils/redact.test.tsagainst the architecture rules inCLAUDE.md/AGENTS.md.- Confirmed from source that FME requests set
Authorization: Bearer ${HARNESS_API_KEY}insrc/registry/index.ts, which makes the new HTTPS guard insrc/config.tssecurity-relevant and correctly aligned with the standards.- Ran targeted regex probes with
python3to verify that the priorauthorization: Bearer ...and whitespace/quoted-value cases are now scrubbed, and to confirm the residualprivate-keymiss described above.Sent by Cursor Automation: Sunil On Demand Architecture Review
Broaden private_?key, client_?secret, and *_?token patterns to [_-]? so kebab-case keys (private-key, client-secret, access-token, etc.) are redacted in both JSON and plaintext contexts. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Stale comment
Findings
- Important:
src/config.ts/src/client/harness-client.ts—HARNESS_FME_BASE_URLis now configurable, butHarnessClientstill unconditionally addsHarness-AccountandaccountIdentifiertoproduct: "fme"requests. That means the new override sends Harness-specific scoping data to whatever Split/FME endpoint the user configures, which contradicts the transport contract and leaves the FME wiring incomplete.- Important:
src/utils/redact.ts/src/client/harness-client.ts/tests/utils/redact.test.ts— the new redaction matcher only catches exact key names likewebhook, so common payload fields such aswebhookUrlstill pass through unredacted. Because the client now funnels request/response/error body debug logs through this helper, debug mode can still leak secret-bearing webhook URLs, and the added tests do not cover that shape.- Suggestion:
src/index.ts— the serverinstructionsstring still violates the anti-bloat rules inCLAUDE.md/AGENTS.md: it is over the ~20-line target and still contains per-resource examples plus pipeline-version guidance that those docs say should live in endpoint/resource metadata instead of the global instructions block.Open Questions / Assumptions
- I could not rerun
pnpm test/pnpm typecheckin this automation environment becausenode/pnpmwere not available onPATH, so this review is based on static inspection of the diff, currentHEAD, and the checked-in tests.- I assumed
HARNESS_FME_BASE_URLis meant to support non-default/custom endpoints. If it is guaranteed to stay on Harness-controlled Split infrastructure only, the first finding is lower risk, but the transport mismatch still remains.- If end-to-end debug redaction is meant to cover non-client logs too,
src/tools/harness-execute.tsstill appears to log mergedinput/paramsraw at debug level outside the new helper path.Assessment
Version sourcing looks improved:
src/index.tsnow pulls the server version frompackage.json, which moves this PR toward the intended single source of truth, and the new HTTPS validation forHARNESS_FME_BASE_URLis the right guard. I don't think the PR fully meets Sunil's architecture/security standards yet, though, because the FME transport override and debug-log redaction are still incomplete, and the global instructions block remains out of policy.Sent by Cursor Automation: Sunil On Demand Architecture Review
| ); | ||
| } | ||
|
|
||
| if (!data.HARNESS_FME_BASE_URL.startsWith("https://") && !data.HARNESS_ALLOW_HTTP) { |
There was a problem hiding this comment.
Making HARNESS_FME_BASE_URL configurable is a good start, but the transport layer still treats product: "fme" requests like Harness requests: HarnessClient always adds Harness-Account and accountIdentifier unless headerBasedScoping is set. With this override in place, those internal scoping values now get sent to whatever endpoint the user points this at, which contradicts the RequestOptions contract and leaves the FME wiring incomplete.
| @@ -0,0 +1,60 @@ | |||
| const SENSITIVE_KEYS = | |||
| "token|secret|password|authorization|bearer|credentials?|webhook|private[_-]?key|client[_-]?secret|api[_-]?key|secret[_-]?key|access[_-]?key|ssh[_-]?key|passphrase|encrypted|access[_-]?token|refresh[_-]?token|id[_-]?token|session[_-]?token|cookie"; | |||
There was a problem hiding this comment.
SENSITIVE_KEY_PATTERN is anchored to exact key names, so this catches webhook but not common fields like webhookUrl. harness-client now routes request/response/error body logs through this helper, so payloads with a webhookUrl field will still be logged verbatim at debug level. The repo already accepts/emits webhookUrl fields in pipeline/template payloads, so this is a live leak path rather than a hypothetical regex gap.
There was a problem hiding this comment.
Stale comment
Important findings:
src/config.ts,src/client/harness-client.ts,src/client/types.ts— P7 wiresHARNESS_FME_BASE_URLinto config, butproduct: "fme"requests still do not honor the transport contract.HarnessClient.request()/requestStream()computeisFmeand then ignore it, still sendingHarness-Account, whilebuildUrl()still injectsaccountIdentifierunlessheaderBasedScopingis set. That leaves the new FME override only half-wired: Split/FME traffic still carries Harness-specific scoping metadata even though the client type comment says FME should skip Harness-specific auth/headers/params.
src/utils/redact.ts— the new debug-log redaction still misses livewebhookUrlfields.SENSITIVE_KEY_PATTERNis exact-match onwebhook, sowebhookUrlsurvives both the JSON walk and the parse-failure fallback. This repo's pipeline/template schemas already includewebhookUrl, and a quick probe against the current regex leaveswebhookUrl: https://example.invalid/...intact, so request/response debug logging can still leak secret callback endpoints.Open questions / assumptions:
- I reviewed current HEAD
2ebd148c8cc88b1021fad61afa4c1f4e15a4eff7; the earlierHARNESS_FME_BASE_URLHTTPS-guard gap and the kebab-case parse-fallback redaction gap appear fixed.- I could not run
pnpm testorpnpm typecheckin this runner becausenode/pnpmare not available onPATH, so this pass is source-based plus small standalone regex probes.Change summary:
- Version sourcing now correctly uses
getVersion()/package.json.HARNESS_FME_BASE_URLis now configurable and guarded by HTTPS /HARNESS_ALLOW_HTTP.- The redaction work materially improves the prior leak paths, but the remaining
webhookUrlmiss means the PR still does not fully meet Sunil's logging-safety standard.Sent by Cursor Automation: Sunil On Demand Architecture Review
- HarnessClient: skip Harness-Account header for product='fme' requests so FME/Split API calls don't carry Harness-specific scoping metadata. - buildUrl: skip accountIdentifier query param for FME requests. - Redaction: add webhookUrl, callback_url compound key patterns to SENSITIVE_KEYS so webhook endpoints in pipeline schemas are redacted. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Findings
- Critical — still present on this PR branch:
src/utils/redact.ts,src/client/harness-client.ts
The new redaction path is not strong enough to satisfy the repo'sNEVER expose secret valuesrule.redactJsonString()falls back to a single-line regex for any non-JSON body, so common YAML block-scalar shapes (for exampleprivateKey: |- ...) still leak the actual secret material, and the key allowlist misses real secret fields from our shipped schemas such asintegrationKey. BecauseHarnessClientnow routes request/error/response debug body logging through this helper, secrets can still land in stderr logs wheneverLOG_LEVEL=debug. The base branch already logged bodies unsafely; this PR improves the default, but the leak is still present on the PR branch. - Important — introduced by this PR:
src/config.ts
HARNESS_FME_BASE_URLis now a supported runtime setting, but the shipped config surfaces are not updated..env.example,manifest.json, andmcp-directory/manifest.jsonstill do not expose the variable, so bundled/manifest consumers cannot actually configure the new FME base URL. That leaves the "FME config wiring" change incomplete and drifts from the repo's environment-configuration/documentation standard.
Open Questions / Assumptions
- I could not run
pnpm testorpnpm typecheckin this runner becausepnpmwas not onPATH, so this review is based on static diff inspection and repo searches. - I'm assuming the MCPB manifests are part of the supported shipping surface, since
scripts/prepare-mcpb.jscopies them into the bundle.
Overall Assessment
Not yet aligned with Sunil's architecture standards. The PR moves in the right direction on version unification and safer default logging, but it still falls short of the NEVER expose secret values requirement and does not fully wire/document the new runtime config across the supported distribution surfaces.
Sent by Cursor Automation: Sunil On Demand Architecture Review
| * Value capture handles quoted strings (double or single) and unquoted values up to the next delimiter. | ||
| */ | ||
| const INLINE_SECRET_PATTERN = new RegExp( | ||
| `(["']?(?:${SENSITIVE_KEYS})["']?)\\s*[:=]\\s*("[^"]*"|'[^']*'|[^,\\n}{\\]]+)`, |
There was a problem hiding this comment.
This fallback still leaks real secrets for the non-JSON/YAML path that HarnessClient now uses for debug body logging. It only rewrites single-line key: value pairs, so a common shape like:
privateKey: |-
-----BEGIN PRIVATE KEY-----
...still leaves the PEM body intact. The allowlist also misses concrete secret fields from our shipped schemas (for example integrationKey on PagerDuty notification channels). Since the project standard is "NEVER expose secret values", I think this needs a parser-based/non-logging fallback before we rely on it as the redaction layer.
| @@ -75,6 +75,7 @@ const RawConfigSchema = z.object({ | |||
| HARNESS_ALLOW_HTTP: booleanFromEnv.default(false), | |||
| HARNESS_MCP_ALLOWED_HOSTS: optionalStringFromEnv.transform(validateAllowedHosts), | |||
| HARNESS_FME_BASE_URL: urlFromEnv("https://api.split.io"), | |||
There was a problem hiding this comment.
HARNESS_FME_BASE_URL becomes a supported runtime setting here, but the PR doesn't propagate it to the public config surfaces (.env.example, manifest.json, mcp-directory/manifest.json). That makes the "FME config wiring" only partial: manifest/MCPB consumers still can't set the new base URL, and the documented env surface drifts from runtime behavior.
There was a problem hiding this comment.
Important finding:
src/utils/redact.ts,src/client/harness-client.ts— the new non-JSON fallback redactor still leaks structured secret values.INLINE_SECRET_PATTERNstops at the first}/ newline /], so payloads likesecret: {"value":"supersecret"}becomesecret: [REDACTED]{"value":"supersecret"}, and YAML block scalars such asprivateKey: | ...keep the PEM body after the first line. BecauseHarnessClientnow routes request, error, and response debug bodies throughredactJsonString()by default, the branch still misses Sunil'sNEVER expose secret valuesstandard in the exact path P8 is intended to harden.
Open questions / assumptions:
- Reviewed current HEAD
17d47761543ed3e3cb5d1b9567a7c82992761ef6. node/pnpmare not available on this runner, so I could not rerunpnpm test/pnpm typecheck; verification here is source-based plus targetedpython3probes of the new regex behavior.- I scoped findings to the PR's current diff. The oversized global
instructionsblock insrc/index.tslooks inherited from the base branch, so I did not re-raise it as a diff-scoped blocker here.
Change summary:
- P1 looks aligned now: MCP server version comes from
package.jsonviagetVersion(). - P7 also looks materially aligned now:
HARNESS_FME_BASE_URLis wired through config, guarded by HTTPS /HARNESS_ALLOW_HTTP, andproduct: "fme"requests now skip Harness account header/query scoping. - P8 is improved, but not complete yet because the fallback redactor still leaks object/block-style secrets.
Sent by Cursor Automation: Sunil On Demand Architecture Review
| * Value capture handles quoted strings (double or single) and unquoted values up to the next delimiter. | ||
| */ | ||
| const INLINE_SECRET_PATTERN = new RegExp( | ||
| `(["']?(?:${SENSITIVE_KEYS})["']?)\\s*[:=]\\s*("[^"]*"|'[^']*'|[^,\\n}{\\]]+)`, |
There was a problem hiding this comment.
This still leaks secrets on the parse-failure path. The value pattern stops at } / newline / ], so inputs like secret: {"value":"supersecret"} become secret: [REDACTED]{"value":"supersecret"}, and YAML block scalars such as privateKey: | -----BEGIN PRIVATE KEY----- ...
keep the PEM body after the first line. Since HarnessClient now sends request / error / response debug bodies through redactJsonString() by default, this branch can still expose secrets in the exact path P8 is supposed to harden. Can we make the fallback drop the entire field payload (or add a safer multiline parser) and cover these shapes in tests/utils/redact.test.ts?
|
Overall: Approve with minor follow-ups P1 — Version unification P7 — FME base URL wiring The bonus FME transport fixes in commit 17d4776 are correct and necessary: harness-client.ts Issue (must-fix nit): the FME transport changes have zero test coverage. tests/client/harness-client.test.ts covers baseUrl override but never asserts that product: "fme" omits Harness-Account or accountIdentifier. A regression to either branch would silently start leaking account scope to Split.io. Recommend adding two assertions in request — headers / request — URL describes: it("omits Harness-Account header for product='fme'", async () => { P8 — Debug log redaction Depth cap (10) — verified that anything deeper collapses to [REDACTED] instead of leaking. Defense in depth. One real gap: the call sites in harness-client.ts aren't tested for the HARNESS_LOG_UNSAFE_BODIES toggle behavior. The redaction utility is heavily tested in isolation; the integration ("when flag is true, raw body is logged; when false, redacted") relies on visual inspection. Not blocking, but a one-line assertion would close the loop. Test 4 from my probe also surfaces a non-issue worth being aware of: stringified secrets inside JSON values (e.g. {"yamlBody": "token: secret123"}) are NOT scrubbed because the redactor short-circuits on strings. This is correct behavior for "Request body" logs (the keys are what we trust as a redaction signal), but if anyone ever logs a YAML-as-string body, secrets in those values will pass through. Probably worth a JSDoc note on redactSensitiveFields. Documentation gap HARNESS_FME_BASE_URL — useful for self-managed/staging FME backends Spec 002 Two suggestions: Land it as a separate doc-only commit/PR rather than bundled here. PR 108's stated scope is P1/P7/P8; the spec is unrelated to those. Mixing 361 lines of design discussion with three small fixes makes the PR harder to skim. FME transport assertions added to tests/client/harness-client.test.ts (Harness-Account header omission, accountIdentifier query omission). Move specs/002-endpoint-spec-transport-overrides.md to its own doc PR. |


Summary
Addresses three Phase 1 quality review items (stacked on #107):
P1 (Version unification): MCP server metadata now reads version from
package.jsonviagetVersion()instead of hardcoded"2.0.0". Single source of truth — CLI--versionand MCPinitializeresponse both reflectpackage.json.P7 (Dead config):
resolveProductBaseUrl()now usesHARNESS_FME_BASE_URLfrom config (defaults tohttps://api.split.io) instead of a dead hardcoded const. Self-managed/staging environments can now override the FME base URL via env var.P8 (Debug log redaction): All 3 debug-level body log sites in
harness-client.tsnow run throughredactSensitiveFields()by default. Redacts values for keys matching:token,secret,password,apiKey,credential,privateKey,clientSecret,authorization,bearer,webhook,passphrase,encrypted,sshKey,accessKey,secretKey. SetHARNESS_LOG_UNSAFE_BODIES=trueto bypass for local debugging.Also includes deferred spec 002 (TransportOverrides) for future reference — design is pre-approved but implementation deprioritized per review.
Test plan
pnpm typecheck— cleanpnpm test— 1072/1072 pass (includes 11 new redaction tests)--versionCLI output matchespackage.jsoninitializeresponseserverInfo.versionmatchespackage.jsonLOG_LEVEL=debugshow[REDACTED]for sensitive fieldsHARNESS_LOG_UNSAFE_BODIES=truebypasses redactionMade with Cursor