• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

marekdedic / rollup-plugin-htaccess / 9423700726

07 Jun 2024 10:11PM UTC coverage: 31.25% (+8.8%) from 22.449%
9423700726

Pull #3

github

marekdedic
Escaping header expressions
Pull Request #3: Added tests

19 of 75 branches covered (25.33%)

1 of 6 new or added lines in 4 files covered. (16.67%)

4 existing lines in 2 files now uncovered.

30 of 96 relevant lines covered (31.25%)

38.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/headers/ContentSecurityPolicy.ts
1
import { escapeValue } from "../utils";
2

3
type ContentSecurityPolicySourceDirective =
4
  | "base-uri"
5
  | "connect-src"
6
  | "default-src"
7
  | "fenced-frame-src"
8
  | "font-src"
9
  | "form-action"
10
  | "frame-ancestors"
11
  | "frame-src"
12
  | "img-src"
13
  | "manifest-src"
14
  | "media-src"
15
  | "object-src"
16
  | "script-src-attr"
17
  | "script-src-elem"
18
  | "script-src"
19
  | "style-src-attr"
20
  | "style-src-elem"
21
  | "style-src"
22
  | "worker-src";
23

24
/* eslint-disable @typescript-eslint/naming-convention -- These are CSP values */
25
interface ContentSecurityPolicySources {
26
  hosts?: Array<string>;
27
  schemes?: {
28
    data?: boolean;
29
    mediastream?: boolean;
30
    blob?: boolean;
31
    filesystem?: boolean;
32
  };
33
  self?: boolean;
34
  "unsafe-eval"?: boolean;
35
  "wasm-unsafe-eval"?: boolean;
36
  "unsafe-hashes"?: boolean;
37
  "unsafe-inline"?: boolean;
38
  nonces?: Array<string>;
39
  hashes?: {
40
    sha256?: Array<string>;
41
    sha384?: Array<string>;
42
    sha512?: Array<string>;
43
  };
44
  "strict-dynamic"?: boolean;
45
  "report-sample"?: boolean;
46
  "inline-speculation-rules"?: boolean;
47
}
48
/* eslint-enable */
49

50
type ContentSecurityPolicySandboxValue =
51
  | "allow-downloads-without-user-activation Experimental"
52
  | "allow-downloads"
53
  | "allow-forms"
54
  | "allow-modals"
55
  | "allow-orientation-lock"
56
  | "allow-pointer-lock"
57
  | "allow-popups-to-escape-sandbox"
58
  | "allow-popups"
59
  | "allow-presentation"
60
  | "allow-same-origin"
61
  | "allow-scripts"
62
  | "allow-storage-access-by-user-activation Experimental"
63
  | "allow-top-navigation-by-user-activation"
64
  | "allow-top-navigation-to-custom-protocols"
65
  | "allow-top-navigation"
66
  | undefined;
67

68
interface ContentSecurityPolicyTrustedTypesValue {
69
  policies: Array<string>;
70
  allowDuplicates?: boolean;
71
}
72

73
/* eslint-disable @typescript-eslint/naming-convention -- These are directive names */
74
export type ContentSecurityPolicySpec = Partial<
75
  Record<ContentSecurityPolicySourceDirective, ContentSecurityPolicySources> & {
76
    sandbox: ContentSecurityPolicySandboxValue;
77
    "report-uri": Array<string>;
78
    "report-to": string;
79
    "require-trusted-types-for": "script";
80
    "upgrade-insecure-requests": boolean;
81
    "trusted-types": ContentSecurityPolicyTrustedTypesValue;
82
  }
83
>;
84
/* eslint-enable */
85

86
function buildSandboxPart(
87
  valueSpec: ContentSecurityPolicySandboxValue,
88
): string {
NEW
89
  if (valueSpec !== undefined) {
×
90
    return "sandbox " + valueSpec;
×
91
  } else {
92
    return "sandbox";
×
93
  }
94
}
95

96
function buildTrustedTypesPart(
97
  valueSpec: ContentSecurityPolicyTrustedTypesValue,
98
): string {
NEW
99
  const parts = ["trusted-types", ...valueSpec.policies];
×
NEW
100
  if (valueSpec.allowDuplicates === true) {
×
UNCOV
101
    parts.push("'allow-duplicates'");
×
102
  }
103
  return parts.join(" ");
×
104
}
105

106
function buildSourcePart(
107
  directive: ContentSecurityPolicySourceDirective,
108
  sourceSpec: ContentSecurityPolicySources,
109
): string {
110
  const sources = [];
×
111
  for (const source of [
×
112
    "self",
113
    "unsafe-eval",
114
    "wasm-unsafe-eval",
115
    "unsafe-hashes",
116
    "unsafe-inline",
117
    "strict-dynamic",
118
    "report-sample",
119
    "inline-speculation-rules",
120
  ] as const) {
121
    if (sourceSpec[source] === true) {
×
122
      sources.push("'" + source + "'");
×
123
    }
124
  }
125
  if (sourceSpec.schemes !== undefined) {
×
126
    for (const scheme of [
×
127
      "data",
128
      "mediastream",
129
      "blob",
130
      "filesystem",
131
    ] as const) {
132
      if (sourceSpec.schemes[scheme] === true) {
×
133
        sources.push(scheme + ":");
×
134
      }
135
    }
136
  }
137
  if (sourceSpec.nonces !== undefined) {
×
138
    sources.push(...sourceSpec.nonces.map((nonce) => "nonce-" + nonce));
×
139
  }
140
  if (sourceSpec.hashes !== undefined) {
×
141
    for (const algo of ["sha256", "sha384", "sha512"] as const) {
×
142
      const hashes = sourceSpec.hashes[algo];
×
143
      if (hashes !== undefined) {
×
144
        sources.push(...hashes.map((hash) => algo + "-" + hash));
×
145
      }
146
    }
147
  }
148
  if (sourceSpec.hosts !== undefined) {
×
149
    sources.push(...sourceSpec.hosts.map(escapeValue));
×
150
  }
151
  if (sources.length === 0) {
×
152
    sources.push("'none'");
×
153
  }
154
  return directive + " " + sources.join(" ");
×
155
}
156

157
function buildPart<
158
  T extends keyof ContentSecurityPolicySpec,
159
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Needed to correctly infer value type
160
  V extends ContentSecurityPolicySpec[T] & Record<T, any>,
161
>(directive: T, valueSpec: V[T]): string {
162
  switch (directive) {
×
163
    case "sandbox":
164
      return buildSandboxPart(valueSpec);
×
165
    case "report-uri":
166
      return ["report-uri", ...valueSpec].join(" ");
×
167
    case "report-to":
168
      return "report-to " + (valueSpec as string);
×
169
    case "require-trusted-types-for":
170
      return "require-trusted-types-for 'script'";
×
171
    case "upgrade-insecure-requests":
172
      return "upgrade-insecure-requests";
×
173
    case "trusted-types":
174
      return buildTrustedTypesPart(valueSpec);
×
175
    default:
176
      return buildSourcePart(directive, valueSpec);
×
177
  }
178
}
179

180
export function buildContentSecurityPolicyValue(
181
  spec: ContentSecurityPolicySpec,
182
): string {
183
  const parts = [];
×
184
  for (const directive in spec) {
×
185
    parts.push(
×
186
      buildPart(
187
        directive as keyof ContentSecurityPolicySpec,
188
        spec[directive as keyof ContentSecurityPolicySpec],
189
      ),
190
    );
191
  }
192
  return '"' + parts.join("; ") + '"';
×
193
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc