package cspbuilder import ( "fmt" "regexp" "strings" ) func buildDirectiveSandbox(sb *strings.Builder, values []string) error { if len(values) == 0 { sb.WriteString(Sandbox) return nil } if len(values) > 1 { return fmt.Errorf("too many values set for directive %s", Sandbox) } sandboxVal := values[0] switch sandboxVal { case "allow-downloads-without-user-activation": case "allow-downloads": case "allow-forms": case "allow-modals": case "allow-orientation-lock": case "allow-pointer-lock": case "allow-popups-to-escape-sandbox": case "allow-popups": case "allow-presentation": case "allow-same-origin": case "allow-scripts": case "allow-storage-access-by-user-activation": case "allow-top-navigation-by-user-activation": case "allow-top-navigation-to-custom-protocols": case "allow-top-navigation": default: return fmt.Errorf("unallowed value %s for directive %s", sandboxVal, Sandbox) } sb.WriteString(Sandbox) sb.WriteString(" ") sb.WriteString(sandboxVal) return nil } func buildDirectiveFrameAncestors(sb *strings.Builder, values []string) error { if len(values) == 0 { return fmt.Errorf("no values set for directive %s", FrameAncestors) } sb.WriteString(FrameAncestors) for _, val := range values { if strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'") { switch val { case "'self'": case "'none'": default: return fmt.Errorf("unallowed value %s for directive %s", val, FrameAncestors) } } sb.WriteString(" ") sb.WriteString(val) } return nil } func buildDirectiveReportTo(sb *strings.Builder, values []string) error { if len(values) == 0 { return fmt.Errorf("no values set for directive %s", ReportTo) } if len(values) > 1 { return fmt.Errorf("too many values set for directive %s", ReportTo) } sb.WriteString(ReportTo) sb.WriteString(" ") sb.WriteString(values[0]) return nil } func buildDirectiveRequireTrustedTypesFor(sb *strings.Builder, values []string) error { const allowedValue = "'script'" if len(values) != 1 || values[0] != allowedValue { return fmt.Errorf("value for directive %s must be %s", RequireTrustedTypesFor, allowedValue) } sb.WriteString(RequireTrustedTypesFor) sb.WriteString(" ") sb.WriteString(values[0]) return nil } func buildDirectiveTrustedTypes(sb *strings.Builder, values []string) error { sb.WriteString(TrustedTypes) for _, val := range values { sb.WriteString(" ") switch val { case "'none'": if len(values) != 1 { return fmt.Errorf("'none' must be only value for directive %s", TrustedTypes) } case "'allow-duplicates'": // nothing to do case "*": // nothing to do default: // value is policy name regex := regexp.MustCompile(`^[A-Za-z0-9\-#=_/@\.%]*$`) if !regex.MatchString(val) { return fmt.Errorf("unallowed value %s for directive %s", val, TrustedTypes) } } sb.WriteString(val) } return nil } func buildDirectiveUpgradeInsecureRequests(sb *strings.Builder, values []string) error { if len(values) != 0 { return fmt.Errorf("directive %s must not contain values", UpgradeInsecureRequests) } sb.WriteString(UpgradeInsecureRequests) return nil } func buildDirectiveDefault(sb *strings.Builder, directive string, values []string) error { if len(values) == 0 { return fmt.Errorf("no values set for directive %s", directive) } sb.WriteString(directive) for i := range values { sb.WriteString(" ") sb.WriteString(values[i]) } return nil }