Skip to content

security: URL/path/JWT validation + proxy warning (APS-19008 partial / 19010 / 19011)#1141

Open
Rohannagariya1 wants to merge 1 commit into
masterfrom
security/cypress-cli-safe-subset
Open

security: URL/path/JWT validation + proxy warning (APS-19008 partial / 19010 / 19011)#1141
Rohannagariya1 wants to merge 1 commit into
masterfrom
security/cypress-cli-safe-subset

Conversation

@Rohannagariya1

Copy link
Copy Markdown
Collaborator

Summary

Ships the low-blast-radius subset of the CLI critical findings (per the agreed proposal); high-blast-radius items are left as documented accepted-risk / opt-in.

  • APS-19010 (env-var API redirect): only honour BSTACK_CYPRESS_NODE_ENV URL overrides (RAILS_HOST / UPLOAD_URL / DASHBOARD_URL / USAGE_REPORTING_URL) when they point at *.browserstack.com / *.bsstag.com / localhost; otherwise warn and fall back to the production defaults. Preserves all internal dev/QA usage; blocks arbitrary external redirects.
  • APS-19011: validate the API-supplied upload_url host before using it for the tests.zip upload; warn (not cert-pin) when an HTTP(S) proxy routes all API traffic incl. credentials; structural-only JWT check on the TestHub token (defence-in-depth — the CLI has no key to verify the signature).
  • APS-19008 (browserstack.json half): read browserstack.json via JSON.parse(fs.readFileSync) instead of require(), so a .js config can't execute code; require .json extension + project-root path containment.

New bin/helpers/securityValidation.js (stdlib-only): isAllowedBrowserstackUrl, isPathInsideBase, isWellFormedJwt.

Tested

  • New test/unit/bin/helpers/securityValidation.js13 tests, both paths (accepts prod/staging/localhost; rejects attacker hosts, suffix-spoofing like browserstack.com.attacker.net, non-http schemes; path-containment accepts inside / rejects ../ + sibling-prefix; JWT structural accept/reject). All pass.
  • Existing validateBstackJson tests still pass with the JSON.parse rewrite.
  • Smoke-tested config.js both paths: attacker RAILS_HOST warned+ignored (keeps prod default); legit *.bsstag.com override applied.

Deliberately NOT changed (accepted-risk / opt-in — needs product decision)

  • cypress.config.js sandbox (APS-19008 other half): cypress.config.js is legitimately JS that imports plugins; a vm sandbox breaks real configs, so it is NOT sandboxed by default.
  • npm_dependencies lifecycle scripts (APS-19009): STILL OPEN. Not fixed here. ⚠️ Note: PR [APS-19734] fix: harden .npmrc with supply-chain security directives #1128's repo .npmrc (ignore-scripts=true) does not protect end users — packageInstaller copies the user's .npmrc into the temp install dir, not the CLI's own. APS-19009's real fix (--ignore-scripts + package name/version validation + shell:false) remains open.

Fixes APS-19010; partially fixes APS-19008 and APS-19011.

…proxy

Ships the low-blast-radius subset of the CLI critical findings.

APS-19010 — env-var API redirect: only honour BSTACK_CYPRESS_NODE_ENV
url overrides (RAILS_HOST/UPLOAD_URL/DASHBOARD_URL/USAGE_REPORTING_URL) when
they point at *.browserstack.com / *.bsstag.com / localhost; otherwise warn and
fall back to the production defaults.

APS-19011 — validate the API-supplied upload_url host before using it for the
tests.zip upload; warn (do NOT cert-pin) when an HTTP(S) proxy routes all API
traffic incl. credentials; structural-only JWT check on the TestHub token
(defence-in-depth — the CLI has no key to verify the signature).

APS-19008 (browserstack.json half) — read browserstack.json via
JSON.parse(fs.readFileSync) instead of require() so a .js config cannot execute
arbitrary code; require a .json extension and project-root path containment.

New bin/helpers/securityValidation.js (stdlib-only): isAllowedBrowserstackUrl,
isPathInsideBase, isWellFormedJwt, covered by test/unit/.../securityValidation.js
(13 tests, both paths).

Deliberately NOT changed (accepted-risk / opt-in — needs product decision):
- cypress.config.js is legitimately JS that imports plugins; NOT sandboxed by
  default (a vm sandbox breaks real configs). APS-19008 cypress-config half.
- npm_dependencies install still runs lifecycle scripts (APS-19009): NOT fixed
  here. Note: PR #1128's repo .npmrc (ignore-scripts=true) does NOT protect end
  users — packageInstaller copies the *user's* .npmrc into the temp install dir,
  not the CLI's. APS-19009's real fix (--ignore-scripts + package-name/version
  validation + shell:false) remains OPEN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Rohannagariya1 Rohannagariya1 requested a review from a team as a code owner July 2, 2026 12:32
if (typeof candidatePath !== 'string' || candidatePath === '') {
return false;
}
const base = path.resolve(baseDir || process.cwd());
return false;
}
const base = path.resolve(baseDir || process.cwd());
const resolved = path.resolve(base, candidatePath);
return false;
}
const base = path.resolve(baseDir || process.cwd());
const resolved = path.resolve(base, candidatePath);
Comment thread bin/helpers/utils.js
// PR-supplied .js config would run arbitrary code, APS-19008). Also require
// a .json extension and that the file resolves inside the project root so a
// crafted --config-file cannot point outside the project or at a script.
const resolvedPath = path.resolve(bsConfigPath);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants