PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

PER-9666: avoid require binding name that breaks the packaged binary - #2306

Closed
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash
Closed

PER-9666: avoid require binding name that breaks the packaged binary#2306
rishigupta1599 wants to merge 4 commits into
masterfrom
fix/cli-command-createRequire-binary-crash

Conversation

@rishigupta1599

Copy link
Copy Markdown
Contributor

Problem

The packaged pkg binary crashes on startup (./percy --version):

TypeError: _require is not a function
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/lockfileDiff.js:35:43)
...
at Object.<anonymous> (/snapshot/cli/packages/cli-command/dist/smartsnap.js:26:21)

Root cause

lockfileDiff.js and smartsnap.js (both new this release) bind:

constrequire=createRequire(import.meta.url);

When these ESM files are transpiled to CommonJS for the binary, two Babel transforms collide:

  1. @babel/preset-env renames the local require_require to avoid shadowing CJS's built-in require.
  2. babel-plugin-transform-import-meta expands import.meta.url into a require('url').pathToFileURL(__filename)... call — and that emitted require gets renamed to _require too, because the scope now has a _require binding.

The transpiled line becomes:

var_require=(0,_module.createRequire)(_require('url').pathToFileURL(__filename).toString());

_require('url') runs inside _require's own initializer, while _require is still undefinedTypeError: _require is not a function.

(No other file hits this — core/src/api.js uses createRequire(url) inline and never binds it to a name called require.)

Fix

Rename the binding to cjsRequire in both files (plus usages). The import-meta-emitted require('url') then stays the real CJS require, and the transpiled output is correct:

varcjsRequire=(0,_module.createRequire)(require('url').pathToFileURL(__filename).toString());

Pure rename — no behavior change.

Verification

  • Reproduced the exact failing line via the project's BABEL_ENV=dev CJS transpile, then confirmed the fixed output executes past the createRequire line.
  • All 143 cli-command unit specs pass.
  • ESLint clean on both files.

🤖 Generated with Claude Code

…ed binary
lockfileDiff.js and smartsnap.js bound `const require = createRequire(import.meta.url)`.
When transpiled to CommonJS for the pkg binary, two Babel transforms collide:
preset-env renames the local `require` to `_require` to avoid shadowing CJS's
built-in require, and transform-import-meta expands `import.meta.url` into a
`require('url')...` call whose `require` is then ALSO renamed to `_require`.
The result is `_require(...)` inside its own initializer, so the binary crashes
on load with `TypeError: _require is not a function`.
Rename the binding to `cjsRequire` so the import-meta-emitted `require('url')`
stays the real CJS require. Pure rename; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:7be2263Reviewers: stack:code-reviewer

Summary

Renames the const require = createRequire(import.meta.url) binding to cjsRequire in lockfileDiff.js and smartsnap.js to avoid a Babel name collision (preset-env + transform-import-meta) that crashed the packaged CJS binary with TypeError: _require is not a function. Rename-only; no logic changes.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANot applicable — module-level require rename.
HighSecurityInput validation and sanitizationN/ANo new external input handled.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access logic.
HighSecurityNo SQL injection (parameterized queries)N/ANo DB access.
HighCorrectnessLogic is correct, handles edge casesPassConfirmed correct in build output (var cjsRequire = ...); no collision.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassExisting try/catch in loadSnyk untouched.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency introduced.
MediumTestingNew code has corresponding testsN/ABug only manifests in transpiled binary; covered by separate executable CI job.
MediumTestingError paths and edge cases testedN/ANo behavior change.
MediumTestingExisting tests still pass (no regressions)PassRename-only; ESM source semantics unchanged.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/ANo data fetching.
MediumPerformanceLong-running tasks use background jobsN/ANot applicable.
MediumQualityFollows existing codebase patternsPassConsistent rename across both files.
MediumQualityChanges are focused (single concern)PassSingle concern: fix binary crash.
LowQualityMeaningful names, no dead codePasscjsRequire is descriptive.
LowQualityComments explain why, not whatPassDetailed rationale comments added; one vague cross-reference (see Findings).
LowQualityNo unnecessary dependencies addedPassNo dependency changes.

Findings

  • File:packages/cli-command/src/lockfileDiff.js:14

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: Comment cross-reference See PER smartsnap binary. lacks a ticket/PR ID, so it will lose meaning over time.

  • Suggestion: Replace with See percy/cli#2306. or the relevant Jira ticket ID.

  • File:packages/core/src/api.js:23 (out of scope — pre-existing on master)

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue: Uses createRequire inline; a separate self-referential __filename pattern exists in its build output. Not introduced by this PR.

  • Suggestion: Follow-up audit of other files combining a require binding with import.meta.url usage; out of scope here.


Verdict: PASS

@RaghavsBrowserStackRaghavsBrowserStack changed the title fix(cli-command): avoid require binding name that breaks the packaged binaryPER-9666: avoid require binding name that breaks the packaged binaryJun 19, 2026
@Shivanshu-07

Copy link
Copy Markdown
Contributor

Add UTs - Rest LGTM

Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
Comment threadpackages/cli-command/test/noRequireBinding.test.js Fixed
@RaghavsBrowserStack

Copy link
Copy Markdown
Contributor

Claude Code PR Review

PR:#2306Head:1bc95eaReviewers: stack:code-reviewer

Summary

Fixes a pkg-packaged binary startup crash: ESM files binding createRequire(import.meta.url) to a name called require collide with Babel's preset-env + transform-import-meta (both rename to _require), yielding TypeError: _require is not a function. The fix renames the binding to cjsRequire in lockfileDiff.js and smartsnap.js, adds a static-scan regression test, and suppresses the resulting semgrep path-traversal false-positive.

Review Table

PriorityCategoryCheckStatusNotes
HighSecurityNo hardcoded secrets or credentialsPassNo secrets introduced.
HighSecurityAuthentication/authorization checks presentN/ANo auth surface in this change.
HighSecurityInput validation and sanitizationPassTest paths derive from process.cwd() + repo's own packages/; no external input.
HighSecurityNo IDOR — resource ownership validatedN/ANo resource access.
HighSecurityNo SQL injection (parameterized queries)N/ANo SQL.
HighCorrectnessLogic is correct, handles edge casesPassRename is the correct mechanical fix; verified against compiled dist/ output.
HighCorrectnessError handling is explicit, no swallowed exceptionsPassloadSnyk() still surfaces the underlying require failure; unchanged.
HighCorrectnessNo race conditions or concurrency issuesN/ANo concurrency.
MediumTestingNew code has corresponding testsPassNew noRequireBinding.test.js static-scan guard added.
MediumTestingError paths and edge cases testedPassTest guards against an empty walk (files.length > 20).
MediumTestingExisting tests still pass (no regressions)PassPure rename of a local binding; call sites updated in lockstep.
MediumPerformanceNo N+1 queries or unbounded data fetchingN/AN/A.
MediumPerformanceLong-running tasks use background jobsN/AN/A.
MediumQualityFollows existing codebase patternsPassMatches existing createRequire interop idiom.
MediumQualityChanges are focused (single concern)PassScoped to the binary-crash fix + guard.
LowQualityMeaningful names, no dead codePasscjsRequire is clearer; minor dead-code note on SKIP_DIRS 'test' entry.
LowQualityComments explain why, not whatFailTest comment claims regex \s "spans newlines" but the scan is line-by-line — misleading.
LowQualityNo unnecessary dependencies addedPassNo new deps; test uses only fs/path.

Findings

  • File:packages/cli-command/test/noRequireBinding.test.js:67

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue: The comment states the regex \s "spans newlines, so a wrapped declaration is caught," but the scan reads the file as split('\n') and tests each line individually. A declaration split across two lines would not be caught. Documentation inconsistency, not a coverage gap today (no split declarations exist), but it creates false confidence.

  • Suggestion: Either scan the full file content with a multiline regex, or correct the comment to state the scan is line-by-line and does not catch multi-line declarations.

  • File:packages/cli-command/test/noRequireBinding.test.js:85

  • Severity: Low

  • Reviewer: stack:code-reviewer

  • Issue:FORBIDDEN has no concept of JS comments, so a commented-out // const require = createRequire(...) line in any src/ file would be flagged as a violation. No such lines exist today, but it is a future-robustness concern.

  • Suggestion: Strip // comments before testing — e.g. FORBIDDEN.test(line.replace(/\/\/.*$/, '')) — or note the limitation in the comment.

  • File:packages/cli-command/test/noRequireBinding.test.js:42

  • Severity: Low (informational)

  • Reviewer: stack:code-reviewer

  • Issue:SKIP_DIRS includes test, but the walk only enters packages/<pkg>/src, so the test entry is effectively dead. Harmless and slightly misleading.

  • Suggestion: No action needed; would only matter if the walk entry-point moves up to packages/<pkg>.


Verdict: PASS

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for more than 14 days with no activity. Remove stale label or comment or this will be closed in 14 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was closed because it has been stalled for 28 days with no activity.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍞 staleClosed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@rishigupta1599@RaghavsBrowserStack@Shivanshu-07@github-advanced-security@this-is-shivamsingh