Skip to content

Commit ce0adc5

Browse files
author
D Kitchen
authored
Merge pull request #136 from kiwiz/main
Add RequireSandboxOnIFrame
2 parents c788a2a + 5638f90 commit ce0adc5

File tree

4 files changed

+126
-1
lines changed

4 files changed

+126
-1
lines changed

helpers.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,9 @@ func (p *Policy) AllowTables() {
295295
CellVerticalAlign,
296296
).OnElements("tbody", "tfoot")
297297
}
298+
299+
func (p *Policy) AllowIFrames(vals ...SandboxValue) {
300+
p.AllowAttrs("sandbox").OnElements("iframe")
301+
302+
p.RequireSandboxOnIFrame(vals...)
303+
}

policy.go

Lines changed: 74 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,58 @@ func (p *Policy) AllowURLSchemeWithCustomPolicy(
680702
return p
681703
}
682704

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

sanitize.go

Lines changed: 28 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,33 @@ 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+
cleanValsSet := make(map[string]bool)
819+
for _, val := range strings.Fields(htmlAttr.Val) {
820+
if p.requireSandboxOnIFrame[val] {
821+
if !cleanValsSet[val] {
822+
cleanVals = append(cleanVals, val)
823+
cleanValsSet[val] = true
824+
}
825+
}
826+
}
827+
cleanAttrs[i].Val = strings.Join(cleanVals, " ")
828+
}
829+
}
830+
831+
if !sandboxFound {
832+
sandbox := html.Attribute{}
833+
sandbox.Key = "sandbox"
834+
sandbox.Val = ""
835+
cleanAttrs = append(cleanAttrs, sandbox)
836+
}
837+
}
838+
812839
return cleanAttrs
813840
}
814841

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 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)