Skip to content

feat(@angular/build): subresource integrity validation for dynamically loaded modules#33109

Open
IchordeDionysos wants to merge 1 commit intoangular:mainfrom
simpleclub-extended:feat/sub-resource-integrity-support-for-dynamic-chunks
Open

feat(@angular/build): subresource integrity validation for dynamically loaded modules#33109
IchordeDionysos wants to merge 1 commit intoangular:mainfrom
simpleclub-extended:feat/sub-resource-integrity-support-for-dynamic-chunks

Conversation

@IchordeDionysos
Copy link
Copy Markdown

@IchordeDionysos IchordeDionysos commented Apr 30, 2026

Adds support for verifying the integrity of dynamically loaded sub-resources by generating and pre-pending an import map with integrity hashes in the index.html

Closes #30724

PR Checklist

Please check to confirm your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Currently, no integrity checks are done for dynamically loaded files, such as chunks.

Issue Number: #30724

What is the new behavior?

The integrity is validated by adding an importmap into as the first script of the index.html head with the integrity hashes of the dynamically loaded modules.

This will allow the browser to validate the integrity of dynamic imports.

The importmap will be added when Angular build is run with the --subresource-integrity flag.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

This PR is part of a two fold effort to enable SRI for our use of Angular.

While this can be shipped on it's own, there is an additional issue blocking us from using SRI, due to Sentry relying on Debug IDs but Angular not outputting them automatically: #33108

I would appreciate a feedback on the following PR implementing Debug IDs in Angular sourcemaps: #33110

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces Subresource Integrity (SRI) support for lazy-loaded JavaScript chunks by generating an integrity-only import map within the index HTML. The changes include calculating SHA-384 hashes for browser output files and injecting them into a <script type="importmap"> block. Review feedback suggests refining the integrity map keys by removing hardcoded leading slashes to improve compatibility with various deployment configurations and filtering out initial bundles to avoid redundant metadata. Additionally, updates to the documentation and test suite were recommended to align with these refinements.

Comment thread packages/angular/build/src/tools/esbuild/index-html-generator.ts
Comment thread packages/angular/build/src/utils/index-file/augment-index-html.ts Outdated
@IchordeDionysos
Copy link
Copy Markdown
Author

  • Docs have been added / updated (for bug fixes / features)

I checked if there are any meaning docs regarding SRI but haven't found anything ...
The only mention was here: https://angular.dev/cli/build

Though I don't see a reason to update the docs, as there was no previous documentation of any limitations of SRI and the support now is just more complete.

@clydin
Copy link
Copy Markdown
Member

clydin commented Apr 30, 2026

Thank you for the contribution.
The concept appears sound and something we can include in the build process.

It appears that there are several lint and test failures. Can you fix those? And then the team can review.

@IchordeDionysos
Copy link
Copy Markdown
Author

@clydin ahh yes sorry somehow slipped through been dangling multiple PRs regarding this topic.

This should now be fixed. It's passing on my local machine

@IchordeDionysos IchordeDionysos force-pushed the feat/sub-resource-integrity-support-for-dynamic-chunks branch from 816b1a8 to 9f979e9 Compare May 1, 2026 07:40
@IchordeDionysos
Copy link
Copy Markdown
Author

@clydin just squashed the commits :)

Copy link
Copy Markdown
Member

@clydin clydin left a comment

Choose a reason for hiding this comment

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

Looks like several files are out of format as well.

Thank you for the contribution.

Comment on lines +119 to +124
const integrity = importmap.integrity['/' + file] ?? importmap.integrity[file];
if (integrity !== undefined) {
expect(integrity)
.withContext('integrity entry for ' + file)
.toBe(expectedSri);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you rework the logic for this part of the test? An empty import map will pass here which from the comment does not appear to be the intention.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Improved the tests here.

integrity[generateUrl(url, deployUrl)] = integrityHash;
}
headerLinkTags.push(
`<script type="importmap">${JSON.stringify({ integrity })}</script>`,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unlikely to occur but can you escape the < character (\\u003c) from the stringified JSON?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Escaped it!


// Emit an integrity-only import map so the browser can validate lazy chunks
// resolved via dynamic `import()` (which otherwise carry no SRI metadata).
// The block is placed first inside `<head>` so it precedes any module
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

headerLinkTags will actually be at the bottom of the <head> since they are appended during the end tag event. Script tags are inserted into the <body> so still after the import map though.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hmm good call out!
It was working for me when I tested it, but still wondering if we should be adjusting the code to append it right after the <base> tag, so add it here and here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Unless the rewriter would always fire the base case startTag event when because the base tag is added when missing 🤔

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@clydin I've implemented this now 😌

@IchordeDionysos IchordeDionysos force-pushed the feat/sub-resource-integrity-support-for-dynamic-chunks branch 2 times, most recently from 7e19b0d to f6e5a94 Compare May 2, 2026 15:47
Copy link
Copy Markdown
Author

@IchordeDionysos IchordeDionysos left a comment

Choose a reason for hiding this comment

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

Fixed the formatting issues


// Emit an integrity-only import map so the browser can validate lazy chunks
// resolved via dynamic `import()` (which otherwise carry no SRI metadata).
// The block is placed first inside `<head>` so it precedes any module
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hmm good call out!
It was working for me when I tested it, but still wondering if we should be adjusting the code to append it right after the <base> tag, so add it here and here?

integrity[generateUrl(url, deployUrl)] = integrityHash;
}
headerLinkTags.push(
`<script type="importmap">${JSON.stringify({ integrity })}</script>`,
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Escaped it!


// Emit an integrity-only import map so the browser can validate lazy chunks
// resolved via dynamic `import()` (which otherwise carry no SRI metadata).
// The block is placed first inside `<head>` so it precedes any module
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Unless the rewriter would always fire the base case startTag event when because the base tag is added when missing 🤔

…y loaded modules

Adds support for verifying the integrity of dynamically loaded sub-resources by generating and pre-pending an import map with integrity hashes in the index.html

Closes angular#30724

Apply suggestions from code review

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
style: Fix lint issues

test: Fix missing lazy modules resulting in test failures

test: Adjust test to validate more specific assumptions

fix: Escape < in generated importmap JSON

style: fix all formatting issues in angular-build

refactor: Inject importmap after base tag
@IchordeDionysos IchordeDionysos force-pushed the feat/sub-resource-integrity-support-for-dynamic-chunks branch from f6e5a94 to bc88ad9 Compare May 2, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: @angular/build detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No subresource integrity validation for dynamically loaded chunks

2 participants