Skip to content

Commit 1eebb57

Browse files
committed
Add RequireSandboxOnIFrame
1 parent c788a2a commit 1eebb57

File tree

3 files changed

+114
-1
lines changed

3 files changed

+114
-1
lines changed

policy.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ type Policy struct {
7474
// When true, add crossorigin="anonymous" to HTML audio, img, link, script, and video tags
7575
requireCrossOriginAnonymous bool
7676

77+
// When true, add and filter sandbox attribute on iframe tags
78+
requireSandboxOnIFrame map[string]bool
79+
7780
// When true add target="_blank" to fully qualified links
7881
// Will add for href="http://foo"
7982
// Will skip for href="/foo" or href="foo"
@@ -189,6 +192,25 @@ type stylePolicyBuilder struct {
189192

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

195+
type SandboxValue int64
196+
197+
const (
198+
SandboxAllowDownloads SandboxValue = iota
199+
SandboxAllowDownloadsWithoutUserActivation
200+
SandboxAllowForms
201+
SandboxAllowModals
202+
SandboxAllowOrientationLock
203+
SandboxAllowPointerLock
204+
SandboxAllowPopups
205+
SandboxAllowPopupsToEscapeSandbox
206+
SandboxAllowPresentation
207+
SandboxAllowSameOrigin
208+
SandboxAllowScripts
209+
SandboxAllowStorageAccessByUserActivation
210+
SandboxAllowTopNavigation
211+
SandboxAllowTopNavigationByUserActivation
212+
)
213+
192214
// init initializes the maps if this has not been done already
193215
func (p *Policy) init() {
194216
if !p.initialized {
@@ -680,6 +702,56 @@ func (p *Policy) AllowURLSchemeWithCustomPolicy(
680702
return p
681703
}
682704

705+
func (p *Policy) RequireSandboxOnIFrame(vals ...SandboxValue) {
706+
p.requireSandboxOnIFrame = make(map[string]bool)
707+
708+
for val := range vals {
709+
switch SandboxValue(val) {
710+
case SandboxAllowDownloads:
711+
p.requireSandboxOnIFrame["allow-downloads"] = true
712+
713+
case SandboxAllowDownloadsWithoutUserActivation:
714+
p.requireSandboxOnIFrame["allow-downloads-without-user-activation"] = true
715+
716+
case SandboxAllowForms:
717+
p.requireSandboxOnIFrame["allow-forms"] = true
718+
719+
case SandboxAllowModals:
720+
p.requireSandboxOnIFrame["allow-modals"] = true
721+
722+
case SandboxAllowOrientationLock:
723+
p.requireSandboxOnIFrame["allow-orientation-lock"] = true
724+
725+
case SandboxAllowPointerLock:
726+
p.requireSandboxOnIFrame["allow-pointer-lock"] = true
727+
728+
case SandboxAllowPopups:
729+
p.requireSandboxOnIFrame["allow-popups"] = true
730+
731+
case SandboxAllowPopupsToEscapeSandbox:
732+
p.requireSandboxOnIFrame["allow-popups-to-escape-sandbox"] = true
733+
734+
case SandboxAllowPresentation:
735+
p.requireSandboxOnIFrame["allow-presentation"] = true
736+
737+
case SandboxAllowSameOrigin:
738+
p.requireSandboxOnIFrame["allow-same-origin"] = true
739+
740+
case SandboxAllowScripts:
741+
p.requireSandboxOnIFrame["allow-scripts"] = true
742+
743+
case SandboxAllowStorageAccessByUserActivation:
744+
p.requireSandboxOnIFrame["allow-storage-access-by-user-activation"] = true
745+
746+
case SandboxAllowTopNavigation:
747+
p.requireSandboxOnIFrame["allow-top-navigation"] = true
748+
749+
case SandboxAllowTopNavigationByUserActivation:
750+
p.requireSandboxOnIFrame["allow-top-navigation-by-user-activation"] = true
751+
}
752+
}
753+
}
754+
683755
// AddSpaceWhenStrippingTag states whether to add a single space " " when
684756
// removing tags that are not allowed by the policy.
685757
//

sanitize.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func (p *Policy) sanitize(r io.Reader, w io.Writer) error {
240240
// rather than:
241241
// p := bluemonday.NewPolicy()
242242
// If this is the case, and if they haven't yet triggered an action that
243-
// would initiliaze the maps, then we need to do that.
243+
// would initialize the maps, then we need to do that.
244244
p.init()
245245

246246
buff, ok := w.(stringWriterWriter)
@@ -809,6 +809,29 @@ attrsLoop:
809809
}
810810
}
811811

812+
if p.requireSandboxOnIFrame != nil && elementName == "iframe" {
813+
var sandboxFound bool
814+
for i, htmlAttr := range cleanAttrs {
815+
if htmlAttr.Key == "sandbox" {
816+
sandboxFound = true
817+
var cleanVals []string
818+
for _, val := range strings.Fields(htmlAttr.Val) {
819+
if p.requireSandboxOnIFrame[val] {
820+
cleanVals = append(cleanVals, val)
821+
}
822+
}
823+
cleanAttrs[i].Val = strings.Join(cleanVals, " ")
824+
}
825+
}
826+
827+
if !sandboxFound {
828+
sandbox := html.Attribute{}
829+
sandbox.Key = "sandbox"
830+
sandbox.Val = ""
831+
cleanAttrs = append(cleanAttrs, sandbox)
832+
}
833+
}
834+
812835
return cleanAttrs
813836
}
814837

sanitize_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,24 @@ func TestIssue107(t *testing.T) {
18711871
wg.Wait()
18721872
}
18731873

1874+
func TestIFrameSandbox(t *testing.T) {
1875+
p := NewPolicy()
1876+
p.AllowAttrs("sandbox").OnElements("iframe")
1877+
p.RequireSandboxOnIFrame(SandboxAllowDownloads)
1878+
1879+
in := `<iframe src="http://example.com" sandbox="allow-forms allow-downloads"></iframe>`
1880+
expected := `<iframe sandbox="allow-downloads"></iframe>`
1881+
out := p.Sanitize(in)
1882+
if out != expected {
1883+
t.Errorf(
1884+
"test failed;\ninput : %s\noutput : %s\nexpected: %s",
1885+
in,
1886+
out,
1887+
expected,
1888+
)
1889+
}
1890+
}
1891+
18741892
func TestSanitizedURL(t *testing.T) {
18751893
tests := []test{
18761894
{

0 commit comments

Comments
 (0)