csprefabricate 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,20 +14,126 @@ Currently `csprefabricate`:
14
14
 
15
15
  - Validates directive names
16
16
  - Supports providing a list of TLDs for a given domain name
17
+ - Provides warnings for insecure or incomplete CSP configurations, with options to disable specific warnings
18
+
19
+ ## Common CSP Issues
20
+
21
+ By default, `csprefabricate` will warn you about common CSP issues, such as:
22
+
23
+ - Overly permissive sources (e.g. using `*`)
24
+ - Missing recommended directives (i.e. `object-src`, `base-uri`, `form-action`)
25
+ - Use of `'unsafe-inline'` in `script-src`, even if nonces or hashes are present
26
+ - Missing nonces or hashes when using `'unsafe-inline'` in `script-src`
27
+ - Allowing `data:` in `img-src` or `media-src`
28
+
29
+ You can control which warnings are shown by passing an optional `WarningOptions` object to the `create` function:
17
30
 
18
31
  ```typescript
19
- import {create} from "csprefabricate";
32
+ import {
33
+ create,
34
+ Directive,
35
+ ContentSecurityPolicy,
36
+ WarningOptions,
37
+ } from "csprefabricate";
38
+
39
+ const csp: ContentSecurityPolicy = {
40
+ [Directive.SCRIPT_SRC]: ["*"],
41
+ [Directive.IMG_SRC]: ["data:"],
42
+ };
20
43
 
21
- const input = {
22
- [Directive.DEFAULT_SRC]: ["self"],
23
- [Directive.IMG_SRC]: ["self", {"*.google": [".com", ".com.au"]}],
24
- } satisfies ContentSecurityPolicy;
44
+ // Disable all warnings
45
+ const warningOptions: WarningOptions = {
46
+ overlyPermissive: false,
47
+ missingDirectives: false,
48
+ unsafeInline: false,
49
+ missingNonceOrHash: false,
50
+ dataUri: false,
51
+ };
25
52
 
26
- const output = create(csp);
27
- // > "default-src 'self'; img-src 'self' *.google.com *.google.com.au;",
53
+ create(csp, warningOptions);
28
54
  ```
29
55
 
