Skip to content

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

Merged
clydin merged 1 commit intoangular:mainfrom
simpleclub-extended:feat/sub-resource-integrity-support-for-dynamic-chunks
May 5, 2026
Merged

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

Conversation

@IchordeDionysos
Copy link
Copy Markdown
Contributor

@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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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
Contributor 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 🤔

@IchordeDionysos IchordeDionysos force-pushed the feat/sub-resource-integrity-support-for-dynamic-chunks branch from f6e5a94 to bc88ad9 Compare May 2, 2026 16:15
@clydin
Copy link
Copy Markdown
Member

clydin commented May 4, 2026

This overall looks good.
Can you address the 3 lint issues? Since they are in a test, you can add disable comments if preferred.

…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

fix: Lint failures in tests
@IchordeDionysos IchordeDionysos force-pushed the feat/sub-resource-integrity-support-for-dynamic-chunks branch from 7cb9c43 to d9b6d29 Compare May 5, 2026 18:01
@clydin clydin added action: merge The PR is ready for merge by the caretaker target: minor This PR is targeted for the next minor release labels May 5, 2026
@clydin clydin merged commit 58c7c7a into angular:main May 5, 2026
36 checks passed
@clydin
Copy link
Copy Markdown
Member

clydin commented May 5, 2026

This PR was merged into the repository. The changes were merged into the following branches:

@IchordeDionysos
Copy link
Copy Markdown
Contributor Author

IchordeDionysos commented May 5, 2026

@clydin Could I ask you to also take a look at this issue: #33108
And comment whether this is something you'd be happy to integrate a fix directly into the Angular CLI or whether it should be added in the downstream dependencies 😌

This would finally make it possible for us to use the native SRI flag.

@IchordeDionysos IchordeDionysos deleted the feat/sub-resource-integrity-support-for-dynamic-chunks branch May 5, 2026 23:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: @angular/build detected: feature PR contains a feature commit target: minor This PR is targeted for the next minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No subresource integrity validation for dynamically loaded chunks

2 participants