Optional integrations build inline <script> elements, forcing script-src 'unsafe-inline'
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 48/100
- Issue type
- Feature
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- angular, typescript
Research direction
Start with the listed script creation sites in src/app/statistics/google-analytics.service.ts, src/app/core/shared/client-math.service.ts, src/app/core/google-recaptcha/google-recaptcha.service.ts, src/app/social/social.service.ts, and src/app/statistics/matomo.factory.ts; check how #6077 exposes CSP_NONCE. Remove inline script assignments and apply the configured nonce to dynamic scripts, then verify the acceptance criteria across all five integrations, including behavior without a nonce.
Written by the indexing model from the issue text.
Description
Is your feature request related to a problem? Please describe.
Two of the optional integrations construct <script> elements with inline content at runtime.
Any deployment that enables them therefore requires script-src 'unsafe-inline', which is the
single directive value that most undermines a Content-Security-Policy.
Google Analytics (src/app/statistics/google-analytics.service.ts:71-85) builds the config
snippet as inline script for both the GTag and the legacy variant:
const libScript = this.document.createElement('script');
libScript.innerHTML = `window.dataLayer = window.dataLayer || [];function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());gtag('config', '${trackingId}');`;
this.document.body.appendChild(libScript);
MathJax (src/app/core/shared/client-math.service.ts:83-86) does the same for its options
object:
const optionsScript: HTMLScriptElement = this._document.createElement('script');
optionsScript.type = 'text/javascript';
optionsScript.text = `MathJax = ${JSON.stringify(this.mathJaxOptions)};`;
this._document.head.appendChild(optionsScript);
In both cases the inline script does nothing that could not be done directly in TypeScript. It
assigns a global. Routing it through a <script> element buys nothing and costs the ability to
run under a meaningful CSP.
Separately, several services inject external scripts at runtime, which is fine in itself but
becomes relevant under a nonce-based policy that does not use 'strict-dynamic', since a
dynamically created script element carries no nonce:
| Service | Location |
|---|---|
| Google Analytics / GTag | src/app/statistics/google-analytics.service.ts:67 |
| Google reCAPTCHA | src/app/core/google-recaptcha/google-recaptcha.service.ts:182 |
| MathJax | src/app/core/shared/client-math.service.ts:88 |
| AddToAny social sharing | src/app/social/social.service.ts:82 |
| Matomo | src/app/statistics/matomo.factory.ts:19-24 |
Describe the solution you'd like
1. Remove the two inline script blocks. Assign the globals directly instead of via a script
element:
// MathJax: set the global before loading the library, no script element needed
(window as any).MathJax = this.mathJaxOptions;
// Google Analytics: same, define dataLayer and gtag in TypeScript
This alone removes the need for script-src 'unsafe-inline' on deployments that enable Analytics
or MathJax, and is worth doing independently of any nonce work.
2. Apply the nonce to dynamically created script elements. Once #6077 provides CSP_NONCE,
each of the services above should set script.nonce = inject(CSP_NONCE) on the element it creates.
This is a no-op when no nonce is configured, so it is backwards compatible, and it makes the
integrations work under an explicit-allowlist policy rather than depending on 'strict-dynamic'.
A small shared helper (for example createScriptElement(doc, src, nonce)) would avoid repeating
this in five places and would make it harder for a future integration to reintroduce the problem.
Acceptance criteria
- No
innerHTMLor.textassignment on a<script>element remains insrc/. - Every dynamically created
<script>element carries the nonce when one is configured,
verifiable in the DOM with Analytics, Matomo, MathJax, reCAPTCHA and AddToAny all enabled. - With those five enabled and a policy of
script-src 'nonce-X' 'strict-dynamic'(no
'unsafe-inline', no'unsafe-eval'), all five load and function with zero CSP violations. - Existing behaviour is unchanged when no nonce is configured.
Describe alternatives or workarounds you've considered
- Rely on
'strict-dynamic'. Because these scripts are created viacreateElementrather than
by the HTML parser,'strict-dynamic'would permit them as-is, including the inline ones. That
is the pragmatic first step proposed in #6077, but it means trusting anything application code
loads, so it should not be the permanent answer. - Allowlist the hosts in the policy. Works for the external scripts but not the inline ones, and
several of the hosts are runtime configuration rather than build-time constants (see phase 3 in
#6079).
Additional information
Umbrella: #6079. Depends on #6077 only for part 2; part 1 is independent and can be merged
first.
- Dominant language
- TypeScript
- Stars
- 180
- Forks
- 577
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 100
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from DSpace/dspace-angular
-
accessibility affects: 10.x affects: main bug ux
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
DSpace/dspace-angular#6531 · 1 comment ·
Maintainers usually reply within 1 day
-
accessibility affects: 10.x affects: main bug help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
DSpace/dspace-angular#6530 ·
Maintainers usually reply within 1 day
-
Submission Form Allows Deletion of All Repeating Item EntriesPossibly taken @Rowan-McKereghan claimed this 5 days ago. Openaffects: 8.x affects: 9.x bug component: submission
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
DSpace/dspace-angular#6511 · 2 comments · 1 assignee ·
Maintainers usually reply within 1 day
-
Visual issues with qualdrop fieldsPossibly taken @DanGastardelli claimed this 15 days ago. Openaffects: 10.x affects: 9.x bug claimed: Acervos Digitais component: submission Difficulty: Low ux
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
DSpace/dspace-angular#6412 · 3 comments · 1 assignee ·
Maintainers usually reply within 1 day
-
Facet headings have incorrect clickable area to expand/collapsePossibly taken @jesielviana claimed this 12 days ago. Openaccessibility affects: 10.x affects: 8.x affects: 9.x bug ux
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
DSpace/dspace-angular#6364 · 3 comments · 1 assignee ·
Maintainers usually reply within 1 day
All issues in DSpace/dspace-angular
Similar issues
-
bug parity
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
WenyuChiou/agent-board#1489 ·
Maintainers usually reply within 1 day
-
area: bazaar bug complexity: medium
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
accensa/x402-facilitator-stellar#779 ·
Maintainers usually reply within 1 day
-
bug good first issue P4 size:S web-ui
Difficulty 2/5 Half a day Newbie friendliness 85/100
josephfung/curia#2600 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
cloudflare/partykit#444 ·