Skip to content

Commit

Permalink
Merge branch 'main' into main-ic-fix-card-component-design
Browse files Browse the repository at this point in the history
  • Loading branch information
isabellechanclou committed May 12, 2023
2 parents fbd2d48 + 02ee250 commit f2cd85c
Show file tree
Hide file tree
Showing 31 changed files with 992 additions and 810 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
run: npm run js-test

- name: Run Coveralls
uses: coverallsapp/github-action@v2.1.0
uses: coverallsapp/github-action@v2.1.2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
path-to-lcov: "./js/coverage/lcov.info"
4 changes: 2 additions & 2 deletions js/src/scrollspy.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,11 @@ class ScrollSpy extends BaseComponent {
continue
}

const observableSection = SelectorEngine.findOne(anchor.hash, this._element)
const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)

// ensure that the observableSection exists & is visible
if (isVisible(observableSection)) {
this._targetLinks.set(anchor.hash, anchor)
this._targetLinks.set(decodeURI(anchor.hash), anchor)
this._observableSections.set(anchor.hash, observableSection)
}
}
Expand Down
78 changes: 36 additions & 42 deletions js/src/util/sanitizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,6 @@
* --------------------------------------------------------------------------
*/

const uriAttributes = new Set([
'background',
'cite',
'href',
'itemtype',
'longdesc',
'poster',
'src',
'xlink:href'
])

/**
* A pattern that recognizes a commonly useful subset of URLs that are safe.
*
* Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
*/
const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i

/**
* A pattern that matches safe data URLs. Only matches image, video and audio types.
*
* Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
*/
const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i

const allowedAttribute = (attribute, allowedAttributeList) => {
const attributeName = attribute.nodeName.toLowerCase()

if (allowedAttributeList.includes(attributeName)) {
if (uriAttributes.has(attributeName)) {
return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue))
}

return true
}

// Check if a regular expression validates the attribute.
return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
.some(regex => regex.test(attributeName))
}

// js-docs-start allow-list
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i

Expand Down Expand Up @@ -84,6 +43,42 @@ export const DefaultAllowlist = {
}
// js-docs-end allow-list

const uriAttributes = new Set([
'background',
'cite',
'href',
'itemtype',
'longdesc',
'poster',
'src',
'xlink:href'
])

/**
* A pattern that recognizes URLs that are safe wrt. XSS in URL navigation
* contexts.
*
* Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38
*/
// eslint-disable-next-line unicorn/better-regex
const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i

const allowedAttribute = (attribute, allowedAttributeList) => {
const attributeName = attribute.nodeName.toLowerCase()

if (allowedAttributeList.includes(attributeName)) {
if (uriAttributes.has(attributeName)) {
return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))
}

return true
}

// Check if a regular expression validates the attribute.
return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
.some(regex => regex.test(attributeName))
}

export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
if (!unsafeHtml.length) {
return unsafeHtml
Expand All @@ -102,7 +97,6 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {

if (!Object.keys(allowList).includes(elementName)) {
element.remove()

continue
}

Expand Down
34 changes: 34 additions & 0 deletions js/tests/unit/scrollspy.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -940,5 +940,39 @@ describe('ScrollSpy', () => {
}, 100)
link.click()
})

it('should smoothscroll to observable with anchor link that contains a french word as id', done => {
fixtureEl.innerHTML = [
'<nav id="navBar" class="navbar">',
' <ul class="nav">',
' <li class="nav-item"><a id="li-jsm-1" class="nav-link" href="#présentation">div 1</a></li>',
' </ul>',
'</nav>',
'<div class="content" data-bs-target="#navBar" style="overflow-y: auto">',
' <div id="présentation">div 1</div>',
'</div>'
].join('')

const div = fixtureEl.querySelector('.content')
const link = fixtureEl.querySelector('[href="#présentation"]')
const observable = fixtureEl.querySelector('#présentation')
const clickSpy = getElementScrollSpy(div)
// eslint-disable-next-line no-new
new ScrollSpy(div, {
offset: 1,
smoothScroll: true
})

setTimeout(() => {
if (div.scrollTo) {
expect(clickSpy).toHaveBeenCalledWith({ top: observable.offsetTop - div.offsetTop, behavior: 'smooth' })
} else {
expect(clickSpy).toHaveBeenCalledWith(observable.offsetTop - div.offsetTop)
}

done()
}, 100)
link.click()
})
})
})
78 changes: 68 additions & 10 deletions js/tests/unit/util/sanitizer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,75 @@ describe('Sanitizer', () => {
expect(result).toEqual(empty)
})

it('should sanitize template by removing tags with XSS', () => {
const template = [
'<div>',
' <a href="javascript:alert(7)">Click me</a>',
' <span>Some content</span>',
'</div>'
].join('')

const result = sanitizeHtml(template, DefaultAllowlist, null)
it('should retain tags with valid URLs', () => {
const validUrls = [
'',
'http://abc',
'HTTP://abc',
'https://abc',
'HTTPS://abc',
'ftp://abc',
'FTP://abc',
'mailto:me@example.com',
'MAILTO:me@example.com',
'tel:123-123-1234',
'TEL:123-123-1234',
'sip:me@example.com',
'SIP:me@example.com',
'#anchor',
'/page1.md',
'http://JavaScript/my.js',
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/', // Truncated.
'data:video/webm;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
'unknown-scheme:abc'
]

for (const url of validUrls) {
const template = [
'<div>',
` <a href="${url}">Click me</a>`,
' <span>Some content</span>',
'</div>'
].join('')

const result = sanitizeHtml(template, DefaultAllowlist, null)

expect(result).toContain(`href="${url}"`)
}
})

expect(result).not.toContain('href="javascript:alert(7)')
it('should sanitize template by removing tags with XSS', () => {
const invalidUrls = [
// eslint-disable-next-line no-script-url
'javascript:alert(7)',
// eslint-disable-next-line no-script-url
'javascript:evil()',
// eslint-disable-next-line no-script-url
'JavaScript:abc',
' javascript:abc',
' \n Java\n Script:abc',
'&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
'&#106&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
'&#106 &#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
'&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058',
'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A;',
'jav&#x09;ascript:alert();',
'jav\u0000ascript:alert();'
]

for (const url of invalidUrls) {
const template = [
'<div>',
` <a href="${url}">Click me</a>`,
' <span>Some content</span>',
'</div>'
].join('')

const result = sanitizeHtml(template, DefaultAllowlist, null)

expect(result).not.toContain(`href="${url}"`)
}
})

it('should sanitize template and work with multiple regex', () => {
Expand Down
9 changes: 9 additions & 0 deletions js/tests/visual/scrollspy.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<li><a class="dropdown-item" href="#one">One</a></li>
<li><a class="dropdown-item" href="#two">Two</a></li>
<li><a class="dropdown-item" href="#three">Three</a></li>
<li><a class="dropdown-item" href="#présentation">Présentation</a></li>
</ul>
</li>
<li class="nav-item">
Expand Down Expand Up @@ -82,6 +83,14 @@ <h2 id="three">three</h2>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<hr>
<h2 id="présentation">Présentation</h2>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<hr>
<h2 id="final">Final section</h2>
<p>Ad leggings keytar, brunch id art party dolor labore.</p>
</div>
Expand Down
Loading

0 comments on commit f2cd85c

Please sign in to comment.