fix: anchor tag safety (via #4789)
* v3.17.6 * release(3.17.6): rebuild dist * add failing tests * fix Link component * fix OnlineValidatorBadge component * switch from <a> to <Link> in operation components * make Markdown inputs safe * use Link component in Info block, for target safety * add eslint rule for unsafe `target` usage
This commit is contained in:
@@ -52,7 +52,7 @@ describe("Markdown component", function() {
|
||||
it("allows links", function() {
|
||||
const str = `[Link](https://example.com/)`
|
||||
const el = render(<Markdown source={str} />)
|
||||
expect(el.html()).toEqual(`<div class="markdown"><p><a target="_blank" href="https://example.com/">Link</a></p>\n</div>`)
|
||||
expect(el.html()).toEqual(`<div class="markdown"><p><a rel="noopener noreferrer" target="_blank" href="https://example.com/">Link</a></p>\n</div>`)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
108
test/xss/anchor-target-rel/info.js
Normal file
108
test/xss/anchor-target-rel/info.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/* eslint-env mocha */
|
||||
import React from "react"
|
||||
import expect from "expect"
|
||||
import { render } from "enzyme"
|
||||
import { fromJS } from "immutable"
|
||||
import Info, { InfoUrl } from "components/info"
|
||||
import { Link } from "components/layout-utils"
|
||||
import Markdown from "components/providers/markdown"
|
||||
|
||||
describe("<Info/> Anchor Target Safety", function(){
|
||||
const dummyComponent = () => null
|
||||
const components = {
|
||||
Markdown,
|
||||
InfoUrl,
|
||||
Link
|
||||
}
|
||||
const baseProps = {
|
||||
getComponent: c => components[c] || dummyComponent,
|
||||
host: "example.test",
|
||||
basePath: "/api",
|
||||
info: fromJS({
|
||||
title: "Hello World"
|
||||
})
|
||||
}
|
||||
|
||||
it("renders externalDocs links with safe `rel` attributes", function () {
|
||||
const props = {
|
||||
...baseProps,
|
||||
externalDocs: fromJS({
|
||||
url: "http://google.com/"
|
||||
})
|
||||
}
|
||||
let wrapper = render(<Info {...props} />)
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.html()).toEqual("http://google.com/")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
|
||||
it("renders Contact links with safe `rel` attributes", function () {
|
||||
const props = {
|
||||
...baseProps,
|
||||
info: fromJS({
|
||||
contact: {
|
||||
url: "http://google.com/",
|
||||
name: "My Site"
|
||||
}
|
||||
})
|
||||
}
|
||||
let wrapper = render(<Info {...props} />)
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://google.com/")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
|
||||
it("renders License links with safe `rel` attributes", function () {
|
||||
const props = {
|
||||
...baseProps,
|
||||
info: fromJS({
|
||||
license: {
|
||||
url: "http://mit.edu/"
|
||||
}
|
||||
})
|
||||
}
|
||||
let wrapper = render(<Info {...props} />)
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://mit.edu/")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
|
||||
it("renders termsOfService links with safe `rel` attributes", function () {
|
||||
const props = {
|
||||
...baseProps,
|
||||
info: fromJS({
|
||||
termsOfService: "http://smartbear.com/"
|
||||
})
|
||||
}
|
||||
let wrapper = render(<Info {...props} />)
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://smartbear.com/")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
|
||||
it("renders definition URL links with safe `rel` attributes", function () {
|
||||
const props = {
|
||||
...baseProps,
|
||||
url: "http://petstore.swagger.io/v2/petstore.json"
|
||||
}
|
||||
let wrapper = render(<Info {...props} />)
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://petstore.swagger.io/v2/petstore.json")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
})
|
||||
44
test/xss/anchor-target-rel/link.js
Normal file
44
test/xss/anchor-target-rel/link.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-env mocha */
|
||||
import React from "react"
|
||||
import expect from "expect"
|
||||
import { render } from "enzyme"
|
||||
import { fromJS } from "immutable"
|
||||
import { Link } from "components/layout-utils"
|
||||
|
||||
describe("<Link/> Anchor Target Safety", function () {
|
||||
const dummyComponent = () => null
|
||||
const components = {
|
||||
Link
|
||||
}
|
||||
const baseProps = {
|
||||
getComponent: c => components[c] || dummyComponent
|
||||
}
|
||||
|
||||
it("renders regular links with `noreferrer` and `noopener`", function () {
|
||||
const props = {
|
||||
...baseProps,
|
||||
href: "http://google.com/"
|
||||
}
|
||||
let wrapper = render(<Link {...props} />)
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://google.com/")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
|
||||
it("enforces `noreferrer` and `noopener` on target=_blank links", function () {
|
||||
const props = {
|
||||
...baseProps,
|
||||
href: "http://google.com/",
|
||||
target: "_blank"
|
||||
}
|
||||
let wrapper = render(<Link {...props} />)
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://google.com/")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
})
|
||||
66
test/xss/anchor-target-rel/markdown.js
Normal file
66
test/xss/anchor-target-rel/markdown.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/* eslint-env mocha */
|
||||
import React from "react"
|
||||
import expect from "expect"
|
||||
import { render } from "enzyme"
|
||||
import Markdown from "components/providers/markdown"
|
||||
import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markdown.js"
|
||||
|
||||
describe("Markdown Link Anchor Safety", function () {
|
||||
describe("Swagger 2.0", function () {
|
||||
it("sanitizes Markdown links", function () {
|
||||
const str = `Hello, [here](http://google.com/) is my link`
|
||||
const wrapper = render(<Markdown source={str} />)
|
||||
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://google.com/")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
|
||||
it("sanitizes raw HTML links", function () {
|
||||
const str = `Hello, <a href="http://google.com/">here</a> is my link`
|
||||
const wrapper = render(<Markdown source={str} />)
|
||||
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://google.com/")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
})
|
||||
|
||||
describe("OAS 3", function () {
|
||||
it("sanitizes Markdown links", function () {
|
||||
const str = `Hello, [here](http://google.com/) is my link`
|
||||
const wrapper = render(<OAS3Markdown source={str} />)
|
||||
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://google.com/")
|
||||
expect(anchor.attr("target")).toEqual("_blank")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
|
||||
it("sanitizes raw HTML links", function () {
|
||||
const str = `Hello, <a href="http://google.com/">here</a> is my link`
|
||||
const wrapper = render(<OAS3Markdown source={str} />)
|
||||
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
expect(anchor.attr("href")).toEqual("http://google.com/")
|
||||
expect(anchor.attr("rel") || "").toInclude("noopener")
|
||||
expect(anchor.attr("rel") || "").toInclude("noreferrer")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function withMarkdownWrapper(str, { isOAS3 = false } = {}) {
|
||||
if(isOAS3) {
|
||||
return `<div class="renderedMarkdown"><p>${str}</p></div>`
|
||||
}
|
||||
|
||||
return `<div class="markdown"><p>${str}</p>\n</div>`
|
||||
}
|
||||
32
test/xss/anchor-target-rel/online-validator-badge.js
Normal file
32
test/xss/anchor-target-rel/online-validator-badge.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/* eslint-env mocha */
|
||||
import React from "react"
|
||||
import expect, { createSpy } from "expect"
|
||||
import { mount } from "enzyme"
|
||||
import { fromJS, Map } from "immutable"
|
||||
import OnlineValidatorBadge from "components/online-validator-badge"
|
||||
|
||||
describe("<OnlineValidatorBadge/> Anchor Target Safety", function () {
|
||||
it("should render a validator link with safe `rel` attributes", function () {
|
||||
// When
|
||||
const props = {
|
||||
getConfigs: () => ({}),
|
||||
getComponent: () => null,
|
||||
specSelectors: {
|
||||
url: () => "swagger.json"
|
||||
}
|
||||
}
|
||||
const wrapper = mount(
|
||||
<OnlineValidatorBadge {...props} />
|
||||
)
|
||||
|
||||
const anchor = wrapper.find("a")
|
||||
|
||||
// Then
|
||||
expect(anchor.props().href).toEqual(
|
||||
"https://online.swagger.io/validator/debug?url=swagger.json"
|
||||
)
|
||||
expect(anchor.props().target).toEqual("_blank")
|
||||
expect(anchor.props().rel || "").toInclude("noopener")
|
||||
expect(anchor.props().rel || "").toInclude("noreferrer")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user