-
Notifications
You must be signed in to change notification settings - Fork 3
/
feature-policy.go
162 lines (138 loc) · 5.57 KB
/
feature-policy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package helmet
import (
"fmt"
"net/http"
"strings"
)
// HeaderFeaturePolicy is the Feature-Policy HTTP security header.
const HeaderFeaturePolicy = "Feature-Policy"
// List of all Feature-Policy directives.
const (
DirectiveAccelerometer FeaturePolicyDirective = "accelerometer"
DirectiveAmbientLightSensor FeaturePolicyDirective = "ambient-light-sensor"
DirectiveAutoplay FeaturePolicyDirective = "autoplay"
DirectiveBattery FeaturePolicyDirective = "battery"
DirectiveCamera FeaturePolicyDirective = "camera"
DirectiveDisplayCapture FeaturePolicyDirective = "display-capture"
DirectiveDocumentDomain FeaturePolicyDirective = "document-domain"
DirectiveEncryptedMedia FeaturePolicyDirective = "encrypted-media"
DirectiveExecutionWhileNotRendered FeaturePolicyDirective = "execution-while-not-rendered"
DirectiveExecutionWhileOutOfViewport FeaturePolicyDirective = "execution-while-out-of-viewport"
DirectiveFullscreen FeaturePolicyDirective = "fullscreen"
DirectiveGamepad FeaturePolicyDirective = "gamepad"
DirectiveGeolocation FeaturePolicyDirective = "geolocation"
DirectiveGyroscope FeaturePolicyDirective = "gyroscope"
DirectiveMagnetometer FeaturePolicyDirective = "magnetometer"
DirectiveMicrophone FeaturePolicyDirective = "microphone"
DirectiveMidi FeaturePolicyDirective = "midi"
DirectiveNavigationOverride FeaturePolicyDirective = "navigation-override"
DirectiveOversizedImages FeaturePolicyDirective = "oversized-images"
DirectivePayment FeaturePolicyDirective = "payment"
DirectivePictureInPicture FeaturePolicyDirective = "picture-in-picture"
DirectivePublicKeyCredentialsGet FeaturePolicyDirective = "publickey-credentials-get"
DirectiveSpeakerSelection FeaturePolicyDirective = "speaker-selection"
DirectiveSyncXHR FeaturePolicyDirective = "sync-xhr"
DirectiveUSB FeaturePolicyDirective = "usb"
DirectiveScreenWakeLock FeaturePolicyDirective = "screen-wake-lock"
DirectiveWebShare FeaturePolicyDirective = "web-share"
DirectiveXRSpacialTracking FeaturePolicyDirective = "xr-spatial-tracking"
)
// deprecated
const (
NonStandardDirectiveLayoutAnimations FeaturePolicyDirective = "layout-animations"
NonStandardDirectiveLegacyImageFormats FeaturePolicyDirective = "legacy-image-formats"
NonStandardDirectiveOversizedImages FeaturePolicyDirective = "oversized-images"
NonStandardDirectiveUnoptimizedImages FeaturePolicyDirective = "unoptimized-images"
NonStandardDirectiveUnsizedMedia FeaturePolicyDirective = "unsized-media"
)
// List of all Feature-Policy origins.
const (
OriginWildcard FeaturePolicyOrigin = "*"
OriginSelf FeaturePolicyOrigin = "'self'"
OriginSrc FeaturePolicyOrigin = "'src'"
OriginNone FeaturePolicyOrigin = "'none'"
)
type (
// FeaturePolicyDirective represents a Feature-Policy directive.
FeaturePolicyDirective string
// FeaturePolicyOrigin represents a Feature-Policy origin.
FeaturePolicyOrigin string
// FeaturePolicy represents the Feature-Policy HTTP security header.
FeaturePolicy struct {
policies map[FeaturePolicyDirective][]FeaturePolicyOrigin
cache string
}
)
// NewFeaturePolicy creates a new Feature-Policy.
func NewFeaturePolicy(policies map[FeaturePolicyDirective][]FeaturePolicyOrigin) *FeaturePolicy {
if policies == nil {
return EmptyFeaturePolicy()
}
return &FeaturePolicy{policies, ""}
}
// EmptyFeaturePolicy creates a blank slate Feature-Policy.
func EmptyFeaturePolicy() *FeaturePolicy {
return NewFeaturePolicy(make(map[FeaturePolicyDirective][]FeaturePolicyOrigin))
}
// Add adds a directive and its origins.
func (fp *FeaturePolicy) Add(directive FeaturePolicyDirective, origins ...FeaturePolicyOrigin) {
if len(directive) == 0 || len(origins) == 0 {
return
}
fp.cache = ""
fp.create(directive)
for _, origin := range origins {
fp.policies[directive] = append(fp.policies[directive], origin)
}
}
func (fp *FeaturePolicy) create(directive FeaturePolicyDirective) {
if len(directive) == 0 {
return
}
fp.cache = ""
if _, ok := fp.policies[directive]; !ok {
fp.policies[directive] = []FeaturePolicyOrigin{}
}
}
// Remove removes a directive and its origins.
func (fp *FeaturePolicy) Remove(directives ...FeaturePolicyDirective) {
if len(directives) == 0 {
return
}
didRemove := false
for _, directive := range directives {
if _, ok := fp.policies[directive]; ok {
didRemove = true
delete(fp.policies, directive)
}
}
if didRemove {
fp.cache = ""
}
}
// String generates the Feature-Policy.
func (fp *FeaturePolicy) String() string {
if fp.cache != "" {
return fp.cache
}
var policies = []string{}
for directive, origins := range fp.policies {
originsAsStrings := []string{}
for _, origin := range origins {
originsAsStrings = append(originsAsStrings, string(origin))
}
policies = append(policies, fmt.Sprintf("%s %s", directive, strings.Join(originsAsStrings, " ")))
}
fp.cache = strings.Join(policies, "; ")
return fp.cache
}
// Empty returns whether the Feature-Policy is empty.
func (fp *FeaturePolicy) Empty() bool {
return len(fp.policies) == 0
}
// Header adds the Feature-Policy HTTP security header to the given http.ResponseWriter.
func (fp *FeaturePolicy) Header(w http.ResponseWriter) {
if !fp.Empty() {
w.Header().Set(HeaderFeaturePolicy, fp.String())
}
}