Skip to content

Commit

Permalink
Merge pull request #136 from kiwiz/main
Browse files Browse the repository at this point in the history
Add RequireSandboxOnIFrame
  • Loading branch information
buro9 committed Dec 31, 2021
2 parents c788a2a + 5638f90 commit ce0adc5
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
6 changes: 6 additions & 0 deletions helpers.go
Expand Up @@ -295,3 +295,9 @@ func (p *Policy) AllowTables() {
CellVerticalAlign,
).OnElements("tbody", "tfoot")
}

func (p *Policy) AllowIFrames(vals ...SandboxValue) {
p.AllowAttrs("sandbox").OnElements("iframe")

p.RequireSandboxOnIFrame(vals...)
}
74 changes: 74 additions & 0 deletions policy.go
Expand Up @@ -74,6 +74,9 @@ type Policy struct {
// When true, add crossorigin="anonymous" to HTML audio, img, link, script, and video tags
requireCrossOriginAnonymous bool

// When true, add and filter sandbox attribute on iframe tags
requireSandboxOnIFrame map[string]bool

// When true add target="_blank" to fully qualified links
// Will add for href="http://foo"
// Will skip for href="/foo" or href="foo"
Expand Down Expand Up @@ -189,6 +192,25 @@ type stylePolicyBuilder struct {

type urlPolicy func(url *url.URL) (allowUrl bool)

type SandboxValue int64

const (
SandboxAllowDownloads SandboxValue = iota
SandboxAllowDownloadsWithoutUserActivation
SandboxAllowForms
SandboxAllowModals
SandboxAllowOrientationLock
SandboxAllowPointerLock
SandboxAllowPopups
SandboxAllowPopupsToEscapeSandbox
SandboxAllowPresentation
SandboxAllowSameOrigin
SandboxAllowScripts
SandboxAllowStorageAccessByUserActivation
SandboxAllowTopNavigation
SandboxAllowTopNavigationByUserActivation
)

// init initializes the maps if this has not been done already
func (p *Policy) init() {
if !p.initialized {
Expand Down Expand Up @@ -680,6 +702,58 @@ func (p *Policy) AllowURLSchemeWithCustomPolicy(
return p
}

// RequireSandboxOnIFrame will result in all iframe tags having a sandbox="" tag
// Any sandbox values not specified here will be filtered from the generated HTML
func (p *Policy) RequireSandboxOnIFrame(vals ...SandboxValue) {
p.requireSandboxOnIFrame = make(map[string]bool)

for val := range vals {
switch SandboxValue(val) {
case SandboxAllowDownloads:
p.requireSandboxOnIFrame["allow-downloads"] = true

case SandboxAllowDownloadsWithoutUserActivation:
p.requireSandboxOnIFrame["allow-downloads-without-user-activation"] = true

case SandboxAllowForms:
p.requireSandboxOnIFrame["allow-forms"] = true

case SandboxAllowModals:
p.requireSandboxOnIFrame["allow-modals"] = true

case SandboxAllowOrientationLock:
p.requireSandboxOnIFrame["allow-orientation-lock"] = true

case SandboxAllowPointerLock:
p.requireSandboxOnIFrame["allow-pointer-lock"] = true

case SandboxAllowPopups:
p.requireSandboxOnIFrame["allow-popups"] = true

case SandboxAllowPopupsToEscapeSandbox:
p.requireSandboxOnIFrame["allow-popups-to-escape-sandbox"] = true

case SandboxAllowPresentation:
p.requireSandboxOnIFrame["allow-presentation"] = true

case SandboxAllowSameOrigin:
p.requireSandboxOnIFrame["allow-same-origin"] = true

case SandboxAllowScripts:
p.requireSandboxOnIFrame["allow-scripts"] = true

case SandboxAllowStorageAccessByUserActivation:
p.requireSandboxOnIFrame["allow-storage-access-by-user-activation"] = true

case SandboxAllowTopNavigation:
p.requireSandboxOnIFrame["allow-top-navigation"] = true

case SandboxAllowTopNavigationByUserActivation:
p.requireSandboxOnIFrame["allow-top-navigation-by-user-activation"] = true
}
}
}

// AddSpaceWhenStrippingTag states whether to add a single space " " when
// removing tags that are not allowed by the policy.
//
Expand Down
29 changes: 28 additions & 1 deletion sanitize.go
Expand Up @@ -240,7 +240,7 @@ func (p *Policy) sanitize(r io.Reader, w io.Writer) error {
// rather than:
// p := bluemonday.NewPolicy()
// If this is the case, and if they haven't yet triggered an action that
// would initiliaze the maps, then we need to do that.
// would initialize the maps, then we need to do that.
p.init()

buff, ok := w.(stringWriterWriter)
Expand Down Expand Up @@ -809,6 +809,33 @@ attrsLoop:
}
}

if p.requireSandboxOnIFrame != nil && elementName == "iframe" {
var sandboxFound bool
for i, htmlAttr := range cleanAttrs {
if htmlAttr.Key == "sandbox" {
sandboxFound = true
var cleanVals []string
cleanValsSet := make(map[string]bool)
for _, val := range strings.Fields(htmlAttr.Val) {
if p.requireSandboxOnIFrame[val] {
if !cleanValsSet[val] {
cleanVals = append(cleanVals, val)
cleanValsSet[val] = true
}
}
}
cleanAttrs[i].Val = strings.Join(cleanVals, " ")
}
}

if !sandboxFound {
sandbox := html.Attribute{}
sandbox.Key = "sandbox"
sandbox.Val = ""
cleanAttrs = append(cleanAttrs, sandbox)
}
}

return cleanAttrs
}

Expand Down
18 changes: 18 additions & 0 deletions sanitize_test.go
Expand Up @@ -1871,6 +1871,24 @@ func TestIssue107(t *testing.T) {
wg.Wait()
}

func TestIFrameSandbox(t *testing.T) {
p := NewPolicy()
p.AllowAttrs("sandbox").OnElements("iframe")
p.RequireSandboxOnIFrame(SandboxAllowDownloads)

in := `<iframe src="http://example.com" sandbox="allow-forms allow-downloads allow-downloads"></iframe>`
expected := `<iframe sandbox="allow-downloads"></iframe>`
out := p.Sanitize(in)
if out != expected {
t.Errorf(
"test failed;\ninput : %s\noutput : %s\nexpected: %s",
in,
out,
expected,
)
}
}

func TestSanitizedURL(t *testing.T) {
tests := []test{
{
Expand Down

0 comments on commit ce0adc5

Please sign in to comment.