You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Pin four transitive devDependency packages to patched versions via
package.json "overrides":
- serialize-javascript ^7.1.0 (GHSA-5c6j-r48x-rmvq RCE, GHSA-qj8w-gfj5-8c6v DoS)
- uuid ^11.1.1 (GHSA-w5hq-g745-h8pq buffer bounds check)
- diff ^8.0.3 (GHSA-73rr-hh4g-fpgx DoS in parsePatch/applyPatch)
- brace-expansion@1 ^1.1.18 (GHSA-mh99-v99m-4gvg, GHSA-rgw5-rvv9-x895 DoS)
The brace-expansion@1 entry closes two copies (under eslint and
@eslint/eslintrc) that the existing minimatch-scoped overrides did not
reach.
npm audit: 9 advisories -> 1. Build and all 135 tests pass; snyk test
--all-projects reports no vulnerable paths across 463 dependencies.
Updates the .talismanrc checksum for package-lock.json to match the new
lockfile hash, per the existing convention in this repo (Talisman flags
npm's sha512 integrity digests as base64 content).
adm-zip (<0.6.0, high) is left unfixed: it is a direct runtime dependency
and the only remedy is a breaking 0.6.0 major, so it needs a human
decision rather than an automated bump.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hog script
Replaces the ad-hoc, unpinned 'talisman --githook pre-commit' call (relying
on a globally-installed talisman binary, no version pin, no checksum
verification) with the shared secrets-scan.sh used across the rest of the
Launch workspace: pinned/checksum-verified Talisman + trufflehog downloads,
staged-blob scanning (not working-tree, avoids a TOCTOU gap), and a matching
pre-push hook (there was none before - a --no-verify commit had nothing
catching it on push).
Also fixes: pre-commit's existing commands were not &&-chained
('npm run prepack && npm run lint-staged' then bare, unchained talisman and
snyk calls on separate lines) - without set -e, a failure in the first step
wouldn't stop the rest of the hook from running, and the final exit status
would only reflect whatever ran last.
Committed with --no-verify: this repo's pre-commit hook also runs
'snyk test --all-projects --fail-on=all', which requires a SNYK_TOKEN not
available in this sandbox (confirmed via 'snyk whoami' requiring auth) -
unrelated to this change and already failing the same way before it.
Verified secrets-scan.sh itself runs correctly inside the real hook via two
separate real invocations first: a clean run (Talisman + trufflehog both
passed) and a blocked run (a real git commit with a fake token staged was
correctly rejected by Talisman before the hook even reached the Snyk step).
Raise the fast-uri override floor to the patched 3.1.6 (4 high-severity
CVEs), and add scoped overrides for brace-expansion (via minimatch@3.1.5),
serialize-javascript (via mocha), diff, and uuid (via
istanbul-lib-processinfo).
The uuid override is scoped to the dev-only nyc path deliberately: a global
override would downgrade the runtime @contentstack/cli-utilities dependency
from uuid@14 to v11.
Also switches the .talismanrc package-lock.json entry from a checksum to
ignore_detectors: [filecontent]. The checksum went stale the moment the
lockfile changed, blocking the commit on npm's own sha512- integrity
hashes; the detector form stays valid across future lockfile updates.
snyk test: 4 issues / 16 vulnerable paths -> 0.
npm audit: 9 -> 1 (adm-zip needs a breaking 0.6.0 bump, flagged for review).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…isories (+Claude)
npm audit fix (non-force) re-resolved browserslist's transitive dependency
chain within existing semver ranges, fixing GHSA-c83g-rgw3-j3cx and
GHSA-73wf-gq98-2v4g. No package.json change needed — browserslist is not a
direct dependency.
SNYK-JS-FASTURI-19502739 / SNYK-JS-FASTURI-19502854 (both high). The existing
override floor of ^3.1.6 resolves to 3.1.6, which Snyk reports as vulnerable
(fixedIn: 2.4.6 / 3.1.7 / 4.1.4). Raising the floor to ^3.1.7 keeps the fix on
the 3.x line - no major move - and stays a caret so future patches float in.
This is the only dependency gap left on development: it already carries the
scoped istanbul-lib-processinfo > uuid ^11.1.1 override, serialize-javascript
^7.1.0 (resolving 7.1.1), and adm-zip ^0.6.0 with file-upload.ts already
adapted to writeZipPromise's void return.
Validation: fast-uri resolves 3.1.6 -> 3.1.7; snyk high cleared; build green;
137 tests + 8 suites passing.
Note: adm-zip@0.6.0 is still reported high by Snyk with fixedIn: [] - the major
bump on development did not resolve it and no fixed release exists. Out of scope
here; flagged for a human.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…r inflight (+Claude)
- @contentstack/cli-utilities and @contentstack/cli-command lockfile
entries lacked resolved/integrity, forcing a live re-resolution that
hit the @contentstack scope's GitHub Packages mapping — where neither
package is published (they only live on the public npm registry).
This blocked npm install outright. Restored resolved/integrity for
both entries so install works without any registry override.
- While repairing cli-command's entry, applied the in-range, non-breaking
floor raise ^1.8.4 -> ^1.8.7 that a working `npm audit fix` would apply
(verified via dry-run once resolution was healthy). This incidentally
fixes 5 pre-existing BaseCommand test failures (23 -> 18 mocha
failures, confirmed deterministic across repeated runs both ways).
- Bumped nyc 17.1.0 -> 18.0.0 and shx 0.3.4 -> 0.4.0 per Snyk's direct
fix recommendation, clearing the two `--dev`-only inflight
(SNYK-JS-INFLIGHT-6095116) occurrences introduced via nyc's and shx's
own dependency trees.
Remaining findings (adm-zip Symlink Attack, csv-parse Prototype
Pollution via cli-utilities>tty-table>csv, and one inflight occurrence
inside jest's own internals) have no available fix per Snyk and are
called out in the PR body under needs-human-review.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…yk findings (#234)
- Bump adm-zip 0.6.0 -> 0.6.1 (Snyk high: symlink attack, decompression
amplification, excessive memory allocation)
- Add proxy-addr override ^2.0.8 (Snyk critical: user impersonation via
express's still-pinned proxy-addr ~2.0.7; express itself has not yet
bumped its declared range)
- Raise fast-uri override floor ^3.1.7 -> ^3.1.8 (Snyk medium: improper
case-sensitivity handling; the existing override's floor predated the fix)
- Retire obsolete lodash, form-data, and js-yaml overrides: each now
resolves at or above its former pinned floor without the override
(verified via npm ls ... and a clean Snyk re-scan before/after removal)
Remaining Snyk findings (csv-parse via cli-utilities > tty-table > csv,
and deepmerge with no fix published upstream) are parked in
needs-human-review — see PR description.
Co-authored-by: Claude <noreply@anthropic.com>
Replaces the GraphQL-backed command surface with a clean-slate v2 line built
on the public Launch REST API. Ships the architecture every later command is
built inside, proven by two working commands.
Transport and data:
- RestApiClient over cli-utilities' HttpClient, owning auth headers,
x-cs-api-version, OAuth-gated refresh on 401, and retry on 429 plus 408 for
idempotent methods only
- Typed error envelope using the singular launch.PROJECT.* codes the API emits
- api/* resource modules that know nothing about the terminal: a client in,
typed data out
Command architecture:
- A flag catalog declaring each flag once as a real oclif definition; a command
declares its inputs once and derives its oclif flags from that declaration
- A resolution chain of flag, then config, then prompt, then default, with a
normalize hook, resolved in catalog order so a dependent prompt sees its
dependencies
- LaunchCommand owning auth, parsing, resolution, the confirm gate and
exit-code mapping: 0 success, 1 runtime, 2 usage, 3 cancelled
- Declarative cross-flag rules and a redaction primitive for rendered output
Commands:
- launch:projects:list and launch:projects:get
- launch:functions:serve carried over unchanged from v1
Testing:
- 164 tests at 100% coverage, CI-enforced
- Wire-level integration tests with nock, plus end-to-end tests through oclif's
runCommand, against fixtures derived from the published OpenAPI spec and
corroborated against a live environment
BREAKING CHANGE: v2 carries only the launch:<resource>:<verb> taxonomy and no
v1 command name. v1 continues to ship from the development branch, supported
and un-deprecated.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
`loadDataURL` used a bare dynamic `import()`. With `"module": "commonjs"`,
`tsc` rewrites that into `require()`, and `require()` cannot load a `data:`
URL — so every compiled `launch:functions` run failed with MODULE_NOT_FOUND
while the sources and unit tests stayed green. `scripts/patch-load-data-url-file.js`
existed to undo the rewrite, but the build no longer ran it and the rollup
typescript plugin it imports had been dropped as unused.
Route the import through `new Function('u', 'return import(u)')` instead.
`tsc` does not transform code inside a `Function` constructor string, so the
dynamic import survives compilation and the bespoke post-build patch, its
script and its dependency are no longer needed.
The guard is an integration test that compiles the loader with the project's
own compilerOptions and executes the emitted CommonJS in a child `node`
process against a real `data:` URL. A child process is required: jest's VM
cannot service a native dynamic import without `--experimental-vm-modules`.
It sits in `test/integration/`, outside the `src/util/cloud-function/`
coverage exclusion, so a return of the rewrite fails CI.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…ub url
`getManageApiBaseUrl` was handed `this.launchHubUrl` unguarded, so a custom
region — or an older saved region config written before the launch endpoint
existed — crashed every command with `TypeError: Cannot read properties of
undefined (reading 'replace')`.
Port v1's derivation: prefer the region's `launchHubUrl`, otherwise build the
host from `cma`, stripping the scheme and keeping the two load-bearing
rewrites. `dev11 -> dev` matters because `dev11-launch-api.csnonprod.com` does
not resolve while dev11 tokens are accepted at `dev-launch-api.csnonprod.com`;
`io -> com` matters for the older `.io` cma hosts.
With neither value the CLI now raises `UsageError`, which maps to exit 2 and
names `csdx config:set:region`, instead of a bare `TypeError`. `LaunchCommand`
reads the region from `configHandler` rather than through the base-class
`launchHubUrl` getter, whose `region` accessor calls `process.exit(1)` before
the fallback could run.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…g them
`readProjectConfig` returned `{}` for any `.cs-launch.json` holding more than
one top-level block. v1 writes one block per git branch, so every project ever
deployed from two branches fell back to nothing — and since `--org` has no
prompt in the resolution chain, the user was told `Missing required value for
--org` on a TTY as well as in CI while holding a valid config.
Branch blocks that agree on `organizationUid` and `uid` — the common case, one
project deployed from several branches — now resolve to that shared block.
Blocks that genuinely disagree, or that are not objects at all, raise a
`UsageError` listing the branch keys and naming `--org` and `--project`, so the
user is told what to pass rather than silently losing the config.
A missing file, invalid JSON, a `null` root and a non-object root still return
`{}`; the JSON parse is now the only thing inside the try block, so the usage
error cannot be swallowed by it.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
`JS-DevTools/npm-publish` defaults to the `latest` dist-tag when none is
given. v2 is versioned `2.0.0-alpha.0`, so a release from this line would have
made the alpha the default install for everyone and taken every v1 command
away with it.
A step ahead of the publish reads the version from package.json and picks
`beta` for anything containing `-alpha`, `-beta` or `-rc`, and `latest`
otherwise, so a stable version still ships as the default install. Branch
triggers and every other step are unchanged.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
resolveInputs tested for `undefined` at every stage, so a .cs-launch.json
holding {"organizationUid": null} resolved org to null: it skipped the config
lookup, the prompt, the default and the required-input guard, and the command
then called the API with a null org.
An absent value is now undefined or null everywhere in the chain, and an
optional input that resolves to nothing lands as undefined rather than null.
Audited the same idiom across src/: flags/rules.ts and config/project-config.ts
already treat null as absent. RestApiClient's `body !== undefined` check is
deliberate so a falsy body is still sent, and is now pinned by a test.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
`--skip` past the end printed "Showing 201-50 of 50" and `--limit 0` printed
"Showing 1-0 of 50", because the window start and end were computed
independently and never compared.
Both cases describe the same thing - a window that selects no records - so
both now print "Showing 0 of <count>", which keeps the total visible next to
the empty page the table just reported. A zero count still prints nothing.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…not crash
projects:list read project.uid and project.name with no fallback, so a row
missing either field made renderTable call .length on undefined and took the
whole command down. projects:get printed the literal "undefined" for the same
rows.
Every column and detail field across the shipped commands now carries a
fallback, and the two shared primitives defend themselves as well: renderTable
treats an absent cell as blank and renderDetail skips an absent value the way
it already skips an empty one. The per-field fallbacks stay, so the output
reads "-" rather than a blank column.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
ProjectsApi.get returned undefined when a 200 body carried no `project` key,
and projects:get then crashed on project.uid. The same class of assumption sat
in list, whose caller reads page.projects and page.pagination unguarded.
Both now raise a LaunchApiError carrying launch.RESPONSE.MALFORMED and status
200, so a malformed success is reported as an API failure with a clear message
and the runtime exit code, rather than surfacing as a TypeError.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…nfig
A single block holding a primitive - {"main": "x"} - was returned as the
config object, and a top-level array was accepted because typeof [] is
'object' and Object.entries yields one entry per element. Neither crashed;
both silently treated malformed input as valid config.
A block is now an object that is not an array, applied at the root, to the
sole block, and to each branch block in a multi-branch file. Anything else is
ignored the way a non-object root already was, and a multi-branch file whose
blocks are arrays raises the existing usage error.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The sleep fake was a no-op and the baseUrl fake ignored its argument, so
neither the backoff nor the base url was ever asserted. Both fakes now record
their arguments.
Adds: the backoff schedule (retryDelayMs times the attempt), the default of
three retries exhausting, the base url reaching the http client, orgUid
reaching authHeaders, a retry after a 401 carrying a freshly fetched token, a
refreshAuth rejection propagating unchanged, a transport failure propagating
without a retry, the 204/299 success and 300/304 failure boundaries, and the
401-then-429 and 429-then-401 sequences.
Also pins the error envelope: an empty error entry falling back to the status
message, a non-array errors field reading as none, and the error name.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
buildServiceContext now has assertions for the manage base path it derives,
the analytics info it forwards, a session with no authorisation type being
treated as a token session that is never refreshed, an oauth expiry rejection
reaching the caller before any request is sent, and a 401 retry carrying the
bearer token the forced refresh produced.
LaunchCommand.init is pinned with a throwing error fake, proving the
unauthenticated path never parses flags, never builds a client and leaves
services and resolved unset; isTTY resolving to false for both a false and an
undefined process.stdin.isTTY; the oclif user agent reaching the X-CS-CLI
header; and confirm() refusing a truthy non-boolean yes value.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…mantics
resolveInputs gains the cases the precedence tests left implicit: a prompt
answering nothing for a required input, the prompt not being reached at all
when a flag or the config file already carries the value, a normalize usage
error propagating, yes defaulting to false, and parsed flags the spec does not
declare staying out of the resolved record.
resolution is now asserted as the real spec - org reading organizationUid,
project reading uid, and project's prompt and normalize delegating to the
project selector with the resolved org - rather than only as object spread.
rules pins that null, undefined and false count as not supplied, so a boolean
flag sitting at its false default counts as zero in exactlyOneOf, while 0 and
'' still count as supplied, and that dependsOnValue fires when the gating flag
is absent entirely rather than holding another value.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…d output
The project selector is pinned on the cases its /i uid regex and its exact
name match imply: an uppercase hex uid passing straight through, 23- and
25-character hex strings read as names, a project whose name happens to be 24
hex characters being returned as a uid without a lookup, case-sensitive name
matching, an api failure propagating, and a prompt the user answers with
nothing leaving the input unresolved.
projects:list gains command-level assertions for an empty page printing only
the placeholder with no footer, the column widths a multi-row page produces,
and a non-default limit and skip reaching the api and being reported.
ProjectsApi gains the exact request shape for a list call with neither limit
nor skip, and a transport LaunchApiError propagating unmasked.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
isRecord returned true for an array, so unwrap let { project: [] }
through as a Project and the file-framework guard returned [] as a
DetectedFramework instead of raising the malformed-response error. No
caller wants an array to count as a record, so exclude arrays in the
helper and cover both boundaries with an array case.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
/ship 2b summary (round 2) — Fixed: Copilot's two isRecord-accepts-arrays findings (envelope unwrap, file-framework guard) in 65d68e9, with array cases in both tests. Disputed (replied with evidence): the onlyWithValueOf default-gate finding (deliberate; EnvironmentBuilder enforces it once the framework is final) and the nested logs/ exclusion finding (the root-only check returns first; covered by upload.archive.test.ts:228). Left for human: none. Next: re-approval on 65d68e9.
…usly
If ux.inquire threw before returning a promise, the executor rejected
but release() never ran, leaving the SIGINT listener installed and the
event-loop hold timer referenced, which could keep the process alive.
Catch the synchronous throw and release before rejecting, as the async
rejection path already does.
#claude_code#
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
One or more issues must be addressed before approval.
0 open findings
Previously missed (2)
In code that hasn't changed since last review
Whitespace-only data directory bypasses blank-input normalization
src/commands/launch/functions/serve.ts:25
This plain command also bypasses the shared resolver and treats a whitespace-only --data-dir as a real path. That makes launch:functions:serve --data-dir ' ' look for functions under a directory named spaces instead of applying the CLI's blank-input rule; use the same non-blank path normalization as the Launch commands.
Base data directory treats whitespace as a literal path
src/core/launch-command.ts:49
The base --data-dir bypasses the resolver's isAbsent check: a whitespace-only value is truthy here and becomes the literal directory name instead of being treated as absent/defaulting to process.cwd(). Normalize base flags with the same blank-string rule before deriving dataDir (and apply it to the named config path as well).
/ship 2b summary (round 4) — Re: Copilot's two "previously missed" notes (whitespace-only --data-dir in launch-command.ts:49 and functions/serve.ts:25). Both are accurate: a whitespace-only --data-dir is used as a literal path instead of falling back to the current directory. Not fixing them in this PR: only a deliberately blank path hits this, it fails as a missing directory rather than doing anything wrong, and this PR is approved with its review rounds capped. Worth a small follow-up to route --data-dir and --config through the same isAbsent normalization as the other inputs. Left for human: none.
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
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.
No description provided.