@growth-labs/conformance 0.1.0 → 0.2.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/CHANGELOG.md +23 -0
- package/README.md +51 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/policy.d.ts +21 -2
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +48 -10
- package/dist/policy.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +512 -15
- package/dist/runner.js.map +1 -1
- package/dist/schema.d.ts +503 -12
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +42 -1
- package/dist/schema.js.map +1 -1
- package/fixtures/negative/required-negative-fixtures.json +131 -0
- package/fixtures/valid/public-article.json +56 -2
- package/package.json +1 -1
- package/src/index.ts +13 -0
- package/src/policy.ts +70 -11
- package/src/runner.ts +584 -14
- package/src/schema.ts +49 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @growth-labs/conformance Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-07-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added schema v2 security receipts for consumer-supplied enforced-CSP
|
|
8
|
+
functional probes and CSP reporting/canary evidence.
|
|
9
|
+
- Added required COOP/CORP response headers, conservative shared policy
|
|
10
|
+
defaults, and documented consumer overrides for verified auth/media/embed
|
|
11
|
+
constraints.
|
|
12
|
+
- Added required negative fixtures for missing COOP/CORP, HSTS below one year,
|
|
13
|
+
wildcard `default-src`, `unsafe-eval`, executable inline scripts without a
|
|
14
|
+
matching nonce or SHA-256 hash, conflicting duplicate security headers,
|
|
15
|
+
missing functional-probe receipts, and missing/failed CSP-report receipts.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- HSTS validation now requires `max-age >= 31536000`.
|
|
20
|
+
- `buildSharedPublicationHeaders()` now keeps `includeSubDomains` and `preload`
|
|
21
|
+
opt-in behind explicit host-inventory proof instead of emitting them by
|
|
22
|
+
default.
|
|
23
|
+
- CSP validation now parses directives instead of substring-matching the raw
|
|
24
|
+
header.
|
|
25
|
+
|
|
3
26
|
## 0.1.0 — 2026-07-15
|
|
4
27
|
|
|
5
28
|
### Added
|
package/README.md
CHANGED
|
@@ -30,7 +30,9 @@ const fixture = {
|
|
|
30
30
|
status: 200,
|
|
31
31
|
headers: {
|
|
32
32
|
'content-security-policy': "default-src 'self'; script-src 'self' 'nonce-runtime'",
|
|
33
|
-
'strict-transport-security': 'max-age=31536000
|
|
33
|
+
'strict-transport-security': 'max-age=31536000',
|
|
34
|
+
'cross-origin-opener-policy': 'same-origin',
|
|
35
|
+
'cross-origin-resource-policy': 'same-origin',
|
|
34
36
|
'x-frame-options': 'DENY',
|
|
35
37
|
'x-content-type-options': 'nosniff',
|
|
36
38
|
'referrer-policy': 'strict-origin-when-cross-origin',
|
|
@@ -53,9 +55,27 @@ const fixture = {
|
|
|
53
55
|
{ audience: 'public', cacheKey: 'fronts:public:/example' },
|
|
54
56
|
{ audience: 'crawler', cacheKey: 'fronts:crawler:/example' },
|
|
55
57
|
],
|
|
58
|
+
securityReceipts: {
|
|
59
|
+
functionalProbes: [
|
|
60
|
+
{ name: 'oauth', status: 'pass', evidence: 'sanitized consumer probe receipt' },
|
|
61
|
+
{ name: 'media', status: 'pass', evidence: 'sanitized consumer probe receipt' },
|
|
62
|
+
{ name: 'images', status: 'pass', evidence: 'sanitized consumer probe receipt' },
|
|
63
|
+
{ name: 'analytics', status: 'pass', evidence: 'sanitized consumer probe receipt' },
|
|
64
|
+
{ name: 'embeds', status: 'pass', evidence: 'sanitized consumer probe receipt' },
|
|
65
|
+
{ name: 'error-pages', status: 'pass', evidence: 'sanitized consumer probe receipt' },
|
|
66
|
+
],
|
|
67
|
+
cspReports: [
|
|
68
|
+
{
|
|
69
|
+
name: 'csp-report-canary',
|
|
70
|
+
status: 'pass',
|
|
71
|
+
endpoint: 'consumer-owned-report-endpoint',
|
|
72
|
+
evidence: 'sanitized CSP report or blocked-canary receipt',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
56
76
|
buildEvidence: {
|
|
57
77
|
commitSha: 'bd39d718f6a5b0df9a249e11638a8b430050941e',
|
|
58
|
-
packageVersions: { '@growth-labs/seo': '0.9.0' },
|
|
78
|
+
packageVersions: { '@growth-labs/conformance': '0.2.0', '@growth-labs/seo': '0.9.0' },
|
|
59
79
|
staticHeaderPolicy: true,
|
|
60
80
|
ssrHeaderPolicy: true,
|
|
61
81
|
},
|
|
@@ -77,16 +97,25 @@ const headers = buildSharedPublicationHeaders({
|
|
|
77
97
|
renderMode: 'ssr',
|
|
78
98
|
audience: 'public',
|
|
79
99
|
nonce: runtimeNonce,
|
|
100
|
+
cspReportUri: 'https://example.com/api/csp-report',
|
|
80
101
|
markdownTwinUrl: 'https://example.com/article.md',
|
|
81
102
|
})
|
|
82
103
|
```
|
|
83
104
|
|
|
84
105
|
The policy emits CSP, HSTS, frame protection, MIME sniffing protection,
|
|
85
|
-
referrer policy, permissions policy, cache semantics, and optional
|
|
86
|
-
twin `Link` metadata. Private audiences use `private, no-store`;
|
|
87
|
-
uses short shared-cache semantics; public static surfaces get longer
|
|
106
|
+
COOP, CORP, referrer policy, permissions policy, cache semantics, and optional
|
|
107
|
+
markdown twin `Link` metadata. Private audiences use `private, no-store`;
|
|
108
|
+
public SSR uses short shared-cache semantics; public static surfaces get longer
|
|
88
109
|
shared-cache semantics.
|
|
89
110
|
|
|
111
|
+
HSTS defaults to `max-age=31536000` only. Add `includeSubDomains` and `preload`
|
|
112
|
+
through `strictTransportSecurity: { includeSubDomains: true, preload: true }`
|
|
113
|
+
only after the consumer has separately verified the full host inventory can
|
|
114
|
+
enforce HTTPS. COOP defaults to `same-origin` and CORP defaults to
|
|
115
|
+
`same-origin`; consumers may override them only when verified OAuth popup,
|
|
116
|
+
media, or embed constraints require a looser policy and the receipt proves the
|
|
117
|
+
result under enforced CSP.
|
|
118
|
+
|
|
90
119
|
## Fixture Commands
|
|
91
120
|
|
|
92
121
|
The root repo exposes:
|
|
@@ -100,6 +129,16 @@ The fixture command proves the required negative fixtures fail loudly with
|
|
|
100
129
|
stable finding codes:
|
|
101
130
|
|
|
102
131
|
- `csp-unsafe-inline`
|
|
132
|
+
- `missing-coop`
|
|
133
|
+
- `missing-corp`
|
|
134
|
+
- `hsts-below-one-year`
|
|
135
|
+
- `csp-wildcard-default-src`
|
|
136
|
+
- `csp-unsafe-eval`
|
|
137
|
+
- `csp-inline-executable-without-nonce-or-hash`
|
|
138
|
+
- `conflicting-duplicate-security-header`
|
|
139
|
+
- `missing-functional-probe-receipt`
|
|
140
|
+
- `missing-csp-report-receipt`
|
|
141
|
+
- `failed-csp-report-receipt`
|
|
103
142
|
- `headers-static-only-while-ssr-missing`
|
|
104
143
|
- `undeclared-na`
|
|
105
144
|
- `stringified-is-accessible-for-free`
|
|
@@ -114,5 +153,11 @@ stable finding codes:
|
|
|
114
153
|
|
|
115
154
|
`runSiteConformance()` returns a `SiteEvidenceBundle`; `runFleetConformance()`
|
|
116
155
|
returns a `FleetEvidenceBundle`. Both are validated by exported Zod schemas
|
|
117
|
-
and carry `schemaVersion: "growth-labs.conformance.
|
|
156
|
+
and carry `schemaVersion: "growth-labs.conformance.v2"` so downstream jobs can
|
|
118
157
|
parse them without relying on console text.
|
|
158
|
+
|
|
159
|
+
Schema v2 adds `securityReceipts` to fixtures and site evidence bundles. Existing
|
|
160
|
+
v1 fixtures must add explicit functional-probe receipts for `oauth`, `media`,
|
|
161
|
+
`images`, `analytics`, `embeds`, and `error-pages`, plus at least one passing CSP
|
|
162
|
+
reporting/canary receipt. The package does not perform those live probes itself;
|
|
163
|
+
consumer CI supplies sanitized pass/fail/NA receipts.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildCacheControl, buildContentSecurityPolicy, buildSharedPublicationHeaders, type CachePolicyOptions, type HeaderPolicyOptions, isPrivateAudience, normalizeHeaders, REQUIRED_SECURITY_HEADERS, } from './policy.js';
|
|
1
|
+
export { buildCacheControl, buildContentSecurityPolicy, buildSharedPublicationHeaders, buildStrictTransportSecurity, type CachePolicyOptions, type ContentSecurityPolicyOptions, type CrossOriginOpenerPolicy, type CrossOriginResourcePolicy, collectHeaders, type HeaderPolicyOptions, isPrivateAudience, normalizeHeaders, REQUIRED_SECURITY_HEADERS, type StrictTransportSecurityOptions, } from './policy.js';
|
|
2
2
|
export { type RunConformanceOptions, runFleetConformance, runSiteConformance } from './runner.js';
|
|
3
|
-
export { type Audience, audienceSchema, type BuildEvidence, buildEvidenceSchema, type CacheVariant, type ConformanceCheckReceipt, type ConformanceFinding, type ConformanceFixture, type ConformanceFixtureInput, cacheVariantSchema, checkStatusSchema, conformanceCheckReceiptSchema, conformanceExpectedSchema, conformanceFindingSchema, conformanceFixtureSchema, EVIDENCE_SCHEMA_VERSION, type EvidenceSummary, evidenceSummarySchema, type FleetEvidenceBundle, findingSeveritySchema, fleetEvidenceBundleSchema, type HeaderMap, headerMapSchema, headerValueSchema, type PublicationSurfaceEvidence, parseConformanceFixture, publicationSurfaceSchema, REQUIRED_NEGATIVE_FIXTURE_CODES, type RenderMode, type RequiredNegativeFindingCode, type RequiredNegativeFixtureId, renderModeSchema, type SiteEvidenceBundle, type SurfaceResponse, siteEvidenceBundleSchema, surfaceResponseSchema, validateFleetEvidenceBundle, validateSiteEvidenceBundle, } from './schema.js';
|
|
3
|
+
export { type Audience, audienceSchema, type BuildEvidence, buildEvidenceSchema, type CacheVariant, type ConformanceCheckReceipt, type ConformanceFinding, type ConformanceFixture, type ConformanceFixtureInput, type CspReportReceipt, cacheVariantSchema, checkStatusSchema, conformanceCheckReceiptSchema, conformanceExpectedSchema, conformanceFindingSchema, conformanceFixtureSchema, cspReportReceiptSchema, EVIDENCE_SCHEMA_VERSION, type EvidenceSummary, evidenceSummarySchema, type FleetEvidenceBundle, findingSeveritySchema, fleetEvidenceBundleSchema, type HeaderMap, headerMapSchema, headerValueSchema, type PublicationSurfaceEvidence, parseConformanceFixture, publicationSurfaceSchema, REQUIRED_FUNCTIONAL_PROBE_NAMES, REQUIRED_NEGATIVE_FIXTURE_CODES, type RenderMode, type RequiredNegativeFindingCode, type RequiredNegativeFixtureId, renderModeSchema, type SecurityFunctionalProbeReceipt, type SecurityReceipts, type SiteEvidenceBundle, type SurfaceResponse, securityFunctionalProbeReceiptSchema, securityReceiptsSchema, siteEvidenceBundleSchema, surfaceResponseSchema, validateFleetEvidenceBundle, validateSiteEvidenceBundle, } from './schema.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,0BAA0B,EAC1B,6BAA6B,EAC7B,4BAA4B,EAC5B,KAAK,kBAAkB,EACvB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,yBAAyB,EAC9B,cAAc,EACd,KAAK,mBAAmB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,8BAA8B,GACnC,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,KAAK,qBAAqB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACjG,OAAO,EACN,KAAK,QAAQ,EACb,cAAc,EACd,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,6BAA6B,EAC7B,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,SAAS,EACd,eAAe,EACf,iBAAiB,EACjB,KAAK,0BAA0B,EAC/B,uBAAuB,EACvB,wBAAwB,EACxB,+BAA+B,EAC/B,+BAA+B,EAC/B,KAAK,UAAU,EACf,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,gBAAgB,EAChB,KAAK,8BAA8B,EACnC,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,oCAAoC,EACpC,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,2BAA2B,EAC3B,0BAA0B,GAC1B,MAAM,aAAa,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildCacheControl, buildContentSecurityPolicy, buildSharedPublicationHeaders, isPrivateAudience, normalizeHeaders, REQUIRED_SECURITY_HEADERS, } from './policy.js';
|
|
1
|
+
export { buildCacheControl, buildContentSecurityPolicy, buildSharedPublicationHeaders, buildStrictTransportSecurity, collectHeaders, isPrivateAudience, normalizeHeaders, REQUIRED_SECURITY_HEADERS, } from './policy.js';
|
|
2
2
|
export { runFleetConformance, runSiteConformance } from './runner.js';
|
|
3
|
-
export { audienceSchema, buildEvidenceSchema, cacheVariantSchema, checkStatusSchema, conformanceCheckReceiptSchema, conformanceExpectedSchema, conformanceFindingSchema, conformanceFixtureSchema, EVIDENCE_SCHEMA_VERSION, evidenceSummarySchema, findingSeveritySchema, fleetEvidenceBundleSchema, headerMapSchema, headerValueSchema, parseConformanceFixture, publicationSurfaceSchema, REQUIRED_NEGATIVE_FIXTURE_CODES, renderModeSchema, siteEvidenceBundleSchema, surfaceResponseSchema, validateFleetEvidenceBundle, validateSiteEvidenceBundle, } from './schema.js';
|
|
3
|
+
export { audienceSchema, buildEvidenceSchema, cacheVariantSchema, checkStatusSchema, conformanceCheckReceiptSchema, conformanceExpectedSchema, conformanceFindingSchema, conformanceFixtureSchema, cspReportReceiptSchema, EVIDENCE_SCHEMA_VERSION, evidenceSummarySchema, findingSeveritySchema, fleetEvidenceBundleSchema, headerMapSchema, headerValueSchema, parseConformanceFixture, publicationSurfaceSchema, REQUIRED_FUNCTIONAL_PROBE_NAMES, REQUIRED_NEGATIVE_FIXTURE_CODES, renderModeSchema, securityFunctionalProbeReceiptSchema, securityReceiptsSchema, siteEvidenceBundleSchema, surfaceResponseSchema, validateFleetEvidenceBundle, validateSiteEvidenceBundle, } from './schema.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,0BAA0B,EAC1B,6BAA6B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,0BAA0B,EAC1B,6BAA6B,EAC7B,4BAA4B,EAK5B,cAAc,EAEd,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,GAEzB,MAAM,aAAa,CAAA;AACpB,OAAO,EAA8B,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACjG,OAAO,EAEN,cAAc,EAEd,mBAAmB,EAOnB,kBAAkB,EAClB,iBAAiB,EACjB,6BAA6B,EAC7B,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EAEvB,qBAAqB,EAErB,qBAAqB,EACrB,yBAAyB,EAEzB,eAAe,EACf,iBAAiB,EAEjB,uBAAuB,EACvB,wBAAwB,EACxB,+BAA+B,EAC/B,+BAA+B,EAI/B,gBAAgB,EAKhB,oCAAoC,EACpC,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,2BAA2B,EAC3B,0BAA0B,GAC1B,MAAM,aAAa,CAAA"}
|
package/dist/policy.d.ts
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import type { Audience, HeaderMap, RenderMode } from './schema.js';
|
|
2
|
-
export declare const REQUIRED_SECURITY_HEADERS: readonly ["content-security-policy", "strict-transport-security", "x-frame-options", "x-content-type-options", "referrer-policy", "permissions-policy"];
|
|
2
|
+
export declare const REQUIRED_SECURITY_HEADERS: readonly ["content-security-policy", "strict-transport-security", "cross-origin-opener-policy", "cross-origin-resource-policy", "x-frame-options", "x-content-type-options", "referrer-policy", "permissions-policy"];
|
|
3
|
+
export type CrossOriginOpenerPolicy = 'same-origin' | 'same-origin-allow-popups' | 'unsafe-none';
|
|
4
|
+
export type CrossOriginResourcePolicy = 'same-origin' | 'same-site' | 'cross-origin';
|
|
5
|
+
export interface StrictTransportSecurityOptions {
|
|
6
|
+
maxAgeSeconds?: number;
|
|
7
|
+
includeSubDomains?: boolean;
|
|
8
|
+
preload?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ContentSecurityPolicyOptions {
|
|
11
|
+
nonce?: string;
|
|
12
|
+
reportUri?: string;
|
|
13
|
+
reportTo?: string;
|
|
14
|
+
}
|
|
3
15
|
export interface HeaderPolicyOptions {
|
|
4
16
|
renderMode: RenderMode;
|
|
5
17
|
audience: Audience;
|
|
6
18
|
nonce?: string;
|
|
19
|
+
cspReportUri?: string;
|
|
20
|
+
cspReportTo?: string;
|
|
21
|
+
strictTransportSecurity?: StrictTransportSecurityOptions;
|
|
22
|
+
crossOriginOpenerPolicy?: CrossOriginOpenerPolicy;
|
|
23
|
+
crossOriginResourcePolicy?: CrossOriginResourcePolicy;
|
|
7
24
|
markdownTwinUrl?: string;
|
|
8
25
|
}
|
|
9
26
|
export interface CachePolicyOptions {
|
|
@@ -11,8 +28,10 @@ export interface CachePolicyOptions {
|
|
|
11
28
|
audience: Audience;
|
|
12
29
|
}
|
|
13
30
|
export declare function buildSharedPublicationHeaders(options: HeaderPolicyOptions): Record<string, string>;
|
|
14
|
-
export declare function buildContentSecurityPolicy(
|
|
31
|
+
export declare function buildContentSecurityPolicy(input?: string | ContentSecurityPolicyOptions): string;
|
|
32
|
+
export declare function buildStrictTransportSecurity(options?: StrictTransportSecurityOptions): string;
|
|
15
33
|
export declare function buildCacheControl({ renderMode, audience }: CachePolicyOptions): string;
|
|
16
34
|
export declare function normalizeHeaders(headers: HeaderMap | undefined): Record<string, string>;
|
|
35
|
+
export declare function collectHeaders(headers: HeaderMap | undefined): Record<string, string[]>;
|
|
17
36
|
export declare function isPrivateAudience(audience: Audience): boolean;
|
|
18
37
|
//# sourceMappingURL=policy.d.ts.map
|
package/dist/policy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAElE,eAAO,MAAM,yBAAyB,
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAElE,eAAO,MAAM,yBAAyB,uNAS5B,CAAA;AAEV,MAAM,MAAM,uBAAuB,GAAG,aAAa,GAAG,0BAA0B,GAAG,aAAa,CAAA;AAChG,MAAM,MAAM,yBAAyB,GAAG,aAAa,GAAG,WAAW,GAAG,cAAc,CAAA;AAEpF,MAAM,WAAW,8BAA8B;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,mBAAmB;IACnC,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,QAAQ,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uBAAuB,CAAC,EAAE,8BAA8B,CAAA;IACxD,uBAAuB,CAAC,EAAE,uBAAuB,CAAA;IACjD,yBAAyB,CAAC,EAAE,yBAAyB,CAAA;IACrD,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,QAAQ,CAAA;CAClB;AAED,wBAAgB,6BAA6B,CAC5C,OAAO,EAAE,mBAAmB,GAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuBxB;AAED,wBAAgB,0BAA0B,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,4BAA4B,GAAG,MAAM,CAyBhG;AAED,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,8BAAmC,GAAG,MAAM,CAejG;AAED,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,MAAM,CAMtF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOvF;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAavF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAO7D"}
|
package/dist/policy.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const REQUIRED_SECURITY_HEADERS = [
|
|
2
2
|
'content-security-policy',
|
|
3
3
|
'strict-transport-security',
|
|
4
|
+
'cross-origin-opener-policy',
|
|
5
|
+
'cross-origin-resource-policy',
|
|
4
6
|
'x-frame-options',
|
|
5
7
|
'x-content-type-options',
|
|
6
8
|
'referrer-policy',
|
|
@@ -8,8 +10,14 @@ export const REQUIRED_SECURITY_HEADERS = [
|
|
|
8
10
|
];
|
|
9
11
|
export function buildSharedPublicationHeaders(options) {
|
|
10
12
|
const headers = {
|
|
11
|
-
'Content-Security-Policy': buildContentSecurityPolicy(
|
|
12
|
-
|
|
13
|
+
'Content-Security-Policy': buildContentSecurityPolicy({
|
|
14
|
+
nonce: options.nonce,
|
|
15
|
+
reportUri: options.cspReportUri,
|
|
16
|
+
reportTo: options.cspReportTo,
|
|
17
|
+
}),
|
|
18
|
+
'Strict-Transport-Security': buildStrictTransportSecurity(options.strictTransportSecurity),
|
|
19
|
+
'Cross-Origin-Opener-Policy': options.crossOriginOpenerPolicy ?? 'same-origin',
|
|
20
|
+
'Cross-Origin-Resource-Policy': options.crossOriginResourcePolicy ?? 'same-origin',
|
|
13
21
|
'X-Frame-Options': 'DENY',
|
|
14
22
|
'X-Content-Type-Options': 'nosniff',
|
|
15
23
|
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
|
@@ -21,14 +29,15 @@ export function buildSharedPublicationHeaders(options) {
|
|
|
21
29
|
}
|
|
22
30
|
return headers;
|
|
23
31
|
}
|
|
24
|
-
export function buildContentSecurityPolicy(
|
|
32
|
+
export function buildContentSecurityPolicy(input) {
|
|
33
|
+
const options = typeof input === 'string' ? { nonce: input } : (input ?? {});
|
|
25
34
|
const scriptSources = ["'self'"];
|
|
26
35
|
const styleSources = ["'self'"];
|
|
27
|
-
if (nonce) {
|
|
28
|
-
scriptSources.push(`'nonce-${nonce}'`);
|
|
29
|
-
styleSources.push(`'nonce-${nonce}'`);
|
|
36
|
+
if (options.nonce) {
|
|
37
|
+
scriptSources.push(`'nonce-${options.nonce}'`);
|
|
38
|
+
styleSources.push(`'nonce-${options.nonce}'`);
|
|
30
39
|
}
|
|
31
|
-
|
|
40
|
+
const directives = [
|
|
32
41
|
"default-src 'self'",
|
|
33
42
|
`script-src ${scriptSources.join(' ')}`,
|
|
34
43
|
`style-src ${styleSources.join(' ')}`,
|
|
@@ -39,7 +48,27 @@ export function buildContentSecurityPolicy(nonce) {
|
|
|
39
48
|
"base-uri 'self'",
|
|
40
49
|
"frame-ancestors 'none'",
|
|
41
50
|
'upgrade-insecure-requests',
|
|
42
|
-
]
|
|
51
|
+
];
|
|
52
|
+
if (options.reportTo)
|
|
53
|
+
directives.push(`report-to ${options.reportTo}`);
|
|
54
|
+
if (options.reportUri)
|
|
55
|
+
directives.push(`report-uri ${options.reportUri}`);
|
|
56
|
+
return directives.join('; ');
|
|
57
|
+
}
|
|
58
|
+
export function buildStrictTransportSecurity(options = {}) {
|
|
59
|
+
const maxAgeSeconds = options.maxAgeSeconds ?? 31536000;
|
|
60
|
+
if (!Number.isInteger(maxAgeSeconds) || maxAgeSeconds < 31536000) {
|
|
61
|
+
throw new Error('Strict-Transport-Security maxAgeSeconds must be at least 31536000');
|
|
62
|
+
}
|
|
63
|
+
if (options.preload === true && options.includeSubDomains !== true) {
|
|
64
|
+
throw new Error('Strict-Transport-Security preload requires includeSubDomains after host inventory proof');
|
|
65
|
+
}
|
|
66
|
+
const directives = [`max-age=${maxAgeSeconds}`];
|
|
67
|
+
if (options.includeSubDomains === true)
|
|
68
|
+
directives.push('includeSubDomains');
|
|
69
|
+
if (options.preload === true)
|
|
70
|
+
directives.push('preload');
|
|
71
|
+
return directives.join('; ');
|
|
43
72
|
}
|
|
44
73
|
export function buildCacheControl({ renderMode, audience }) {
|
|
45
74
|
if (isPrivateAudience(audience))
|
|
@@ -50,15 +79,24 @@ export function buildCacheControl({ renderMode, audience }) {
|
|
|
50
79
|
return 'public, max-age=0, s-maxage=300, stale-while-revalidate=86400';
|
|
51
80
|
}
|
|
52
81
|
export function normalizeHeaders(headers) {
|
|
82
|
+
const entries = collectHeaders(headers);
|
|
83
|
+
const normalized = {};
|
|
84
|
+
for (const [name, values] of Object.entries(entries)) {
|
|
85
|
+
normalized[name] = values.join(', ');
|
|
86
|
+
}
|
|
87
|
+
return normalized;
|
|
88
|
+
}
|
|
89
|
+
export function collectHeaders(headers) {
|
|
53
90
|
const normalized = {};
|
|
54
91
|
if (!headers)
|
|
55
92
|
return normalized;
|
|
56
93
|
for (const [name, value] of Object.entries(headers)) {
|
|
94
|
+
const normalizedName = name.toLowerCase();
|
|
57
95
|
if (typeof value === 'string') {
|
|
58
|
-
normalized[
|
|
96
|
+
normalized[normalizedName] = [...(normalized[normalizedName] ?? []), value];
|
|
59
97
|
}
|
|
60
98
|
else if (Array.isArray(value)) {
|
|
61
|
-
normalized[
|
|
99
|
+
normalized[normalizedName] = [...(normalized[normalizedName] ?? []), ...value];
|
|
62
100
|
}
|
|
63
101
|
}
|
|
64
102
|
return normalized;
|
package/dist/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,yBAAyB;IACzB,2BAA2B;IAC3B,iBAAiB;IACjB,wBAAwB;IACxB,iBAAiB;IACjB,oBAAoB;CACX,CAAA;
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAOA,OAAO,EAKN,KAAK,uBAAuB,EAG5B,KAAK,mBAAmB,EAGxB,KAAK,kBAAkB,EAEvB,MAAM,aAAa,CAAA;AAuBpB,MAAM,WAAW,qBAAqB;IACrC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAClB;AAED,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,uBAAuB,EAC9B,OAAO,GAAE,qBAA0B,GACjC,kBAAkB,CA+BpB;AAED,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,uBAAuB,EAAE,EACjC,OAAO,GAAE,qBAA0B,GACjC,mBAAmB,CAuBrB"}
|