fix(integrations): validate runtime values the type system only claims to constrain
Four verified defects from the review of the v0.8.16 release PR (#7224). Each is the same shape: a declared TypeScript type standing in for a check that never runs.
Two other findings on that PR were checked and deliberately not changed — details at the bottom.
Datadog — site decided where the credentials went
DatadogSite is a compile-time union, erased at runtime. site is interpolated straight into the request host, and every Datadog request carries DD-API-KEY and DD-APPLICATION-KEY:
return `https://api.${site || 'datadoghq.com'}${path}`
An unvalidated value therefore chose the destination for the workspace's Datadog credentials. evil.com addresses api.evil.com; datadoghq.com@evil.com addresses evil.com with the expected host parsed as userinfo.
The site list is now a runtime array with DatadogSite derived from it, so the two cannot drift, and both host builders resolve through one validator: datadogApiUrl (31 call sites) and the separate logs-intake host in send_logs. That ternary in send_logs had three branches that all produced the same string, so it collapses to the one expression.
Not reachable from the editor today — the block renders site as a dropdown and the param is user-only, so neither a user nor a model can set it. The value still survives in stored workflow state, which imports and programmatic edits write without passing through that dropdown. This is the defence-in-depth layer, not a live exploit.
CB Insights — optional chaining is not a type check
params.x?.trim() guards undefined, not the type. A block-to-block reference resolving to a number reached .trim() and threw a bare TypeError naming no parameter. The direct sibling, get-commercial-maturity-history, already used parseOptionalStringParam for the identical startDate/endDate pair; the remaining 19 sites now do too.
Everything else is unchanged, and that is checked rather than assumed: compactBody already dropped '', null, and undefined identically, and every use outside it only tests falsiness — so '' and undefined were already interchangeable at all 19 sites. The only behaviour that changes is the non-string case.
CB Insights RAG — the guard contradicted its own contract
message.length > 10_000 admitted a 10,000-character message, while both the thrown error and the tool's param description say "under 10,000 characters". Now >= 10_000, so the code, the error, and the documented contract agree.
Managed Agent — a throw that escaped the result contract
denyMessage was trimmed above the try, so a non-string threw past every success: false path the operation otherwise returns. It is now coerced the same way decision is a few lines above it.
Checked and left alone
Drive addParents/removeParents — the finding says sending the destination in both parameters can make Drive reject the update. Google's own documented move sample does exactly what this code does — fetch all current parents, pass them all as removeParents, pass the destination as addParents, with no exclusion — in C#, Java, Python, and JavaScript. No documentation supports the claimed failure, so changing it would mean deviating from the vendor's canonical pattern on a guess.
Bitbucket zero-byte Range — the finding says Range: bytes=0-N against an empty file can return 416 instead of an empty result. RFC 9110 makes that plausible, but Bitbucket's actual behaviour on the raw endpoint is undocumented and there is no test coverage either way. Unverified, so unchanged.
Verification
- Each of the four fixes was reverted individually and its test watched go red before being restored.
bun run check:audits— 39/39bun run lint— cleantype-check— no errors in any touched file (the worktree's other errors are the known stale workspace-package resolution and exist on the base)- 162 tests pass across
tools/datadog,tools/cbinsights,tools/managed_agent tool-metadata:check,docs:check,integration-catalog:check, block-registry — all pass