@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/dist/schema.js CHANGED
@@ -1,7 +1,25 @@
1
1
  import { z } from 'zod';
2
- export const EVIDENCE_SCHEMA_VERSION = 'growth-labs.conformance.v1';
2
+ export const EVIDENCE_SCHEMA_VERSION = 'growth-labs.conformance.v2';
3
+ export const REQUIRED_FUNCTIONAL_PROBE_NAMES = [
4
+ 'oauth',
5
+ 'media',
6
+ 'images',
7
+ 'analytics',
8
+ 'embeds',
9
+ 'error-pages',
10
+ ];
3
11
  export const REQUIRED_NEGATIVE_FIXTURE_CODES = {
4
12
  'csp-unsafe-inline': 'security.csp.unsafe_inline',
13
+ 'missing-coop': 'security.header.missing',
14
+ 'missing-corp': 'security.header.missing',
15
+ 'hsts-below-one-year': 'security.hsts.max_age_too_low',
16
+ 'csp-wildcard-default-src': 'security.csp.default_src_wildcard',
17
+ 'csp-unsafe-eval': 'security.csp.unsafe_eval',
18
+ 'csp-inline-executable-without-nonce-or-hash': 'security.csp.inline_executable_without_nonce_or_hash',
19
+ 'conflicting-duplicate-security-header': 'security.header.conflicting_values',
20
+ 'missing-functional-probe-receipt': 'security.functional_probe.missing',
21
+ 'missing-csp-report-receipt': 'security.csp_report.missing',
22
+ 'failed-csp-report-receipt': 'security.csp_report.failed',
5
23
  'headers-static-only-while-ssr-missing': 'security.headers.static_only_for_ssr',
6
24
  'undeclared-na': 'publication.na.undeclared',
7
25
  'stringified-is-accessible-for-free': 'structured_data.is_accessible_for_free.stringified',
@@ -50,6 +68,27 @@ export const cacheVariantSchema = z.object({
50
68
  vary: z.array(z.string().min(1)).default([]),
51
69
  headers: headerMapSchema.default({}),
52
70
  });
71
+ export const securityFunctionalProbeReceiptSchema = z.object({
72
+ name: z.string().min(1),
73
+ status: checkStatusSchema,
74
+ checkedAt: z.string().min(1).optional(),
75
+ evidence: z.string().min(1).optional(),
76
+ capabilityBoundary: z.string().min(1).optional(),
77
+ });
78
+ export const cspReportReceiptSchema = z.object({
79
+ name: z.string().min(1),
80
+ status: checkStatusSchema,
81
+ checkedAt: z.string().min(1).optional(),
82
+ evidence: z.string().min(1).optional(),
83
+ endpoint: z.string().min(1).optional(),
84
+ capabilityBoundary: z.string().min(1).optional(),
85
+ });
86
+ export const securityReceiptsSchema = z
87
+ .object({
88
+ functionalProbes: z.array(securityFunctionalProbeReceiptSchema).default([]),
89
+ cspReports: z.array(cspReportReceiptSchema).default([]),
90
+ })
91
+ .default({ functionalProbes: [], cspReports: [] });
53
92
  export const buildEvidenceSchema = z.object({
54
93
  commitSha: z.string().min(7).optional(),
55
94
  artifactId: z.string().min(1).optional(),
@@ -74,6 +113,7 @@ export const conformanceFixtureSchema = z.object({
74
113
  expected: conformanceExpectedSchema.default({}),
75
114
  publicationSurfaces: z.array(publicationSurfaceSchema).default([]),
76
115
  cacheVariants: z.array(cacheVariantSchema).default([]),
116
+ securityReceipts: securityReceiptsSchema,
77
117
  buildEvidence: buildEvidenceSchema.optional(),
78
118
  expectedFailingCode: z.string().min(1).optional(),
79
119
  });
@@ -107,6 +147,7 @@ export const siteEvidenceBundleSchema = z.object({
107
147
  summary: evidenceSummarySchema,
108
148
  checks: z.array(conformanceCheckReceiptSchema),
109
149
  findings: z.array(conformanceFindingSchema),
150
+ securityReceipts: securityReceiptsSchema,
110
151
  });
111
152
  export const fleetEvidenceBundleSchema = z.object({
112
153
  schemaVersion: z.literal(EVIDENCE_SCHEMA_VERSION),
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,uBAAuB,GAAG,4BAA4B,CAAA;AAEnE,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC9C,mBAAmB,EAAE,4BAA4B;IACjD,uCAAuC,EAAE,sCAAsC;IAC/E,eAAe,EAAE,2BAA2B;IAC5C,oCAAoC,EAAE,oDAAoD;IAC1F,oCAAoC,EAAE,oDAAoD;IAC1F,0BAA0B,EAAE,2BAA2B;IACvD,mBAAmB,EAAE,wBAAwB;IAC7C,iCAAiC,EAAE,iDAAiD;IACpF,oCAAoC,EAAE,oCAAoC;IAC1E,wCAAwC,EAAE,iDAAiD;CAClF,CAAA;AAMV,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC;IACpC,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,OAAO;IACP,SAAS;CACT,CAAC,CAAA;AACF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;AAC/D,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAA;AAEjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;AAC3E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAA;AAE1D,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;IACvD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAC5C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IACzD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACzC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACpD,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAChD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IAC3E,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5C,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;CACpC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACzC,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACxD,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC9C,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CAC3C,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACzC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACnD,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CAC3C,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACpD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,UAAU,EAAE,gBAAgB;IAC5B,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,qBAAqB;IAC/B,QAAQ,EAAE,yBAAyB,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/C,mBAAmB,EAAE,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAClE,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACtD,aAAa,EAAE,mBAAmB,CAAC,QAAQ,EAAE;IAC7C,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACjD,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,QAAQ,EAAE,qBAAqB;IAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAClC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,iBAAiB;IACzB,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC;CAC3C,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACpC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACpC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAClC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;CACxC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC;IACjD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,qBAAqB;IAC9B,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,6BAA6B,CAAC;IAC9C,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC;CAC3C,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC;IACjD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,qBAAqB,CAAC,MAAM,CAAC;QACrC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;IACF,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC;CACxC,CAAC,CAAA;AAiBF,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,OAAO,wBAAwB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACxD,OAAO,wBAAwB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,KAAc;IACzD,OAAO,yBAAyB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;AAC9C,CAAC"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,137 @@
10
10
  }
11
11
  }
12
12
  },
13
+ {
14
+ "name": "missing-coop",
15
+ "expectedFailingCode": "security.header.missing",
16
+ "replacePaths": ["response.headers"],
17
+ "patch": {
18
+ "response": {
19
+ "headers": {
20
+ "content-security-policy": "default-src 'self'; script-src 'self' 'nonce-fixture'; style-src 'self' 'nonce-fixture'; 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",
21
+ "strict-transport-security": "max-age=31536000",
22
+ "cross-origin-resource-policy": "same-origin",
23
+ "x-frame-options": "DENY",
24
+ "x-content-type-options": "nosniff",
25
+ "referrer-policy": "strict-origin-when-cross-origin",
26
+ "permissions-policy": "accelerometer=(), camera=(), geolocation=(), gyroscope=(), microphone=(), payment=(), usb=()",
27
+ "cache-control": "public, max-age=0, s-maxage=300, stale-while-revalidate=86400"
28
+ }
29
+ }
30
+ }
31
+ },
32
+ {
33
+ "name": "missing-corp",
34
+ "expectedFailingCode": "security.header.missing",
35
+ "replacePaths": ["response.headers"],
36
+ "patch": {
37
+ "response": {
38
+ "headers": {
39
+ "content-security-policy": "default-src 'self'; script-src 'self' 'nonce-fixture'; style-src 'self' 'nonce-fixture'; 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",
40
+ "strict-transport-security": "max-age=31536000",
41
+ "cross-origin-opener-policy": "same-origin",
42
+ "x-frame-options": "DENY",
43
+ "x-content-type-options": "nosniff",
44
+ "referrer-policy": "strict-origin-when-cross-origin",
45
+ "permissions-policy": "accelerometer=(), camera=(), geolocation=(), gyroscope=(), microphone=(), payment=(), usb=()",
46
+ "cache-control": "public, max-age=0, s-maxage=300, stale-while-revalidate=86400"
47
+ }
48
+ }
49
+ }
50
+ },
51
+ {
52
+ "name": "hsts-below-one-year",
53
+ "expectedFailingCode": "security.hsts.max_age_too_low",
54
+ "patch": {
55
+ "response": {
56
+ "headers": {
57
+ "strict-transport-security": "max-age=300"
58
+ }
59
+ }
60
+ }
61
+ },
62
+ {
63
+ "name": "csp-wildcard-default-src",
64
+ "expectedFailingCode": "security.csp.default_src_wildcard",
65
+ "patch": {
66
+ "response": {
67
+ "headers": {
68
+ "content-security-policy": "default-src *; script-src 'self' 'nonce-fixture'; style-src 'self' 'nonce-fixture'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
69
+ }
70
+ }
71
+ }
72
+ },
73
+ {
74
+ "name": "csp-unsafe-eval",
75
+ "expectedFailingCode": "security.csp.unsafe_eval",
76
+ "patch": {
77
+ "response": {
78
+ "headers": {
79
+ "content-security-policy": "default-src 'self'; script-src 'self' 'nonce-fixture' 'unsafe-eval'; style-src 'self' 'nonce-fixture'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
80
+ }
81
+ }
82
+ }
83
+ },
84
+ {
85
+ "name": "csp-inline-executable-without-nonce-or-hash",
86
+ "expectedFailingCode": "security.csp.inline_executable_without_nonce_or_hash",
87
+ "patch": {
88
+ "response": {
89
+ "headers": {
90
+ "content-security-policy": "default-src 'self'; script-src 'self' 'nonce-fixture'; style-src 'self' 'nonce-fixture'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
91
+ },
92
+ "html": "<!doctype html><html><head><title>Fixture publication conformance article</title><link rel=\"canonical\" href=\"https://fixture.example/articles/valid-publication\"><meta name=\"robots\" content=\"index, follow, max-image-preview:large\"><script>window.fixtureInlineExecutable = true</script><script type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"NewsArticle\",\"headline\":\"Fixture publication conformance article\",\"isAccessibleForFree\":true,\"datePublished\":\"2026-07-15T12:00:00Z\",\"image\":[{\"url\":\"https://media.fixture.example/images/articles/valid-16x9.jpg\",\"width\":1200,\"height\":675}]}</script></head><body><main><h1>Fixture publication conformance article</h1></main></body></html>"
93
+ }
94
+ }
95
+ },
96
+ {
97
+ "name": "conflicting-duplicate-security-header",
98
+ "expectedFailingCode": "security.header.conflicting_values",
99
+ "patch": {
100
+ "response": {
101
+ "headers": {
102
+ "x-content-type-options": ["nosniff", "none"]
103
+ }
104
+ }
105
+ }
106
+ },
107
+ {
108
+ "name": "missing-functional-probe-receipt",
109
+ "expectedFailingCode": "security.functional_probe.missing",
110
+ "replacePaths": ["securityReceipts.functionalProbes"],
111
+ "patch": {
112
+ "securityReceipts": {
113
+ "functionalProbes": []
114
+ }
115
+ }
116
+ },
117
+ {
118
+ "name": "missing-csp-report-receipt",
119
+ "expectedFailingCode": "security.csp_report.missing",
120
+ "replacePaths": ["securityReceipts.cspReports"],
121
+ "patch": {
122
+ "securityReceipts": {
123
+ "cspReports": []
124
+ }
125
+ }
126
+ },
127
+ {
128
+ "name": "failed-csp-report-receipt",
129
+ "expectedFailingCode": "security.csp_report.failed",
130
+ "replacePaths": ["securityReceipts.cspReports"],
131
+ "patch": {
132
+ "securityReceipts": {
133
+ "cspReports": [
134
+ {
135
+ "name": "csp-report-canary",
136
+ "status": "fail",
137
+ "checkedAt": "2026-07-15T23:30:00Z",
138
+ "evidence": "Synthetic blocked-script canary report was not observed by the consumer-owned reporting endpoint."
139
+ }
140
+ ]
141
+ }
142
+ }
143
+ },
13
144
  {
14
145
  "name": "headers-static-only-while-ssr-missing",
15
146
  "expectedFailingCode": "security.headers.static_only_for_ssr",
@@ -10,7 +10,9 @@
10
10
  "url": "https://fixture.example/articles/valid-publication",
11
11
  "headers": {
12
12
  "content-security-policy": "default-src 'self'; script-src 'self' 'nonce-fixture'; style-src 'self' 'nonce-fixture'; 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",
13
- "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
13
+ "strict-transport-security": "max-age=31536000",
14
+ "cross-origin-opener-policy": "same-origin",
15
+ "cross-origin-resource-policy": "same-origin",
14
16
  "x-frame-options": "DENY",
15
17
  "x-content-type-options": "nosniff",
16
18
  "referrer-policy": "strict-origin-when-cross-origin",
@@ -62,12 +64,64 @@
62
64
  "url": "https://fixture.example/articles/valid-publication"
63
65
  }
64
66
  ],
67
+ "securityReceipts": {
68
+ "functionalProbes": [
69
+ {
70
+ "name": "oauth",
71
+ "status": "na",
72
+ "checkedAt": "2026-07-15T23:30:00Z",
73
+ "capabilityBoundary": "Fixture site has no OAuth flow",
74
+ "evidence": "Fixture-only package conformance baseline"
75
+ },
76
+ {
77
+ "name": "media",
78
+ "status": "pass",
79
+ "checkedAt": "2026-07-15T23:30:00Z",
80
+ "evidence": "Fixture media image probes loaded under enforced CSP"
81
+ },
82
+ {
83
+ "name": "images",
84
+ "status": "pass",
85
+ "checkedAt": "2026-07-15T23:30:00Z",
86
+ "evidence": "Fixture structured and rendered images loaded under enforced CSP"
87
+ },
88
+ {
89
+ "name": "analytics",
90
+ "status": "na",
91
+ "checkedAt": "2026-07-15T23:30:00Z",
92
+ "capabilityBoundary": "Fixture site has no analytics runtime",
93
+ "evidence": "Fixture-only package conformance baseline"
94
+ },
95
+ {
96
+ "name": "embeds",
97
+ "status": "na",
98
+ "checkedAt": "2026-07-15T23:30:00Z",
99
+ "capabilityBoundary": "Fixture article has no third-party embeds",
100
+ "evidence": "Fixture-only package conformance baseline"
101
+ },
102
+ {
103
+ "name": "error-pages",
104
+ "status": "pass",
105
+ "checkedAt": "2026-07-15T23:30:00Z",
106
+ "evidence": "Fixture error-page probe returned enforced security headers"
107
+ }
108
+ ],
109
+ "cspReports": [
110
+ {
111
+ "name": "csp-report-canary",
112
+ "status": "pass",
113
+ "checkedAt": "2026-07-15T23:30:00Z",
114
+ "endpoint": "fixture-csp-report-endpoint",
115
+ "evidence": "Synthetic blocked-script canary produced a sanitized CSP report receipt"
116
+ }
117
+ ]
118
+ },
65
119
  "buildEvidence": {
66
120
  "commitSha": "bd39d718f6a5b0df9a249e11638a8b430050941e",
67
121
  "artifactId": "fixture-build-001",
68
122
  "generatedAt": "2026-07-15T23:30:00Z",
69
123
  "packageVersions": {
70
- "@growth-labs/conformance": "0.1.0",
124
+ "@growth-labs/conformance": "0.2.0",
71
125
  "@growth-labs/seo": "0.9.0"
72
126
  },
73
127
  "staticHeaderPolicy": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-labs/conformance",
3
- "version": "0.1.0",
3
+ "version": "0.2.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",
package/src/index.ts CHANGED
@@ -2,11 +2,17 @@ export {
2
2
  buildCacheControl,
3
3
  buildContentSecurityPolicy,
4
4
  buildSharedPublicationHeaders,
5
+ buildStrictTransportSecurity,
5
6
  type CachePolicyOptions,
7
+ type ContentSecurityPolicyOptions,
8
+ type CrossOriginOpenerPolicy,
9
+ type CrossOriginResourcePolicy,
10
+ collectHeaders,
6
11
  type HeaderPolicyOptions,
7
12
  isPrivateAudience,
8
13
  normalizeHeaders,
9
14
  REQUIRED_SECURITY_HEADERS,
15
+ type StrictTransportSecurityOptions,
10
16
  } from './policy.js'
11
17
  export { type RunConformanceOptions, runFleetConformance, runSiteConformance } from './runner.js'
12
18
  export {
@@ -19,12 +25,14 @@ export {
19
25
  type ConformanceFinding,
20
26
  type ConformanceFixture,
21
27
  type ConformanceFixtureInput,
28
+ type CspReportReceipt,
22
29
  cacheVariantSchema,
23
30
  checkStatusSchema,
24
31
  conformanceCheckReceiptSchema,
25
32
  conformanceExpectedSchema,
26
33
  conformanceFindingSchema,
27
34
  conformanceFixtureSchema,
35
+ cspReportReceiptSchema,
28
36
  EVIDENCE_SCHEMA_VERSION,
29
37
  type EvidenceSummary,
30
38
  evidenceSummarySchema,
@@ -37,13 +45,18 @@ export {
37
45
  type PublicationSurfaceEvidence,
38
46
  parseConformanceFixture,
39
47
  publicationSurfaceSchema,
48
+ REQUIRED_FUNCTIONAL_PROBE_NAMES,
40
49
  REQUIRED_NEGATIVE_FIXTURE_CODES,
41
50
  type RenderMode,
42
51
  type RequiredNegativeFindingCode,
43
52
  type RequiredNegativeFixtureId,
44
53
  renderModeSchema,
54
+ type SecurityFunctionalProbeReceipt,
55
+ type SecurityReceipts,
45
56
  type SiteEvidenceBundle,
46
57
  type SurfaceResponse,
58
+ securityFunctionalProbeReceiptSchema,
59
+ securityReceiptsSchema,
47
60
  siteEvidenceBundleSchema,
48
61
  surfaceResponseSchema,
49
62
  validateFleetEvidenceBundle,
package/src/policy.ts CHANGED
@@ -3,16 +3,38 @@ import type { Audience, HeaderMap, RenderMode } from './schema.js'
3
3
  export const REQUIRED_SECURITY_HEADERS = [
4
4
  'content-security-policy',
5
5
  'strict-transport-security',
6
+ 'cross-origin-opener-policy',
7
+ 'cross-origin-resource-policy',
6
8
  'x-frame-options',
7
9
  'x-content-type-options',
8
10
  'referrer-policy',
9
11
  'permissions-policy',
10
12
  ] as const
11
13
 
14
+ export type CrossOriginOpenerPolicy = 'same-origin' | 'same-origin-allow-popups' | 'unsafe-none'
15
+ export type CrossOriginResourcePolicy = 'same-origin' | 'same-site' | 'cross-origin'
16
+
17
+ export interface StrictTransportSecurityOptions {
18
+ maxAgeSeconds?: number
19
+ includeSubDomains?: boolean
20
+ preload?: boolean
21
+ }
22
+
23
+ export interface ContentSecurityPolicyOptions {
24
+ nonce?: string
25
+ reportUri?: string
26
+ reportTo?: string
27
+ }
28
+
12
29
  export interface HeaderPolicyOptions {
13
30
  renderMode: RenderMode
14
31
  audience: Audience
15
32
  nonce?: string
33
+ cspReportUri?: string
34
+ cspReportTo?: string
35
+ strictTransportSecurity?: StrictTransportSecurityOptions
36
+ crossOriginOpenerPolicy?: CrossOriginOpenerPolicy
37
+ crossOriginResourcePolicy?: CrossOriginResourcePolicy
16
38
  markdownTwinUrl?: string
17
39
  }
18
40
 
@@ -25,8 +47,14 @@ export function buildSharedPublicationHeaders(
25
47
  options: HeaderPolicyOptions,
26
48
  ): Record<string, string> {
27
49
  const headers: Record<string, string> = {
28
- 'Content-Security-Policy': buildContentSecurityPolicy(options.nonce),
29
- 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
50
+ 'Content-Security-Policy': buildContentSecurityPolicy({
51
+ nonce: options.nonce,
52
+ reportUri: options.cspReportUri,
53
+ reportTo: options.cspReportTo,
54
+ }),
55
+ 'Strict-Transport-Security': buildStrictTransportSecurity(options.strictTransportSecurity),
56
+ 'Cross-Origin-Opener-Policy': options.crossOriginOpenerPolicy ?? 'same-origin',
57
+ 'Cross-Origin-Resource-Policy': options.crossOriginResourcePolicy ?? 'same-origin',
30
58
  'X-Frame-Options': 'DENY',
31
59
  'X-Content-Type-Options': 'nosniff',
32
60
  'Referrer-Policy': 'strict-origin-when-cross-origin',
@@ -42,15 +70,16 @@ export function buildSharedPublicationHeaders(
42
70
  return headers
43
71
  }
44
72
 
45
- export function buildContentSecurityPolicy(nonce?: string): string {
73
+ export function buildContentSecurityPolicy(input?: string | ContentSecurityPolicyOptions): string {
74
+ const options = typeof input === 'string' ? { nonce: input } : (input ?? {})
46
75
  const scriptSources = ["'self'"]
47
76
  const styleSources = ["'self'"]
48
- if (nonce) {
49
- scriptSources.push(`'nonce-${nonce}'`)
50
- styleSources.push(`'nonce-${nonce}'`)
77
+ if (options.nonce) {
78
+ scriptSources.push(`'nonce-${options.nonce}'`)
79
+ styleSources.push(`'nonce-${options.nonce}'`)
51
80
  }
52
81
 
53
- return [
82
+ const directives = [
54
83
  "default-src 'self'",
55
84
  `script-src ${scriptSources.join(' ')}`,
56
85
  `style-src ${styleSources.join(' ')}`,
@@ -61,7 +90,28 @@ export function buildContentSecurityPolicy(nonce?: string): string {
61
90
  "base-uri 'self'",
62
91
  "frame-ancestors 'none'",
63
92
  'upgrade-insecure-requests',
64
- ].join('; ')
93
+ ]
94
+ if (options.reportTo) directives.push(`report-to ${options.reportTo}`)
95
+ if (options.reportUri) directives.push(`report-uri ${options.reportUri}`)
96
+
97
+ return directives.join('; ')
98
+ }
99
+
100
+ export function buildStrictTransportSecurity(options: StrictTransportSecurityOptions = {}): string {
101
+ const maxAgeSeconds = options.maxAgeSeconds ?? 31536000
102
+ if (!Number.isInteger(maxAgeSeconds) || maxAgeSeconds < 31536000) {
103
+ throw new Error('Strict-Transport-Security maxAgeSeconds must be at least 31536000')
104
+ }
105
+ if (options.preload === true && options.includeSubDomains !== true) {
106
+ throw new Error(
107
+ 'Strict-Transport-Security preload requires includeSubDomains after host inventory proof',
108
+ )
109
+ }
110
+
111
+ const directives = [`max-age=${maxAgeSeconds}`]
112
+ if (options.includeSubDomains === true) directives.push('includeSubDomains')
113
+ if (options.preload === true) directives.push('preload')
114
+ return directives.join('; ')
65
115
  }
66
116
 
67
117
  export function buildCacheControl({ renderMode, audience }: CachePolicyOptions): string {
@@ -73,14 +123,23 @@ export function buildCacheControl({ renderMode, audience }: CachePolicyOptions):
73
123
  }
74
124
 
75
125
  export function normalizeHeaders(headers: HeaderMap | undefined): Record<string, string> {
126
+ const entries = collectHeaders(headers)
76
127
  const normalized: Record<string, string> = {}
77
- if (!headers) return normalized
128
+ for (const [name, values] of Object.entries(entries)) {
129
+ normalized[name] = values.join(', ')
130
+ }
131
+ return normalized
132
+ }
78
133
 
134
+ export function collectHeaders(headers: HeaderMap | undefined): Record<string, string[]> {
135
+ const normalized: Record<string, string[]> = {}
136
+ if (!headers) return normalized
79
137
  for (const [name, value] of Object.entries(headers)) {
138
+ const normalizedName = name.toLowerCase()
80
139
  if (typeof value === 'string') {
81
- normalized[name.toLowerCase()] = value
140
+ normalized[normalizedName] = [...(normalized[normalizedName] ?? []), value]
82
141
  } else if (Array.isArray(value)) {
83
- normalized[name.toLowerCase()] = value.join(', ')
142
+ normalized[normalizedName] = [...(normalized[normalizedName] ?? []), ...value]
84
143
  }
85
144
  }
86
145