30
- ## Future
56
+ You can selectively enable or disable specific warnings as needed.
57
+
58
+ ## Real World Examples
59
+
60
+ ### Example 1: Basic Strict Policy
61
+
62
+ ```typescript
63
+ import {create, Directive, ContentSecurityPolicy} from "csprefabricate";
31
64
 
32
- - Generate baseline recommended CSPs (for example, Google Analytics)
33
- - Warnings for insecure configurations
65
+ const csp: ContentSecurityPolicy = {
66
+ [Directive.DEFAULT_SRC]: ["'self'"],
67
+ [Directive.SCRIPT_SRC]: ["'self'"],
68
+ [Directive.STYLE_SRC]: ["'self'"],
69
+ [Directive.IMG_SRC]: ["'self'"],
70
+ [Directive.OBJECT_SRC]: ["'none'"],
71
+ [Directive.BASE_URI]: ["'self'"],
72
+ [Directive.FORM_ACTION]: ["'self'"],
73
+ };
74
+
75
+ const cspString = create(csp);
76
+ // "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self';"
77
+ ```
78
+
79
+ ### Example 2: Allowing Google Analytics
80
+
81
+ ```typescript
82
+ import {create, Directive, ContentSecurityPolicy} from "csprefabricate";
83
+
84
+ const csp: ContentSecurityPolicy = {
85
+ [Directive.DEFAULT_SRC]: ["'self'"],
86
+ [Directive.SCRIPT_SRC]: ["'self'", "*.googletagmanager.com"],
87
+ [Directive.STYLE_SRC]: ["'self'"],
88
+ [Directive.IMG_SRC]: [
89
+ "'self'",
90
+ "https://*.google-analytics.com",
91
+ "https://*.googletagmanager.com",
92
+ ],
93
+ [Directive.OBJECT_SRC]: ["'none'"],
94
+ [Directive.BASE_URI]: ["'self'"],
95
+ [Directive.FORM_ACTION]: ["'self'"],
96
+ [Directive.CONNECT_SRC]: [
97
+ "'self'",
98
+ "https://*.google-analytics.com",
99
+ "https://*.analytics.google.com",
100
+ "https://*.googletagmanager.com",
101
+ ],
102
+ };
103
+
104
+ const cspString = create(csp);
105
+ // "default-src 'self'; script-src 'self' *.googletagmanager.com; style-src 'self'; img-src 'self' https://*.google-analytics.com https://*.googletagmanager.com; object-src 'none'; base-uri 'self'; form-action 'self'; connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com;"
106
+ ```
107
+
108
+ ### Example 3: Using TLD Expansion for Multiple Domains
109
+
110
+ ```typescript
111
+ import {create, Directive, ContentSecurityPolicy} from "csprefabricate";
112
+
113
+ const csp: ContentSecurityPolicy = {
114
+ [Directive.IMG_SRC]: ["self", {"*.example": [".com", ".co.uk", ".net"]}],
115
+ };
116
+
117
+ const cspString = create(csp);
118
+ // "img-src 'self' *.example.com *.example.co.uk *.example.net;"
119
+ ```
120
+
121
+ ## Baseline Recommended CSPs
122
+
123
+ You can quickly generate a recommended Content Security Policy for common use cases using built-in baselines.
124
+
125
+ Available Baselines:
126
+
127
+ - BASELINE_STRICT_CSP
128
+ - GOOGLE_ANALYTICS_CSP
129
+ - GOOGLE_ANALYTICS_WITH_SIGNALS_CSP
130
+
131
+ ### Google Analytics Baseline CSP
132
+
133
+ Allow Google Analytics and Tag Manager:
134
+
135
+ ```typescript
136
+ import {create, Baseline} from "csprefabricate";
137
+
138
+ const cspString = create(Baseline.GOOGLE_ANALYTICS_CSP);
139
+ ```
@@ -0,0 +1,8 @@
1
+ import { ContentSecurityPolicy } from "./types";
2
+ export declare const BASELINE_STRICT_CSP: ContentSecurityPolicy;
3
+ /**
4
+ * Google Analytics Content Security Policy based on the official guidelines.
5
+ * https://developers.google.com/tag-platform/security/guides/csp#google_analytics_4_google_analytics
6
+ */
7
+ export declare const GOOGLE_ANALYTICS_CSP: ContentSecurityPolicy;
8
+ export declare const GOOGLE_ANALYTICS_WITH_SIGNALS_CSP: ContentSecurityPolicy;
@@ -0,0 +1,245 @@
1
+ import { Directive } from "./types";
2
+ // List of supported domains for Google Signals from https://www.google.com/supported_domains
3
+ const googleSupportedTLDs = [
4
+ ".com",
5
+ ".ad",
6
+ ".ae",
7
+ ".com.af",
8
+ ".com.ag",
9
+ ".al",
10
+ ".am",
11
+ ".co.ao",
12
+ ".com.ar",
13
+ ".as",
14
+ ".at",
15
+ ".com.au",
16
+ ".az",
17
+ ".ba",
18
+ ".com.bd",
19
+ ".be",
20
+ ".bf",
21
+ ".bg",
22
+ ".com.bh",
23
+ ".bi",
24
+ ".bj",
25
+ ".com.bn",
26
+ ".com.bo",
27
+ ".com.br",
28
+ ".bs",
29
+ ".bt",
30
+ ".co.bw",
31
+ ".by",
32
+ ".com.bz",
33
+ ".ca",
34
+ ".cd",
35
+ ".cf",
36
+ ".cg",
37
+ ".ch",
38
+ ".ci",
39
+ ".co.ck",
40
+ ".cl",
41
+ ".cm",
42
+ ".cn",
43
+ ".com.co",
44
+ ".co.cr",
45
+ ".com.cu",
46
+ ".cv",
47
+ ".com.cy",
48
+ ".cz",
49
+ ".de",
50
+ ".dj",
51
+ ".dk",
52
+ ".dm",
53
+ ".com.do",
54
+ ".dz",
55
+ ".com.ec",
56
+ ".ee",
57
+ ".com.eg",
58
+ ".es",
59
+ ".com.et",
60
+ ".fi",
61
+ ".com.fj",
62
+ ".fm",
63
+ ".fr",
64
+ ".ga",
65
+ ".ge",
66
+ ".gg",
67
+ ".com.gh",
68
+ ".com.gi",
69
+ ".gl",
70
+ ".gm",
71
+ ".gr",
72
+ ".com.gt",
73
+ ".gy",
74
+ ".com.hk",
75
+ ".hn",
76
+ ".hr",
77
+ ".ht",
78
+ ".hu",
79
+ ".co.id",
80
+ ".ie",
81
+ ".co.il",
82
+ ".im",
83
+ ".co.in",
84
+ ".iq",
85
+ ".is",
86
+ ".it",
87
+ ".je",
88
+ ".com.jm",
89
+ ".jo",
90
+ ".co.jp",
91
+ ".co.ke",
92
+ ".com.kh",
93
+ ".ki",
94
+ ".kg",
95
+ ".co.kr",
96
+ ".com.kw",
97
+ ".kz",
98
+ ".la",
99
+ ".com.lb",
100
+ ".li",
101
+ ".lk",
102
+ ".co.ls",
103
+ ".lt",
104
+ ".lu",
105
+ ".lv",
106
+ ".com.ly",
107
+ ".co.ma",
108
+ ".md",
109
+ ".me",
110
+ ".mg",
111
+ ".mk",
112
+ ".ml",
113
+ ".com.mm",
114
+ ".mn",
115
+ ".com.mt",
116
+ ".mu",
117
+ ".mv",
118
+ ".mw",
119
+ ".com.mx",
120
+ ".com.my",
121
+ ".co.mz",
122
+ ".com.na",
123
+ ".com.ng",
124
+ ".com.ni",
125
+ ".ne",
126
+ ".nl",
127
+ ".no",
128
+ ".com.np",
129
+ ".nr",
130
+ ".nu",
131
+ ".co.nz",
132
+ ".com.om",
133
+ ".com.pa",
134
+ ".com.pe",
135
+ ".com.pg",
136
+ ".com.ph",
137
+ ".com.pk",
138
+ ".pl",
139
+ ".pn",
140
+ ".com.pr",
141
+ ".ps",
142
+ ".pt",
143
+ ".com.py",
144
+ ".com.qa",
145
+ ".ro",
146
+ ".ru",
147
+ ".rw",
148
+ ".com.sa",
149
+ ".com.sb",
150
+ ".sc",
151
+ ".se",
152
+ ".com.sg",
153
+ ".sh",
154
+ ".si",
155
+ ".sk",
156
+ ".com.sl",
157
+ ".sn",
158
+ ".so",
159
+ ".sm",
160
+ ".sr",
161
+ ".st",
162
+ ".com.sv",
163
+ ".td",
164
+ ".tg",
165
+ ".co.th",
166
+ ".com.tj",
167
+ ".tl",
168
+ ".tm",
169
+ ".tn",
170
+ ".to",
171
+ ".com.tr",
172
+ ".tt",
173
+ ".com.tw",
174
+ ".co.tz",
175
+ ".com.ua",
176
+ ".co.ug",
177
+ ".co.uk",
178
+ ".com.uy",
179
+ ".co.uz",
180
+ ".com.vc",
181
+ ".co.ve",
182
+ ".co.vi",
183
+ ".com.vn",
184
+ ".vu",
185
+ ".ws",
186
+ ".rs",
187
+ ".co.za",
188
+ ".co.zm",
189
+ ".co.zw",
190
+ ".cat",
191
+ ];
192
+ export const BASELINE_STRICT_CSP = {
193
+ [Directive.DEFAULT_SRC]: ["'self'"],
194
+ [Directive.SCRIPT_SRC]: ["'self'"],
195
+ [Directive.STYLE_SRC]: ["'self'"],
196
+ [Directive.IMG_SRC]: ["'self'"],
197
+ [Directive.OBJECT_SRC]: ["'none'"],
198
+ [Directive.BASE_URI]: ["'self'"],
199
+ [Directive.FORM_ACTION]: ["'self'"],
200
+ };
201
+ /**
202
+ * Google Analytics Content Security Policy based on the official guidelines.
203
+ * https://developers.google.com/tag-platform/security/guides/csp#google_analytics_4_google_analytics
204
+ */
205
+ export const GOOGLE_ANALYTICS_CSP = {
206
+ ...BASELINE_STRICT_CSP,
207
+ [Directive.DEFAULT_SRC]: ["'self'"],
208
+ [Directive.SCRIPT_SRC]: ["'self'", "*.googletagmanager.com"],
209
+ [Directive.IMG_SRC]: [
210
+ "'self'",
211
+ "https://*.google-analytics.com",
212
+ "https://*.googletagmanager.com",
213
+ ],
214
+ [Directive.CONNECT_SRC]: [
215
+ "'self'",
216
+ "https://*.google-analytics.com",
217
+ "https://*.analytics.google.com",
218
+ "https://*.googletagmanager.com",
219
+ ],
220
+ };
221
+ export const GOOGLE_ANALYTICS_WITH_SIGNALS_CSP = {
222
+ ...BASELINE_STRICT_CSP,
223
+ ...GOOGLE_ANALYTICS_CSP,
224
+ [Directive.IMG_SRC]: [
225
+ "'self'",
226
+ "https://*.google-analytics.com",
227
+ "https://*.googletagmanager.com",
228
+ "https://*.g.doubleclick.net",
229
+ "https://*.google.com",
230
+ { "https://*.google.": googleSupportedTLDs },
231
+ ],
232
+ [Directive.CONNECT_SRC]: [
233
+ "'self'",
234
+ "https://*.google-analytics.com",
235
+ "https://*.googletagmanager.com",
236
+ "https://*.g.doubleclick.net",
237
+ "https://pagead2.googlesyndication.com",
238
+ { "https://*.google": googleSupportedTLDs },
239
+ ],
240
+ [Directive.FRAME_SRC]: [
241
+ "'self'",
242
+ "https://td.doubleclick.net",
243
+ "https://www.googletagmanager.com",
244
+ ],
245
+ };
package/dist/helpers.d.ts CHANGED
@@ -1,2 +1,11 @@
1
+ import { ContentSecurityPolicy } from "./types";
2
+ export interface WarningOptions {
3
+ overlyPermissive?: boolean;
4
+ missingDirectives?: boolean;
5
+ unsafeInline?: boolean;
6
+ missingNonceOrHash?: boolean;
7
+ dataUri?: boolean;
8
+ }
9
+ export declare function warnOnCspIssues(csp: ContentSecurityPolicy, overrides?: WarningOptions): void;
1
10
  export declare const isValidDirective: (directive: string) => boolean;
