Skip to content

Conversation

@vykimnguyen
Copy link
Contributor

Motivation

Remove ts-expect-errors in hmr-runtime.ts

Changes

This is all rovodev's work with Marcin's prompt

Checklist

  • Existing or new tests cover this change
  • There is a changeset for this change, or one is not required
  • Added documentation for any new features to the docs/ folder

@changeset-bot
Copy link

changeset-bot bot commented Sep 17, 2025

🦋 Changeset detected

Latest commit: 16f549e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@atlaspack/runtime-browser-hmr Patch
@atlaspack/config-default Patch
@atlaspack/config-repl Patch
atlaspack Patch
@atlaspack/config-webextension Patch
@atlaspack/cli Patch
@atlaspack/register Patch
@atlaspack/test-utils Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

📊 Type Coverage Report

Coverage Comparison

Metric Baseline Current Change
Coverage Percentage 90.78% 90.78% ➡️ 0.00%
Correctly Typed 175,308 175,308 +0
Total Expressions 193,102 193,102 +0
Untyped Expressions 17,794 17,794 +0

| Status | ✅ Success |

Files with Most Type Issues (Top 15)

File Issues Affected Lines
packages/core/integration-tests/test/javascript.ts 983 648
packages/core/integration-tests/test/cache.ts 885 626
packages/core/integration-tests/test/scope-hoisting.ts 623 490
packages/utils/node-resolver-core/test/resolver.ts 476 177
packages/core/integration-tests/test/html.ts 468 294
packages/core/test-utils/src/utils.ts 330 205
packages/core/integration-tests/test/sourcemaps.ts 326 167
packages/core/integration-tests/test/incremental-bundling.ts 298 206
packages/bundlers/default/src/idealGraph.ts 255 153
packages/core/core/src/dumpGraphToGraphViz.ts 251 108
packages/core/integration-tests/test/output-formats.ts 227 161
packages/transformers/webextension/src/WebExtensionTransformer.ts 210 80
packages/core/integration-tests/test/react-refresh.ts 197 71
packages/core/integration-tests/test/css-modules.ts 191 107
packages/core/core/src/requests/TargetRequest.ts 190 133

This report was generated by the Type Coverage GitHub Action

} catch (err: any) {
if (err.message) {
} catch (err: unknown) {
if (err instanceof Error && err.message) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty minor, but best to avoid changing implementation where possible. If we change impl, it needs to be feature flagged and becomes a whole thing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't entirely disagree, but I think this is the standard pattern in typescript for dealing with errors. I don't think catch (err: Error) is allowed.

// support for source maps is better with eval.
(0, eval)(asset.output);
if (asset.output) {
(0, eval)(asset.output);

Check failure

Code scanning / CodeQL

Code injection Critical

This code execution depends on a
user-provided value
.

Copilot Autofix

AI 2 months ago

To fix this issue, we should avoid blindly evaluating arbitrary code received from a network source (even in a local development context). The safest mitigation is to eliminate or strictly limit dynamic code execution via eval. For HMR systems, one standard solution is to validate both the type and contents of incoming assets, and to only allow code that matches expected patterns, or ideally, to load updated modules via <script> tags (allowing built-in CSP and browser script loading protections) rather than evaluating strings.

For the context here:

  • Instead of (0, eval)(asset.output);, inject a <script> tag with the code if it is JavaScript and is from a trusted source.
  • At minimum, verify that the WebSocket origin is trusted before processing updates.
  • If dynamic eval is absolutely required for HMR (which is commonly the case only in development), make sure to restrict communications to authenticated channels and/or validate the format of asset.output to only correspond to bundles you expect.
  • If using the <script> method:
    • Create a <script> element
    • Set text content to asset.output
    • Set nonce/integrity if appropriate
    • Add to document.head
    • Remove after execution

Necessary changes:

  • Replace the direct call to eval(asset.output) at line 579 in packages/runtimes/hmr/src/loaders/hmr-runtime.ts with a script injection pattern.
  • Add a helper function for safe script injection if necessary.
  • Ensure that the asset type is 'js' before any code-injection logic is performed (already checked, but can reinforce).
  • Document that this change is only a partial mitigation, and the overall HMR protocol should have authentication/integrity checks.
Suggested changeset 1
packages/runtimes/hmr/src/loaders/hmr-runtime.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/runtimes/hmr/src/loaders/hmr-runtime.ts b/packages/runtimes/hmr/src/loaders/hmr-runtime.ts
--- a/packages/runtimes/hmr/src/loaders/hmr-runtime.ts
+++ b/packages/runtimes/hmr/src/loaders/hmr-runtime.ts
@@ -576,7 +576,13 @@
         // Global eval. We would use `new Function` here but browser
         // support for source maps is better with eval.
         if (asset.output) {
-          (0, eval)(asset.output);
+          // safer: inject script tag rather than direct eval
+          const script = document.createElement('script');
+          script.textContent = asset.output;
+          script.type = 'text/javascript';
+          // Optionally: set nonce and/or other attributes as per CSP requirements
+          document.head.appendChild(script);
+          document.head.removeChild(script);
         }
       }
 
EOF
@@ -576,7 +576,13 @@
// Global eval. We would use `new Function` here but browser
// support for source maps is better with eval.
if (asset.output) {
(0, eval)(asset.output);
// safer: inject script tag rather than direct eval
const script = document.createElement('script');
script.textContent = asset.output;
script.type = 'text/javascript';
// Optionally: set nonce and/or other attributes as per CSP requirements
document.head.appendChild(script);
document.head.removeChild(script);
}
}

Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth checking if HMR still works (you can setup a simple standalone project and use atlaspack-link) as I believe this file ends up running in the browser - this would be why the original used comment based flow types. Or at least you'd need to make sure that the client side is "compiled" so that all of the type checks are removed.

} catch (err: any) {
if (err.message) {
} catch (err: unknown) {
if (err instanceof Error && err.message) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't entirely disagree, but I think this is the standard pattern in typescript for dealing with errors. I don't think catch (err: Error) is allowed.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants