fix(bootstrap): reject on non-2xx tarball response and handle zlib errors - #356
Conversation
…rors streamRelease now throws GithubError for HTTP 4xx/5xx responses instead of silently piping the error body (e.g. "404: Not Found") into the zlib decompressor. This was the root cause of the Z_DATA_ERROR crash when the cli-use branch was absent from a repo. extract now attaches an error handler directly on the zlib.createUnzip() stream. Node's pipe() does not forward stream errors, so without this listener a zlib failure emitted an unhandled error event and crashed the process rather than rejecting the Promise cleanly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
Moving cliux.loader() (spinner stop) out of finally and into catch before cliux.error() prevents the spinner's carriage-return from wiping the error line. Success path stops the spinner inline after getLatest resolves. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
Replace the generic cliux.error+rethrow pattern with a single clean Error throw so oclif prints one message. Message names both the repo and the missing cli-use branch so the developer knows exactly what to check on GitHub. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
There was a problem hiding this comment.
Pull request overview
This PR hardens the contentstack-bootstrap plugin’s GitHub tarball download + extraction path so csdx cm:bootstrap fails gracefully (rejects promises) instead of crashing on invalid gzip data returned from failed GitHub responses.
Changes:
- Add HTTP status validation to
streamRelease()so error responses aren’t treated as tarball streams. - Attach an
errorhandler to the unzip stream inextract()and add unit tests covering these failure modes. - Update bootstrap error messaging for missing/unavailable app downloads.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/contentstack-bootstrap/test/github.test.js | Adds unit tests for streamRelease() status handling and extract() invalid gzip rejection. |
| packages/contentstack-bootstrap/src/bootstrap/index.ts | Adjusts loader lifecycle and maps GitHub 404s to a user-facing “app unavailable” error. |
| packages/contentstack-bootstrap/src/bootstrap/github/client.ts | Adds response status checking before returning the tarball stream; adds unzip error handling. |
| packages/contentstack-bootstrap/messages/index.json | Introduces a new user-facing message for app download unavailability. |
| .talismanrc | Updates checksums / ignore entries (incl. newly added test file). |
Suppressed comments (1)
packages/contentstack-bootstrap/src/bootstrap/github/client.ts:93
extract()now listens forunziperrors, but the sourcestreamcan still emit anerrorevent with no listener (Node treats that as an unhandled exception). Attach an error handler to the input stream so network/IO failures reject the Promise instead of crashing.
return new Promise((resolve, reject) => {
const unzip = zlib.createUnzip();
unzip.on('error', reject);
stream
.pipe(unzip)
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Add stream.on('error', reject) to handle network/IO failures on the
source stream, not just zlib decompression errors
- Use distinct error message for non-404 HTTP failures (5xx, 403, etc.)
so users aren't told "repo not found" when it's a server/auth error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
packages/contentstack-bootstrap/test/github.test.js:2
github.test.jsnow requiressinon, butpackages/contentstack-bootstrap/package.jsondoes not declare it indevDependencies. This makes the test suite depend on workspace hoisting (e.g.shamefully-hoist) and can break if hoisting settings change.
const sinon = require('sinon');
packages/contentstack-bootstrap/test/github.test.js:93
- Avoid using a token-like literal in tests if it triggers secret-scanner false positives. Using a clearly dummy value also makes it easier to remove the
.talismanrcallowlist entry for this file.
const client = new GitHubClient(GitHubClient.parsePath('contentstack/private-repo'), true, 'my-token');
await client.streamRelease(client.gitTarBallUrl);
const callOptions = httpStub.options.firstCall.args[0];
expect(callOptions.headers).to.deep.equal({ Authorization: 'token my-token' });
- Widen CLI_BOOTSTRAP_APP_UNAVAILABLE to cover both repo and branch missing, not just branch, since GitHub returns 404 for both cases - Change status check from >= 400 to < 200 || >= 400 so unexpected non-2xx responses (e.g. stray 3xx) are also rejected as invalid Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
✅ BUILD PASSED - All security checks passed |
- Widen status check from >= 400 to >= 300 so unexpected 3xx responses are rejected before being streamed into extract() - Add regression test asserting a 302 response throws GithubError - Update .talismanrc checksum for github.test.js to reflect new 302 test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
🟡 Medium Severity - SLA Breached Issues (with fixes)Showing 2 issue(s) that have exceeded the 90-day SLA threshold:
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
❌ BUILD FAILED - Security checks failed Please review and fix the security vulnerabilities before merging. |
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
✅ BUILD PASSED - All security checks passed |
sinon was used in tests but only available via pnpm workspace hoisting. Adding it explicitly to the package's own devDependencies ensures consistent resolution under strict pnpm module resolution. Version ^21.1.2 matches the standard used across most packages in the monorepo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
🟡 Medium Severity - SLA Breached Issues (with fixes)Showing 2 issue(s) that have exceeded the 90-day SLA threshold:
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
❌ BUILD FAILED - Security checks failed Please review and fix the security vulnerabilities before merging. |
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
✅ BUILD PASSED - All security checks passed |
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
✅ BUILD PASSED - All security checks passed |
Problem
csdx cm:bootstrapcrashed with an unhandledZ_DATA_ERROR(incorrect header check) when cloning the Kickstart Next.js starter app. Two bugs combined to cause this:streamRelease()did not check the HTTP response status. When thecli-usebranch was absent fromcontentstack/kickstart-next, codeload.github.com returned a404: Not Foundbody. That body stream was silently passed downstream as if it were a valid tarball.extract()had no error handler on thezlib.createUnzip()stream. Node'spipe()does not forward stream errors between stages. When zlib tried to decompress the"404: Not Found"bytes (which have no gzip magic header), it emitted anerrorevent on theUnzipinstance with no listener — causing an unhandled exception that crashed the process instead of rejecting the Promise cleanly.Relates to: DX-10257
Fix
streamRelease()— throwsGithubErrorwith the actual HTTP status code for any4xx/5xxresponse. The existingBootstrap.run()catch block already handlesGithubErrorwithstatus === 404and prints a user-friendly "Unable to find a repo" message; no caller changes needed.extract()— extracts thezlib.createUnzip()instance and attaches.on('error', reject)directly to it, so zlib errors reject the Promise rather than escaping as unhandled events.Test plan
packages/contentstack-bootstrap/test/github.test.jsstreamReleasethrowsGithubError(404)on a 404 responsestreamReleasethrowsGithubError(500)on a 500 responsestreamReleasereturns the data stream on a 200 responsestreamReleasesendsAuthorizationheader for private reposstreamReleasethrows immediately for private repos with no tokenextractrejects withZ_DATA_ERROR(not a process crash) on invalid gzip datacsdx cm:bootstrap→ Kickstart Next.js ran end-to-end successfully after the missingcli-usebranch was created on the repo🤖 Generated with Claude Code