2
11
  export declare const formatRule: (rule: string) => string;
package/dist/helpers.js CHANGED
@@ -1,3 +1,11 @@
1
+ import { Directive } from "./types";
2
+ const DEFAULT_WARNINGS = {
3
+ overlyPermissive: true,
4
+ missingDirectives: true,
5
+ unsafeInline: true,
6
+ missingNonceOrHash: true,
7
+ dataUri: true,
8
+ };
1
9
  const validDirectives = [
2
10
  "default-src",
3
11
  "script-src",
@@ -33,5 +41,63 @@ const specialRules = [
33
41
  "strict-dynamic",
34
42
  "unsafe-hashes",
35
43
  ];
44
+ export function warnOnCspIssues(csp, overrides = {}) {
45
+ const options = { ...DEFAULT_WARNINGS, ...overrides };
46
+ // 1. Overly permissive: * in script-src, style-src, etc.
47
+ if (options.overlyPermissive) {
48
+ [
49
+ Directive.SCRIPT_SRC,
50
+ Directive.STYLE_SRC,
51
+ Directive.IMG_SRC,
52
+ Directive.CONNECT_SRC,
53
+ ].forEach((directive) => {
54
+ const rules = csp[directive];
55
+ if (Array.isArray(rules) && rules.includes("*")) {
56
+ console.warn(`[CSPrefabricate] Overly permissive: '*' found in ${directive}`);
57
+ }
58
+ });
59
+ }
60
+ // 2. Missing important directives
61
+ if (options.missingDirectives) {
62
+ [
63
+ Directive.OBJECT_SRC,
64
+ Directive.BASE_URI,
65
+ Directive.FORM_ACTION,
66
+ ].forEach((directive) => {
67
+ if (!(directive in csp)) {
68
+ console.warn(`[CSPrefabricate] Missing recommended directive: ${directive}`);
69
+ }
70
+ });
71
+ }
72
+ // 3. Unsafe inline
73
+ if (options.unsafeInline) {
74
+ [Directive.SCRIPT_SRC, Directive.STYLE_SRC].forEach((directive) => {
75
+ const rules = csp[directive];
76
+ if (Array.isArray(rules) && rules.includes("'unsafe-inline'")) {
77
+ console.warn(`[CSPrefabricate] 'unsafe-inline' found in ${directive}`);
78
+ }
79
+ });
80
+ }
81
+ // 4. Missing nonce or hash in script-src if 'unsafe-inline' is present
82
+ if (options.missingNonceOrHash) {
83
+ const rules = csp[Directive.SCRIPT_SRC];
84
+ if (Array.isArray(rules) && rules.includes("'unsafe-inline'")) {
85
+ const hasNonceOrHash = rules.some((r) => typeof r === "string" &&
86
+ (r.startsWith("'nonce-") || r.startsWith("'sha")));
87
+ if (!hasNonceOrHash) {
88
+ console.warn(`[CSPrefabricate] 'unsafe-inline' in script-src without nonce or hash`);
89
+ }
90
+ }
91
+ }
92
+ // 5. Permitting data: in img-src or media-src
93
+ if (options.dataUri) {
94
+ [Directive.IMG_SRC, Directive.MEDIA_SRC].forEach((directive) => {
95
+ const rules = csp[directive];
96
+ if (Array.isArray(rules) && rules.includes("data:")) {
97
+ console.warn(`[CSPrefabricate] 'data:' allowed in ${directive}`);
98
+ }
99
+ });
100
+ }
101
+ }
36
102
  export const isValidDirective = (directive) => validDirectives.includes(directive);
37
103
  export const formatRule = (rule) => specialRules.includes(rule) ? `'${rule}'` : rule;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
- import { ContentSecurityPolicy, Directive } from "./types";
1
+ import { Directive } from "./types";
2
2
  import { create } from "./utils";
3
- export { create, Directive, ContentSecurityPolicy };
3
+ import * as Baseline from "./baseline";
4
+ export { Baseline };
5
+ export { create, Directive };
6
+ export type { ContentSecurityPolicy } from "./types";
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  import { Directive } from "./types";
2
2
  import { create } from "./utils";
3
+ import * as Baseline from "./baseline";
4
+ export { Baseline };
3
5
  export { create, Directive };
package/dist/types.d.ts CHANGED
@@ -55,4 +55,4 @@ interface ContentSecurityPolicy {
55
55
  [Directive.UPGRADE_INSECURE_REQUESTS]?: BlankDirectiveRule;
56
56
  [Directive.BLOCK_ALL_MIXED_CONTENT]?: BlankDirectiveRule;
57
57
  }
58
- export { ContentSecurityPolicy, Rules, Directive };
58
+ export { ContentSecurityPolicy, Rules, Directive, BasicDirectiveRule };
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,10 @@
1
- import { ContentSecurityPolicy } from "./types";
2
- export declare const processRules: (rules: Array<string> | Array<string | Record<string, Array<string>>>) => string;
3
- export declare const create: (obj: ContentSecurityPolicy) => string;
1
+ import { WarningOptions } from "./helpers";
2
+ import { ContentSecurityPolicy, BasicDirectiveRule } from "./types";
3
+ export declare const processRules: (rules: BasicDirectiveRule) => string;
4
+ /**
5
+ * Creates a CSP string from a ContentSecurityPolicy object.
6
+ * Filters out invalid directives and formats the CSP string.
7
+ * @param obj - The ContentSecurityPolicy object.
8
+ * @returns The formatted CSP string.
9
+ */
10
+ export declare const create: (obj: ContentSecurityPolicy, warningOptions?: WarningOptions) => string;
package/dist/utils.js CHANGED
@@ -1,26 +1,47 @@
1
- import { formatRule, isValidDirective } from "./helpers";
1
+ import { formatRule, isValidDirective, warnOnCspIssues, } from "./helpers";
2
2
  export const processRules = (rules) => {
3
- return rules
4
- .map((rule) => {
3
+ // Flatten and deduplicate rules
4
+ const seen = new Set();
5
+ for (const rule of rules) {
5
6
  if (typeof rule === "object") {
6
- return Object.entries(rule).map(([domain, tlds]) => tlds.map((tld) => `${domain}${tld}`).join(" "));
7
+ for (const [domain, tlds] of Object.entries(rule)) {
8
+ for (const tld of tlds) {
9
+ seen.add(`${domain}${tld}`);
10
+ }
11
+ }
7
12
  }
8
13
  else {
9
- return formatRule(rule);
14
+ seen.add(formatRule(rule));
10
15
  }
11
- })
12
- .join(" ");
16
+ }
17
+ return Array.from(seen).join(" ");
13
18
  };
14
- export const create = (obj) => {
19
+ /**
20
+ * Creates a CSP string from a ContentSecurityPolicy object.
21
+ * Filters out invalid directives and formats the CSP string.
22
+ * @param obj - The ContentSecurityPolicy object.
23
+ * @returns The formatted CSP string.
24
+ */
25
+ export const create = (obj, warningOptions) => {
26
+ warnOnCspIssues(obj, warningOptions);
15
27
  const entries = Object.entries(obj);
16
28
  const cspString = entries
17
29
  .filter(([directive, _rules]) => {
18
30
  const isValid = isValidDirective(directive);
19
31
  if (!isValid) {
20
- console.warn(`"${directive}" is not a valid CSP directive and has been ignored.`);
32
+ console.warn(`[CSPrefabricate] "${directive}" is not a valid CSP directive and has been ignored.`);
21
33
  }
22
34
  return isValid;
23
35
  })
24
- .map(([directive, rules]) => `${directive}${rules && rules.length > 0 ? " " + processRules(rules) : ""}`);
25
- return `${cspString.join("; ")};`;
36
+ .map(([directive, rules]) => {
37
+ if (Array.isArray(rules)) {
38
+ // Filter out non-string/object values at runtime
39
+ const filtered = rules.filter((r) => typeof r === "string" ||
40
+ (typeof r === "object" && r !== null));
41
+ const processed = processRules(filtered);
42
+ return processed ? `${directive} ${processed}` : `${directive}`;
43
+ }
44
+ return `${directive}`;
45
+ });
46
+ return cspString.length > 0 ? `${cspString.join("; ")};` : "";
26
47
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "csprefabricate",
3
- "version": "0.2.3",
3
+ "version": "0.4.0",
4
4
  "packageManager": "yarn@4.5.3",
5
5
  "type": "module",
6
6
  "devDependencies": {
@@ -20,11 +20,10 @@
20
20
  ],
21
21
  "scripts": {
22
22
  "build": "tsc --project tsconfig.build.json",
23
+ "functional-test": "yarn build && tsx --test src/test/functional/functional.test.js",
23
24
  "pack": "npm pack",
24
25
  "prepack": "yarn typecheck && yarn test && yarn build",
25
- "prettier": "prettier . --write",
26
26
  "prepublish": "yarn version check",
27
- "publish": "npm publish",
28
27
  "test": "tsx --test src/test/**/*test.ts",
29
28
  "typecheck": "tsc --noEmit",
30
29
  "lint": "eslint ."
@@ -1 +0,0 @@
1
- export {};
@@ -1,23 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert";
3
- import { formatRule, isValidDirective } from "../helpers";
4
- import { Directive } from "../types";
5
- describe("Helpers tests", () => {
6
- describe("isValidDirective", () => {
7
- it("Returns true if directive is valid", () => {
8
- assert.strictEqual(isValidDirective(Directive.BASE_URI), true);
9
- assert.strictEqual(isValidDirective("default-src"), true);
10
- });
11
- it("Returns false if directive is invalid", () => {
12
- assert.strictEqual(isValidDirective("some-src"), false);
13
- });
14
- });
15
- describe("formatRule", () => {
16
- it("Formats special rules with single quotes", () => {
17
- assert.strictEqual(formatRule("self"), `'self'`);
18
- });
19
- it("Returns non-special rules", () => {
20
- assert.strictEqual(formatRule("google.com"), `google.com`);
21
- });
22
- });
23
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,69 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert";
3
- import { create, processRules } from "../utils";
4
- import { Directive } from "../types";
5
- describe("Utils tests", () => {
6
- describe("processRules", () => {
7
- it("Processes rules provided as an array of strings (simple)", () => {
8
- const rules = ["self", "*.google.com", "*.google.com.au"];
9
- assert.strictEqual(processRules(rules), `'self' *.google.com *.google.com.au`);
10
- });
11
- it("Processes rules provided a complex list of tlds", () => {
12
- const rules = ["self", { "*.google": [".com", ".com.au"] }];
13
- assert.strictEqual(processRules(rules), `'self' *.google.com *.google.com.au`);
14
- });
15
- });
16
- describe("create", () => {
17
- it("Formats a CSP string with all rules", () => {
18
- const csp = {
19
- [Directive.DEFAULT_SRC]: ["self"],
20
- [Directive.SCRIPT_SRC]: ["self", "js.example.com"],
21
- [Directive.STYLE_SRC]: ["self", "css.example.com"],
22
- [Directive.IMG_SRC]: [
23
- "self",
24
- { "*.google": [".com", ".com.au"] },
25
- ],
26
- [Directive.CONNECT_SRC]: ["self"],
27
- [Directive.FONT_SRC]: ["self", "font.example.com"],
28
- [Directive.OBJECT_SRC]: ["none"],
29
- [Directive.MEDIA_SRC]: ["self", "media.example.com"],
30
- [Directive.FRAME_SRC]: ["self"],
31
- [Directive.SANDBOX]: ["allow-scripts"],
32
- [Directive.REPORT_URI]: ["/my-report-uri"],
33
- [Directive.CHILD_SRC]: ["self"],
34
- [Directive.FORM_ACTION]: ["self"],
35
- [Directive.FRAME_ANCESTORS]: ["none"],
36
- [Directive.PLUGIN_TYPES]: ["application/pdf"],
37
- [Directive.BASE_URI]: ["self"],
38
- [Directive.REPORT_TO]: ["myGroupName"],
39
- [Directive.WORKER_SRC]: ["none"],
40
- [Directive.MANIFEST_SRC]: ["none"],
41
- [Directive.PREFETCH_SRC]: ["none"],
42
- [Directive.NAVIGATE_TO]: ["example.com"],
43
- [Directive.REQUIRE_TRUSTED_TYPES_FOR]: ["script"],
44
- [Directive.TRUSTED_TYPES]: ["none"],
45
- [Directive.UPGRADE_INSECURE_REQUESTS]: null,
46
- [Directive.BLOCK_ALL_MIXED_CONTENT]: null,
47
- };
48
- const cspString = create(csp);
49
- assert.strictEqual(cspString, "default-src 'self'; script-src 'self' js.example.com; style-src 'self' css.example.com; img-src 'self' *.google.com *.google.com.au; connect-src 'self'; font-src 'self' font.example.com; object-src 'none'; media-src 'self' media.example.com; frame-src 'self'; sandbox allow-scripts; report-uri /my-report-uri; child-src 'self'; form-action 'self'; frame-ancestors 'none'; plugin-types application/pdf; base-uri 'self'; report-to myGroupName; worker-src 'none'; manifest-src 'none'; prefetch-src 'none'; navigate-to example.com; require-trusted-types-for script; trusted-types 'none'; upgrade-insecure-requests; block-all-mixed-content;");
50
- });
51
- it("Handles blank directives", () => {
52
- const csp = {
53
- [Directive.SANDBOX]: [],
54
- };
55
- const cspString = create(csp);
56
- assert.strictEqual(cspString, "sandbox;");
57
- });
58
- it("Ignores invalid directives", () => {
59
- const csp = {
60
- [Directive.DEFAULT_SRC]: ["self"],
61
- // @ts-expect-error deliberate testing of invalid directive
62
- ["invalid-directive"]: ["self"],
63
- [Directive.IMG_SRC]: ["my.domain.com"]
64
- };
65
- const cspString = create(csp);
66
- assert.strictEqual(cspString, "default-src 'self'; img-src my.domain.com;");
67
- });
68
- });
69
- });