@growth-labs/conformance 0.2.0 → 0.3.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 +64 -5
- 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 +33 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +91 -5
- package/dist/policy.js.map +1 -1
- package/dist/runner.js +74 -4
- package/dist/runner.js.map +1 -1
- package/dist/schema.d.ts +326 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +75 -0
- package/dist/schema.js.map +1 -1
- package/fixtures/negative/required-negative-fixtures.json +11 -0
- package/fixtures/valid/public-article.json +1 -1
- package/package.json +7 -1
- package/src/index.ts +6 -0
- package/src/policy.ts +138 -6
- package/src/runner.ts +93 -4
- package/src/schema.ts +83 -0
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,uBAAuB,+BAA+B,CAAA;AACnE,eAAO,MAAM,+BAA+B,6EAOlC,CAAA;AAEV,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -16,6 +16,7 @@ export const REQUIRED_NEGATIVE_FIXTURE_CODES = {
|
|
|
16
16
|
'csp-wildcard-default-src': 'security.csp.default_src_wildcard',
|
|
17
17
|
'csp-unsafe-eval': 'security.csp.unsafe_eval',
|
|
18
18
|
'csp-inline-executable-without-nonce-or-hash': 'security.csp.inline_executable_without_nonce_or_hash',
|
|
19
|
+
'csp-style-attr-inline-without-inventory': 'security.csp.style_attr_inline_inventory_missing',
|
|
19
20
|
'conflicting-duplicate-security-header': 'security.header.conflicting_values',
|
|
20
21
|
'missing-functional-probe-receipt': 'security.functional_probe.missing',
|
|
21
22
|
'missing-csp-report-receipt': 'security.csp_report.missing',
|
|
@@ -83,10 +84,19 @@ export const cspReportReceiptSchema = z.object({
|
|
|
83
84
|
endpoint: z.string().min(1).optional(),
|
|
84
85
|
capabilityBoundary: z.string().min(1).optional(),
|
|
85
86
|
});
|
|
87
|
+
export const inlineStyleAttributeInventorySchema = z.object({
|
|
88
|
+
commitSha: z.string().regex(/^[0-9a-f]{40}$/),
|
|
89
|
+
occurrenceCount: z.number().int().positive(),
|
|
90
|
+
evidence: z.object({
|
|
91
|
+
tool: z.string().regex(/^[a-z0-9][a-z0-9:@/._-]*$/i),
|
|
92
|
+
outputHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
86
95
|
export const securityReceiptsSchema = z
|
|
87
96
|
.object({
|
|
88
97
|
functionalProbes: z.array(securityFunctionalProbeReceiptSchema).default([]),
|
|
89
98
|
cspReports: z.array(cspReportReceiptSchema).default([]),
|
|
99
|
+
inlineStyleAttributes: inlineStyleAttributeInventorySchema.optional(),
|
|
90
100
|
})
|
|
91
101
|
.default({ functionalProbes: [], cspReports: [] });
|
|
92
102
|
export const buildEvidenceSchema = z.object({
|
|
@@ -97,10 +107,75 @@ export const buildEvidenceSchema = z.object({
|
|
|
97
107
|
staticHeaderPolicy: z.boolean().default(false),
|
|
98
108
|
ssrHeaderPolicy: z.boolean().default(false),
|
|
99
109
|
});
|
|
110
|
+
const frameAncestorSourceSchema = z.string().superRefine((source, context) => {
|
|
111
|
+
if (source === "'none'" || source === "'self'" || source === 'https:')
|
|
112
|
+
return;
|
|
113
|
+
let parsed;
|
|
114
|
+
try {
|
|
115
|
+
parsed = new URL(source);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
context.addIssue({
|
|
119
|
+
code: z.ZodIssueCode.custom,
|
|
120
|
+
message: "Expected 'none', 'self', https:, or an exact HTTPS origin.",
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (parsed.protocol !== 'https:' ||
|
|
125
|
+
parsed.username !== '' ||
|
|
126
|
+
parsed.password !== '' ||
|
|
127
|
+
parsed.search !== '' ||
|
|
128
|
+
parsed.hash !== '' ||
|
|
129
|
+
source !== parsed.origin) {
|
|
130
|
+
context.addIssue({
|
|
131
|
+
code: z.ZodIssueCode.custom,
|
|
132
|
+
message: "Expected 'none', 'self', https:, or an exact HTTPS origin.",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
const framePolicySchema = z
|
|
137
|
+
.object({
|
|
138
|
+
ancestors: z
|
|
139
|
+
.array(frameAncestorSourceSchema)
|
|
140
|
+
.min(1)
|
|
141
|
+
.refine((sources) => new Set(sources).size === sources.length, {
|
|
142
|
+
message: 'Frame ancestor sources must be unique.',
|
|
143
|
+
}),
|
|
144
|
+
legacyHeader: z.enum(['DENY', 'SAMEORIGIN', 'omit']),
|
|
145
|
+
})
|
|
146
|
+
.superRefine((policy, context) => {
|
|
147
|
+
if (policy.ancestors.includes("'none'") && policy.ancestors.length !== 1) {
|
|
148
|
+
context.addIssue({
|
|
149
|
+
code: z.ZodIssueCode.custom,
|
|
150
|
+
path: ['ancestors'],
|
|
151
|
+
message: "The 'none' frame ancestor cannot be combined with other sources.",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (policy.legacyHeader === 'DENY' &&
|
|
155
|
+
(policy.ancestors.length !== 1 || policy.ancestors[0] !== "'none'")) {
|
|
156
|
+
context.addIssue({
|
|
157
|
+
code: z.ZodIssueCode.custom,
|
|
158
|
+
path: ['legacyHeader'],
|
|
159
|
+
message: "DENY is compatible only with frame-ancestors 'none'.",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (policy.legacyHeader === 'SAMEORIGIN' &&
|
|
163
|
+
(policy.ancestors.length !== 1 || policy.ancestors[0] !== "'self'")) {
|
|
164
|
+
context.addIssue({
|
|
165
|
+
code: z.ZodIssueCode.custom,
|
|
166
|
+
path: ['legacyHeader'],
|
|
167
|
+
message: "SAMEORIGIN is compatible only with frame-ancestors 'self'.",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
100
171
|
export const conformanceExpectedSchema = z.object({
|
|
101
172
|
canonicalUrl: z.string().url().optional(),
|
|
102
173
|
schemaTypes: z.array(z.string().min(1)).default([]),
|
|
103
174
|
discoverEligible: z.boolean().default(true),
|
|
175
|
+
framePolicy: framePolicySchema.default({
|
|
176
|
+
ancestors: ["'none'"],
|
|
177
|
+
legacyHeader: 'DENY',
|
|
178
|
+
}),
|
|
104
179
|
});
|
|
105
180
|
export const conformanceFixtureSchema = z.object({
|
|
106
181
|
id: z.string().min(1),
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -93,6 +93,17 @@
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
|
+
{
|
|
97
|
+
"name": "csp-style-attr-inline-without-inventory",
|
|
98
|
+
"expectedFailingCode": "security.csp.style_attr_inline_inventory_missing",
|
|
99
|
+
"patch": {
|
|
100
|
+
"response": {
|
|
101
|
+
"headers": {
|
|
102
|
+
"content-security-policy": "default-src 'self'; script-src 'self' 'nonce-fixture'; style-src 'self' 'nonce-fixture'; style-src-attr 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
96
107
|
{
|
|
97
108
|
"name": "conflicting-duplicate-security-header",
|
|
98
109
|
"expectedFailingCode": "security.header.conflicting_values",
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"artifactId": "fixture-build-001",
|
|
122
122
|
"generatedAt": "2026-07-15T23:30:00Z",
|
|
123
123
|
"packageVersions": {
|
|
124
|
-
"@growth-labs/conformance": "0.
|
|
124
|
+
"@growth-labs/conformance": "0.3.0",
|
|
125
125
|
"@growth-labs/seo": "0.9.0"
|
|
126
126
|
},
|
|
127
127
|
"staticHeaderPolicy": true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growth-labs/conformance",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Reusable fleet publication conformance harness for Growth Labs sites: headers, cache, metadata, structured data, images, publication surfaces, and evidence bundles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -32,9 +32,15 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"zod": "^3.25.0"
|
|
34
34
|
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"astro": "^7.1.0"
|
|
37
|
+
},
|
|
35
38
|
"devDependencies": {
|
|
36
39
|
"@cloudflare/workers-types": "^4.20260402.1",
|
|
40
|
+
"@types/node": "^25.5.0",
|
|
41
|
+
"astro": "7.1.0",
|
|
37
42
|
"typescript": "^5.7.0",
|
|
43
|
+
"vite": "7.3.5",
|
|
38
44
|
"vitest": "^4.1.8"
|
|
39
45
|
},
|
|
40
46
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export {
|
|
2
|
+
type AstroCspConfig,
|
|
3
|
+
type AstroCspPolicyOptions,
|
|
4
|
+
type AstroCspResource,
|
|
5
|
+
buildAstroCspConfig,
|
|
2
6
|
buildCacheControl,
|
|
3
7
|
buildContentSecurityPolicy,
|
|
4
8
|
buildSharedPublicationHeaders,
|
|
@@ -42,6 +46,8 @@ export {
|
|
|
42
46
|
type HeaderMap,
|
|
43
47
|
headerMapSchema,
|
|
44
48
|
headerValueSchema,
|
|
49
|
+
type InlineStyleAttributeInventory,
|
|
50
|
+
inlineStyleAttributeInventorySchema,
|
|
45
51
|
type PublicationSurfaceEvidence,
|
|
46
52
|
parseConformanceFixture,
|
|
47
53
|
publicationSurfaceSchema,
|
package/src/policy.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AstroConfig } from 'astro'
|
|
2
|
+
import {
|
|
3
|
+
type Audience,
|
|
4
|
+
type HeaderMap,
|
|
5
|
+
type InlineStyleAttributeInventory,
|
|
6
|
+
inlineStyleAttributeInventorySchema,
|
|
7
|
+
type RenderMode,
|
|
8
|
+
} from './schema.js'
|
|
2
9
|
|
|
3
10
|
export const REQUIRED_SECURITY_HEADERS = [
|
|
4
11
|
'content-security-policy',
|
|
@@ -32,12 +39,42 @@ export interface HeaderPolicyOptions {
|
|
|
32
39
|
nonce?: string
|
|
33
40
|
cspReportUri?: string
|
|
34
41
|
cspReportTo?: string
|
|
42
|
+
contentSecurityPolicy?: false | ContentSecurityPolicyOptions
|
|
35
43
|
strictTransportSecurity?: StrictTransportSecurityOptions
|
|
36
44
|
crossOriginOpenerPolicy?: CrossOriginOpenerPolicy
|
|
37
45
|
crossOriginResourcePolicy?: CrossOriginResourcePolicy
|
|
38
46
|
markdownTwinUrl?: string
|
|
39
47
|
}
|
|
40
48
|
|
|
49
|
+
export interface AstroCspPolicyOptions {
|
|
50
|
+
scriptSources?: readonly string[]
|
|
51
|
+
styleSources?: readonly string[]
|
|
52
|
+
inlineStyleAttributes?: InlineStyleAttributeInventory
|
|
53
|
+
imgSources?: readonly string[]
|
|
54
|
+
fontSources?: readonly string[]
|
|
55
|
+
connectSources?: readonly string[]
|
|
56
|
+
mediaSources?: readonly string[]
|
|
57
|
+
frameSources?: readonly string[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type AstroNativeCspConfig = Exclude<
|
|
61
|
+
NonNullable<NonNullable<AstroConfig['security']>['csp']>,
|
|
62
|
+
boolean
|
|
63
|
+
>
|
|
64
|
+
|
|
65
|
+
type AstroCspDirective = NonNullable<AstroNativeCspConfig['directives']>[number]
|
|
66
|
+
|
|
67
|
+
export type AstroCspResource = NonNullable<
|
|
68
|
+
NonNullable<AstroNativeCspConfig['styleDirective']>['resources']
|
|
69
|
+
>[number]
|
|
70
|
+
|
|
71
|
+
export interface AstroCspConfig {
|
|
72
|
+
algorithm: 'SHA-256'
|
|
73
|
+
directives: AstroCspDirective[]
|
|
74
|
+
scriptDirective: { resources: string[] }
|
|
75
|
+
styleDirective: { resources: AstroCspResource[] }
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
export interface CachePolicyOptions {
|
|
42
79
|
renderMode: RenderMode
|
|
43
80
|
audience: Audience
|
|
@@ -47,11 +84,6 @@ export function buildSharedPublicationHeaders(
|
|
|
47
84
|
options: HeaderPolicyOptions,
|
|
48
85
|
): Record<string, string> {
|
|
49
86
|
const headers: Record<string, string> = {
|
|
50
|
-
'Content-Security-Policy': buildContentSecurityPolicy({
|
|
51
|
-
nonce: options.nonce,
|
|
52
|
-
reportUri: options.cspReportUri,
|
|
53
|
-
reportTo: options.cspReportTo,
|
|
54
|
-
}),
|
|
55
87
|
'Strict-Transport-Security': buildStrictTransportSecurity(options.strictTransportSecurity),
|
|
56
88
|
'Cross-Origin-Opener-Policy': options.crossOriginOpenerPolicy ?? 'same-origin',
|
|
57
89
|
'Cross-Origin-Resource-Policy': options.crossOriginResourcePolicy ?? 'same-origin',
|
|
@@ -62,6 +94,15 @@ export function buildSharedPublicationHeaders(
|
|
|
62
94
|
'accelerometer=(), camera=(), geolocation=(), gyroscope=(), microphone=(), payment=(), usb=()',
|
|
63
95
|
'Cache-Control': buildCacheControl(options),
|
|
64
96
|
}
|
|
97
|
+
if (options.contentSecurityPolicy !== false) {
|
|
98
|
+
headers['Content-Security-Policy'] = buildContentSecurityPolicy(
|
|
99
|
+
options.contentSecurityPolicy ?? {
|
|
100
|
+
nonce: options.nonce,
|
|
101
|
+
reportUri: options.cspReportUri,
|
|
102
|
+
reportTo: options.cspReportTo,
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
}
|
|
65
106
|
|
|
66
107
|
if (options.markdownTwinUrl) {
|
|
67
108
|
headers.Link = `<${options.markdownTwinUrl}>; rel="alternate"; type="text/markdown"`
|
|
@@ -70,6 +111,97 @@ export function buildSharedPublicationHeaders(
|
|
|
70
111
|
return headers
|
|
71
112
|
}
|
|
72
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Build framework-owned CSP configuration for Astro 7.1+.
|
|
116
|
+
* Astro hashes every script and style it emits, while this helper keeps the
|
|
117
|
+
* consumer's external source inventory exact and fail-closed.
|
|
118
|
+
*/
|
|
119
|
+
export function buildAstroCspConfig(options: AstroCspPolicyOptions = {}): AstroCspConfig {
|
|
120
|
+
const inlineStyleAttributes = options.inlineStyleAttributes
|
|
121
|
+
? inlineStyleAttributeInventorySchema.parse(options.inlineStyleAttributes)
|
|
122
|
+
: undefined
|
|
123
|
+
const scriptSources = exactCspSources(options.scriptSources)
|
|
124
|
+
const styleSources = exactCspSources(options.styleSources)
|
|
125
|
+
const imgSources = exactCspSources(options.imgSources)
|
|
126
|
+
const fontSources = exactCspSources(options.fontSources)
|
|
127
|
+
const connectSources = exactCspSources(options.connectSources)
|
|
128
|
+
const mediaSources = exactCspSources(options.mediaSources)
|
|
129
|
+
const frameSources = exactCspSources(options.frameSources)
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
algorithm: 'SHA-256',
|
|
133
|
+
directives: [
|
|
134
|
+
"default-src 'self'",
|
|
135
|
+
cspDirective('img-src', ["'self'", 'data:', 'blob:', ...imgSources]),
|
|
136
|
+
cspDirective('font-src', ["'self'", 'data:', ...fontSources]),
|
|
137
|
+
cspDirective('connect-src', ["'self'", ...connectSources]),
|
|
138
|
+
cspDirective('media-src', ["'self'", 'blob:', ...mediaSources]),
|
|
139
|
+
cspDirective('frame-src', ["'self'", ...frameSources]),
|
|
140
|
+
"object-src 'none'",
|
|
141
|
+
"base-uri 'self'",
|
|
142
|
+
"frame-ancestors 'none'",
|
|
143
|
+
"form-action 'self'",
|
|
144
|
+
'upgrade-insecure-requests',
|
|
145
|
+
],
|
|
146
|
+
scriptDirective: {
|
|
147
|
+
resources: uniqueCspSources(["'self'", ...scriptSources]),
|
|
148
|
+
},
|
|
149
|
+
styleDirective: {
|
|
150
|
+
resources: inlineStyleAttributes
|
|
151
|
+
? [
|
|
152
|
+
...uniqueCspSources(["'self'", ...styleSources]).map((resource) => ({
|
|
153
|
+
resource,
|
|
154
|
+
kind: 'element' as const,
|
|
155
|
+
})),
|
|
156
|
+
{ resource: "'unsafe-inline'", kind: 'attribute' as const },
|
|
157
|
+
]
|
|
158
|
+
: uniqueCspSources(["'self'", ...styleSources]),
|
|
159
|
+
},
|
|
160
|
+
} satisfies AstroNativeCspConfig
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function uniqueCspSources(sources: readonly string[]): string[] {
|
|
164
|
+
return [...new Set(sources)]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function cspDirective(name: string, sources: readonly string[]): AstroCspDirective {
|
|
168
|
+
return `${name} ${[...new Set(sources)].join(' ')}` as AstroCspDirective
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function exactCspSources(sources: readonly string[] | undefined): string[] {
|
|
172
|
+
return [...new Set(sources ?? [])].map((source) => {
|
|
173
|
+
const normalized = source.trim()
|
|
174
|
+
if (normalized === "'self'") return normalized
|
|
175
|
+
if (
|
|
176
|
+
normalized === '' ||
|
|
177
|
+
normalized.includes('*') ||
|
|
178
|
+
normalized === 'https:' ||
|
|
179
|
+
normalized === 'http:' ||
|
|
180
|
+
normalized.startsWith("'unsafe-")
|
|
181
|
+
) {
|
|
182
|
+
throw new Error(`Astro CSP sources must be exact and cannot use ${source}`)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let parsed: URL
|
|
186
|
+
try {
|
|
187
|
+
parsed = new URL(normalized)
|
|
188
|
+
} catch {
|
|
189
|
+
throw new Error(`Astro CSP sources must be exact HTTPS/WSS origins: ${source}`)
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
(parsed.protocol !== 'https:' && parsed.protocol !== 'wss:') ||
|
|
193
|
+
parsed.username !== '' ||
|
|
194
|
+
parsed.password !== '' ||
|
|
195
|
+
parsed.search !== '' ||
|
|
196
|
+
parsed.hash !== '' ||
|
|
197
|
+
(parsed.pathname !== '' && parsed.pathname !== '/')
|
|
198
|
+
) {
|
|
199
|
+
throw new Error(`Astro CSP sources must be exact HTTPS/WSS origins: ${source}`)
|
|
200
|
+
}
|
|
201
|
+
return parsed.origin
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
73
205
|
export function buildContentSecurityPolicy(input?: string | ContentSecurityPolicyOptions): string {
|
|
74
206
|
const options = typeof input === 'string' ? { nonce: input } : (input ?? {})
|
|
75
207
|
const scriptSources = ["'self'"]
|
package/src/runner.ts
CHANGED
|
@@ -113,7 +113,10 @@ function checkSecurityHeaders(fixture: ConformanceFixture, html: string): Confor
|
|
|
113
113
|
const headerEntries = collectHeaders(fixture.response.headers)
|
|
114
114
|
const headers = normalizeHeaders(fixture.response.headers)
|
|
115
115
|
const findings: ConformanceFinding[] = []
|
|
116
|
-
const
|
|
116
|
+
const requiredSecurityHeaders = REQUIRED_SECURITY_HEADERS.filter(
|
|
117
|
+
(name) => name !== 'x-frame-options' || fixture.expected.framePolicy.legacyHeader !== 'omit',
|
|
118
|
+
)
|
|
119
|
+
const missingSecurityHeaders = requiredSecurityHeaders.filter((name) => {
|
|
117
120
|
const values = headerEntries[name] ?? []
|
|
118
121
|
return !values.some((value) => value.trim().length > 0)
|
|
119
122
|
})
|
|
@@ -161,7 +164,33 @@ function checkSecurityHeaders(fixture: ConformanceFixture, html: string): Confor
|
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
const csp = headers['content-security-policy']
|
|
164
|
-
if (csp) findings.push(...validateContentSecurityPolicy(csp, html))
|
|
167
|
+
if (csp) findings.push(...validateContentSecurityPolicy(csp, html, fixture))
|
|
168
|
+
|
|
169
|
+
const xFrameOptions = headers['x-frame-options']
|
|
170
|
+
const expectedLegacyFrameHeader = fixture.expected.framePolicy.legacyHeader
|
|
171
|
+
if (expectedLegacyFrameHeader === 'omit' && xFrameOptions) {
|
|
172
|
+
findings.push({
|
|
173
|
+
code: 'security.frame_legacy_conflict',
|
|
174
|
+
severity: 'error',
|
|
175
|
+
message: 'X-Frame-Options conflicts with the declared embeddable frame-ancestors policy.',
|
|
176
|
+
path: 'response.headers.x-frame-options',
|
|
177
|
+
evidence: xFrameOptions,
|
|
178
|
+
remediation:
|
|
179
|
+
'Omit the legacy header only on the explicitly declared embeddable route and retain the exact CSP frame-ancestors directive.',
|
|
180
|
+
})
|
|
181
|
+
} else if (
|
|
182
|
+
expectedLegacyFrameHeader !== 'omit' &&
|
|
183
|
+
xFrameOptions &&
|
|
184
|
+
xFrameOptions.toUpperCase() !== expectedLegacyFrameHeader
|
|
185
|
+
) {
|
|
186
|
+
findings.push({
|
|
187
|
+
code: 'security.header.invalid',
|
|
188
|
+
severity: 'error',
|
|
189
|
+
message: `X-Frame-Options must be ${expectedLegacyFrameHeader} for this response class.`,
|
|
190
|
+
path: 'response.headers.x-frame-options',
|
|
191
|
+
evidence: xFrameOptions,
|
|
192
|
+
})
|
|
193
|
+
}
|
|
165
194
|
|
|
166
195
|
const coop = headers['cross-origin-opener-policy']
|
|
167
196
|
if (coop && !VALID_COOP_VALUES.has(coop.toLowerCase())) {
|
|
@@ -387,10 +416,37 @@ interface InlineExecutableScript {
|
|
|
387
416
|
path: string
|
|
388
417
|
}
|
|
389
418
|
|
|
390
|
-
function validateContentSecurityPolicy(
|
|
419
|
+
function validateContentSecurityPolicy(
|
|
420
|
+
csp: string,
|
|
421
|
+
html: string,
|
|
422
|
+
fixture: ConformanceFixture,
|
|
423
|
+
): ConformanceFinding[] {
|
|
391
424
|
const findings: ConformanceFinding[] = []
|
|
392
425
|
const { directives, findings: parseFindings } = parseContentSecurityPolicy(csp)
|
|
393
426
|
findings.push(...parseFindings)
|
|
427
|
+
const frameAncestors = directives.get('frame-ancestors')
|
|
428
|
+
const expectedFrameAncestors = fixture.expected.framePolicy.ancestors
|
|
429
|
+
if (!frameAncestors) {
|
|
430
|
+
findings.push({
|
|
431
|
+
code: 'security.csp.frame_ancestors_missing',
|
|
432
|
+
severity: 'error',
|
|
433
|
+
message: 'Content-Security-Policy must declare frame-ancestors.',
|
|
434
|
+
path: 'response.headers.content-security-policy.frame-ancestors',
|
|
435
|
+
})
|
|
436
|
+
} else if (
|
|
437
|
+
normalizeCspSources(frameAncestors.sources) !== normalizeCspSources(expectedFrameAncestors)
|
|
438
|
+
) {
|
|
439
|
+
findings.push({
|
|
440
|
+
code: 'security.csp.frame_ancestors_mismatch',
|
|
441
|
+
severity: 'error',
|
|
442
|
+
message:
|
|
443
|
+
'Content-Security-Policy frame-ancestors does not match the declared response class.',
|
|
444
|
+
path: 'response.headers.content-security-policy.frame-ancestors',
|
|
445
|
+
evidence: frameAncestors.raw,
|
|
446
|
+
remediation:
|
|
447
|
+
'Emit only the exact declared frame ancestors and keep embeddable routes explicitly bounded.',
|
|
448
|
+
})
|
|
449
|
+
}
|
|
394
450
|
|
|
395
451
|
const defaultSources = lowerSources(directives.get('default-src')?.sources ?? [])
|
|
396
452
|
if (defaultSources.includes('*')) {
|
|
@@ -425,6 +481,39 @@ function validateContentSecurityPolicy(csp: string, html: string): ConformanceFi
|
|
|
425
481
|
const directive = directives.get(directiveName)
|
|
426
482
|
if (!directive) continue
|
|
427
483
|
if (lowerSources(directive.sources).includes("'unsafe-inline'")) {
|
|
484
|
+
if (directiveName === 'style-src-attr') {
|
|
485
|
+
const inventory = fixture.securityReceipts.inlineStyleAttributes
|
|
486
|
+
if (!inventory) {
|
|
487
|
+
findings.push({
|
|
488
|
+
code: 'security.csp.style_attr_inline_inventory_missing',
|
|
489
|
+
severity: 'error',
|
|
490
|
+
message:
|
|
491
|
+
"style-src-attr 'unsafe-inline' requires a commit-bound inline-style-attribute inventory receipt.",
|
|
492
|
+
path: 'securityReceipts.inlineStyleAttributes',
|
|
493
|
+
evidence: directive.raw,
|
|
494
|
+
remediation:
|
|
495
|
+
'Remove the allowance or attach the exact consumer commit SHA, positive occurrence count, and scan evidence.',
|
|
496
|
+
})
|
|
497
|
+
continue
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (
|
|
501
|
+
!fixture.buildEvidence?.commitSha ||
|
|
502
|
+
fixture.buildEvidence.commitSha !== inventory.commitSha
|
|
503
|
+
) {
|
|
504
|
+
findings.push({
|
|
505
|
+
code: 'security.csp.style_attr_inline_inventory_stale',
|
|
506
|
+
severity: 'error',
|
|
507
|
+
message:
|
|
508
|
+
'The inline-style-attribute inventory must match the exact commit in build evidence.',
|
|
509
|
+
path: 'securityReceipts.inlineStyleAttributes.commitSha',
|
|
510
|
+
evidence: `inventory=${inventory.commitSha}; build=${fixture.buildEvidence?.commitSha ?? 'missing'}`,
|
|
511
|
+
remediation:
|
|
512
|
+
'Rescan the exact build commit before retaining the style attribute allowance.',
|
|
513
|
+
})
|
|
514
|
+
}
|
|
515
|
+
continue
|
|
516
|
+
}
|
|
428
517
|
findings.push({
|
|
429
518
|
code: 'security.csp.unsafe_inline',
|
|
430
519
|
severity: 'error',
|
|
@@ -535,7 +624,7 @@ function inlineExecutableScriptAllowed(script: InlineExecutableScript, sources:
|
|
|
535
624
|
}
|
|
536
625
|
|
|
537
626
|
function hashSourceMatchesInlineScript(source: string, content: string): boolean {
|
|
538
|
-
const match = /^'(sha256|sha384|sha512)-([^']+)'
|
|
627
|
+
const match = /^'(sha256|sha384|sha512)-([^']+)'$/.exec(source)
|
|
539
628
|
if (!match) return false
|
|
540
629
|
const algorithm = match[1]?.toLowerCase()
|
|
541
630
|
const expected = match[2]
|
package/src/schema.ts
CHANGED
|
@@ -19,6 +19,7 @@ export const REQUIRED_NEGATIVE_FIXTURE_CODES = {
|
|
|
19
19
|
'csp-unsafe-eval': 'security.csp.unsafe_eval',
|
|
20
20
|
'csp-inline-executable-without-nonce-or-hash':
|
|
21
21
|
'security.csp.inline_executable_without_nonce_or_hash',
|
|
22
|
+
'csp-style-attr-inline-without-inventory': 'security.csp.style_attr_inline_inventory_missing',
|
|
22
23
|
'conflicting-duplicate-security-header': 'security.header.conflicting_values',
|
|
23
24
|
'missing-functional-probe-receipt': 'security.functional_probe.missing',
|
|
24
25
|
'missing-csp-report-receipt': 'security.csp_report.missing',
|
|
@@ -98,10 +99,20 @@ export const cspReportReceiptSchema = z.object({
|
|
|
98
99
|
capabilityBoundary: z.string().min(1).optional(),
|
|
99
100
|
})
|
|
100
101
|
|
|
102
|
+
export const inlineStyleAttributeInventorySchema = z.object({
|
|
103
|
+
commitSha: z.string().regex(/^[0-9a-f]{40}$/),
|
|
104
|
+
occurrenceCount: z.number().int().positive(),
|
|
105
|
+
evidence: z.object({
|
|
106
|
+
tool: z.string().regex(/^[a-z0-9][a-z0-9:@/._-]*$/i),
|
|
107
|
+
outputHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
108
|
+
}),
|
|
109
|
+
})
|
|
110
|
+
|
|
101
111
|
export const securityReceiptsSchema = z
|
|
102
112
|
.object({
|
|
103
113
|
functionalProbes: z.array(securityFunctionalProbeReceiptSchema).default([]),
|
|
104
114
|
cspReports: z.array(cspReportReceiptSchema).default([]),
|
|
115
|
+
inlineStyleAttributes: inlineStyleAttributeInventorySchema.optional(),
|
|
105
116
|
})
|
|
106
117
|
.default({ functionalProbes: [], cspReports: [] })
|
|
107
118
|
|
|
@@ -114,10 +125,81 @@ export const buildEvidenceSchema = z.object({
|
|
|
114
125
|
ssrHeaderPolicy: z.boolean().default(false),
|
|
115
126
|
})
|
|
116
127
|
|
|
128
|
+
const frameAncestorSourceSchema = z.string().superRefine((source, context) => {
|
|
129
|
+
if (source === "'none'" || source === "'self'" || source === 'https:') return
|
|
130
|
+
let parsed: URL
|
|
131
|
+
try {
|
|
132
|
+
parsed = new URL(source)
|
|
133
|
+
} catch {
|
|
134
|
+
context.addIssue({
|
|
135
|
+
code: z.ZodIssueCode.custom,
|
|
136
|
+
message: "Expected 'none', 'self', https:, or an exact HTTPS origin.",
|
|
137
|
+
})
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
if (
|
|
141
|
+
parsed.protocol !== 'https:' ||
|
|
142
|
+
parsed.username !== '' ||
|
|
143
|
+
parsed.password !== '' ||
|
|
144
|
+
parsed.search !== '' ||
|
|
145
|
+
parsed.hash !== '' ||
|
|
146
|
+
source !== parsed.origin
|
|
147
|
+
) {
|
|
148
|
+
context.addIssue({
|
|
149
|
+
code: z.ZodIssueCode.custom,
|
|
150
|
+
message: "Expected 'none', 'self', https:, or an exact HTTPS origin.",
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
const framePolicySchema = z
|
|
156
|
+
.object({
|
|
157
|
+
ancestors: z
|
|
158
|
+
.array(frameAncestorSourceSchema)
|
|
159
|
+
.min(1)
|
|
160
|
+
.refine((sources) => new Set(sources).size === sources.length, {
|
|
161
|
+
message: 'Frame ancestor sources must be unique.',
|
|
162
|
+
}),
|
|
163
|
+
legacyHeader: z.enum(['DENY', 'SAMEORIGIN', 'omit']),
|
|
164
|
+
})
|
|
165
|
+
.superRefine((policy, context) => {
|
|
166
|
+
if (policy.ancestors.includes("'none'") && policy.ancestors.length !== 1) {
|
|
167
|
+
context.addIssue({
|
|
168
|
+
code: z.ZodIssueCode.custom,
|
|
169
|
+
path: ['ancestors'],
|
|
170
|
+
message: "The 'none' frame ancestor cannot be combined with other sources.",
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
if (
|
|
174
|
+
policy.legacyHeader === 'DENY' &&
|
|
175
|
+
(policy.ancestors.length !== 1 || policy.ancestors[0] !== "'none'")
|
|
176
|
+
) {
|
|
177
|
+
context.addIssue({
|
|
178
|
+
code: z.ZodIssueCode.custom,
|
|
179
|
+
path: ['legacyHeader'],
|
|
180
|
+
message: "DENY is compatible only with frame-ancestors 'none'.",
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
if (
|
|
184
|
+
policy.legacyHeader === 'SAMEORIGIN' &&
|
|
185
|
+
(policy.ancestors.length !== 1 || policy.ancestors[0] !== "'self'")
|
|
186
|
+
) {
|
|
187
|
+
context.addIssue({
|
|
188
|
+
code: z.ZodIssueCode.custom,
|
|
189
|
+
path: ['legacyHeader'],
|
|
190
|
+
message: "SAMEORIGIN is compatible only with frame-ancestors 'self'.",
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
117
195
|
export const conformanceExpectedSchema = z.object({
|
|
118
196
|
canonicalUrl: z.string().url().optional(),
|
|
119
197
|
schemaTypes: z.array(z.string().min(1)).default([]),
|
|
120
198
|
discoverEligible: z.boolean().default(true),
|
|
199
|
+
framePolicy: framePolicySchema.default({
|
|
200
|
+
ancestors: ["'none'"],
|
|
201
|
+
legacyHeader: 'DENY',
|
|
202
|
+
}),
|
|
121
203
|
})
|
|
122
204
|
|
|
123
205
|
export const conformanceFixtureSchema = z.object({
|
|
@@ -190,6 +272,7 @@ export type PublicationSurfaceEvidence = z.infer<typeof publicationSurfaceSchema
|
|
|
190
272
|
export type CacheVariant = z.infer<typeof cacheVariantSchema>
|
|
191
273
|
export type SecurityFunctionalProbeReceipt = z.infer<typeof securityFunctionalProbeReceiptSchema>
|
|
192
274
|
export type CspReportReceipt = z.infer<typeof cspReportReceiptSchema>
|
|
275
|
+
export type InlineStyleAttributeInventory = z.infer<typeof inlineStyleAttributeInventorySchema>
|
|
193
276
|
export type SecurityReceipts = z.infer<typeof securityReceiptsSchema>
|
|
194
277
|
export type BuildEvidence = z.infer<typeof buildEvidenceSchema>
|
|
195
278
|
export type ConformanceFixtureInput = z.input<typeof conformanceFixtureSchema>
|