diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index d8afafd4c0b5..b06c4ac8320f 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -129,6 +129,13 @@ jobs:
- name: Record build start time
run: echo "CI_BUILD_START_EPOCH=$(date +%s)" >> "$GITHUB_ENV"
+ # Node's built-in runner over scripts/*.test.js. Runs before the build because it
+ # needs nothing installed -- `make ensure` begins with `clean`, so anything
+ # installed ahead of it is thrown away. `make test` is gated on static/programs
+ # changes in scheduled-test.yml, which would leave this coverage off content PRs.
+ - name: Run unit tests
+ run: make test-unit
+
- name: Build and deploy
run: make ci_pull_request
env:
diff --git a/Makefile b/Makefile
index 37a4541df88f..555197352bb5 100644
--- a/Makefile
+++ b/Makefile
@@ -148,8 +148,14 @@ meta-images:
.PHONY: test
test:
+ $(MAKE) test-unit
$(MAKE) test-programs
+# Unit tests: the scripts/*.test.js files. Fast; no network.
+.PHONY: test-unit
+test-unit:
+ ./scripts/run-unit-tests.sh
+
.PHONY: test-programs
test-programs:
./scripts/programs/test.sh preview
diff --git a/content/docs/administration/get-started/setting-up-for-success.md b/content/docs/administration/get-started/setting-up-for-success.md
index 1222c5a1ee27..e2fdba739a1b 100644
--- a/content/docs/administration/get-started/setting-up-for-success.md
+++ b/content/docs/administration/get-started/setting-up-for-success.md
@@ -20,11 +20,11 @@ Security is a team effort that's best established from the outset. Pulumi Cloud
### Choose your compliance approach
-Modern enterprises face rigorous compliance requirements. Pulumi Cloud is SOC 2 Type II certified and AWS-reviewed for compliance best practices. The infrastructure hosting Pulumi Cloud aligns with IT security standards including SOC 1/SSAE 16/ISAE 3402, SOC 2, SOC 3, FISMA, FedRAMP, DOD SRG Levels 2 and 4, PCI DSS Level 1, EU Model Clauses, ISO 9001/27001/27017/27018, ITAR, IRAP, FIPS 140-2, MLPS Level 3, and MTCS. Learn more at [Pulumi Security](/security/).
+Modern enterprises face rigorous compliance requirements. Pulumi Cloud is SOC 2 Type II certified. The infrastructure hosting Pulumi Cloud aligns with IT security standards including SOC 1/SSAE 16/ISAE 3402, SOC 2, SOC 3, FISMA, FedRAMP, DOD SRG Levels 2 and 4, PCI DSS Level 1, EU Model Clauses, ISO 9001/27001/27017/27018, ITAR, IRAP, FIPS 140-2, MLPS Level 3, and MTCS. Learn more at [Pulumi Security](/security/).
-Use Pulumi's Policy as Code engine, [Pulumi Policies](/docs/insights/policy/), to enforce compliant infrastructure practices. Pulumi Policies includes hundreds of out-of-the-box policies for AWS, Azure, Google Cloud, and Kubernetes, spanning CIS, HITRUST, NIST, and PCI DSS. You can also write custom policies for your specific industry or enterprise requirements.
+Use Pulumi's Policy as Code engine, [Pulumi Policies](/docs/insights/policy/), to enforce compliant infrastructure practices. Pulumi Policies includes hundreds of out-of-the-box policies for AWS, Azure, Google Cloud, and Kubernetes, spanning CIS, HITRUST, NIST, PCI DSS, ISO 27001, and CMMC. You can also write custom policies for your specific industry or enterprise requirements.
-Pulumi Policies identifies issues in existing cloud infrastructure and prevents new problems from being introduced. Configure it at warning or error levels, and apply it flexibly across projects—for example, GDPR rules might only apply to infrastructure in European regions. Pulumi Policies also features automatic remediations.
+Pulumi Policies identifies issues in existing cloud infrastructure and prevents new problems from being introduced. Configure each policy as advisory, mandatory, or remediate, and apply policies flexibly across projects—for example, GDPR rules might only apply to infrastructure in European regions. Pulumi Policies also features automatic remediations.
Pulumi Cloud maintains an audit log of every activity and who performed it for complete visibility.
diff --git a/content/docs/insights/_index.md b/content/docs/insights/_index.md
index b7fdac6802ef..0e866793daf9 100644
--- a/content/docs/insights/_index.md
+++ b/content/docs/insights/_index.md
@@ -70,7 +70,7 @@ sections:
- icon: package
heading: Pre-built Policy Packs
link: /docs/insights/policy/policy-packs/pre-built-packs/
- description: Use ready-made compliance rules for CIS, HITRUST, NIST, and PCI DSS with no code required.
+ description: Use ready-made compliance rules for CIS, HITRUST, NIST, PCI DSS, ISO 27001, and CMMC with no code required.
- icon: arrows-clockwise
heading: Policy Groups
link: /docs/insights/policy/policy-groups/
diff --git a/content/docs/insights/policy/_index.md b/content/docs/insights/policy/_index.md
index 8d67458a3185..e4c949d10263 100644
--- a/content/docs/insights/policy/_index.md
+++ b/content/docs/insights/policy/_index.md
@@ -52,7 +52,7 @@ Policy as Code is implemented via [analyzer plugins](/docs/iac/concepts/plugins/
Pulumi Policies uses a hierarchy of components to enforce compliance rules:
1. **Policies** are individual rules that validate infrastructure configuration (e.g., "S3 buckets must be private" or "VMs must use approved instance types").
-1. **Policy packs** are versioned collections of related policies that you publish and manage together. You can use [pre-built policy packs](/docs/insights/policy/policy-packs/pre-built-packs/) for common compliance frameworks (CIS, HITRUST, NIST, and PCI DSS) or [write custom packs](/docs/insights/policy/policy-packs/authoring/) in TypeScript, JavaScript, Python, or [OPA (Rego)](/docs/insights/policy/policy-packs/authoring/#opa).
+1. **Policy packs** are versioned collections of related policies that you publish and manage together. You can use [pre-built policy packs](/docs/insights/policy/policy-packs/pre-built-packs/) for common compliance frameworks (CIS, HITRUST, ISO 27001, NIST, PCI DSS, and CMMC) or [write custom packs](/docs/insights/policy/policy-packs/authoring/) in TypeScript, JavaScript, Python, or [OPA (Rego)](/docs/insights/policy/policy-packs/authoring/#opa).
1. **Policy groups** apply policy packs to specific stacks or cloud accounts. This lets you enforce stricter policies in production and more permissive policies in development environments. Learn more about [policy groups](/docs/insights/policy/policy-groups/).
### Enforcement modes
@@ -115,7 +115,7 @@ Policies can be written in TypeScript/JavaScript (Node.js), Python, or OPA (Rego
Choose your path based on your needs:
- **New to Pulumi Policies?** Start with the [Get Started guide](/docs/insights/policy/get-started/) to configure your first policy group and apply policies to stacks or cloud accounts.
-- **Want ready-made compliance rules?** Browse [pre-built policy packs](/docs/insights/policy/policy-packs/pre-built-packs/) for CIS, PCI DSS, HITRUST, NIST, and other frameworks. Enable them directly from Pulumi Cloud with no code required.
+- **Want ready-made compliance rules?** Browse [pre-built policy packs](/docs/insights/policy/policy-packs/pre-built-packs/) for CIS, PCI DSS, HITRUST, NIST, ISO 27001, CMMC, and other frameworks. Enable them directly from Pulumi Cloud with no code required.
- **Need custom policies?** Learn to [write custom policy packs](/docs/insights/policy/policy-packs/authoring/) in TypeScript, JavaScript, Python, or OPA (Rego). Create organization-specific rules tailored to your requirements.
- **Managing compliance?** View violations and track remediation progress in [Policy Findings](/docs/insights/policy/policy-findings/). Triage issues, assign owners, and monitor compliance trends across your organization.
- **Configuring discovered resources?** Visit the [Discovery Get Started tutorial](/docs/insights/discovery/get-started/) for a detailed guide on audit policies for cloud resources discovered outside Pulumi.
diff --git a/content/docs/insights/policy/policy-packs/_index.md b/content/docs/insights/policy/policy-packs/_index.md
index 16fcf4d0db83..d2ec9222065c 100644
--- a/content/docs/insights/policy/policy-packs/_index.md
+++ b/content/docs/insights/policy/policy-packs/_index.md
@@ -24,7 +24,7 @@ A pack does nothing on its own. To enforce it, add it to a [policy group](/docs/
## Types of policy packs
-- **[Pre-built policy packs](/docs/insights/policy/policy-packs/pre-built-packs/)** are written and maintained by Pulumi. They cover common compliance frameworks, including CIS, PCI DSS, HITRUST, NIST, and ISO 27001, as well as security, cost, and operational best practices for AWS, Azure, and Google Cloud. You enable them from Pulumi Cloud without writing any code. The Pulumi Best Practices and AWS Organizations Tag Policies packs are included in the Team edition and above; the compliance-framework packs require the [Business Critical edition](/pricing/#pre-built-policy-packs).
+- **[Pre-built policy packs](/docs/insights/policy/policy-packs/pre-built-packs/)** are written and maintained by Pulumi. They cover common compliance frameworks, including CIS, PCI DSS, HITRUST, NIST, ISO 27001, and CMMC, as well as security, cost, and operational best practices for AWS, Azure, and Google Cloud. You enable them from Pulumi Cloud without writing any code. The Pulumi Best Practices and AWS Organizations Tag Policies packs are included in the Team edition and above; the compliance-framework packs require the [Business Critical edition](/pricing/#pre-built-policy-packs).
- **[Custom policy packs](/docs/insights/policy/policy-packs/authoring/)** are the ones you write yourself, in TypeScript, Python, or [OPA (Rego)](/docs/insights/policy/policy-packs/authoring/#opa), to enforce requirements specific to your organization. You can test a custom pack locally with `pulumi preview --policy-pack` before publishing it to Pulumi Cloud.
diff --git a/content/docs/insights/policy/policy-packs/pre-built-packs.md b/content/docs/insights/policy/policy-packs/pre-built-packs.md
index daa90e21a0d1..290206058d66 100644
--- a/content/docs/insights/policy/policy-packs/pre-built-packs.md
+++ b/content/docs/insights/policy/policy-packs/pre-built-packs.md
@@ -39,10 +39,11 @@ The following pre-built policy packs are available out of the box in Pulumi Clou
| ----- | ----- | ----- | ----- |
| **CIS 8.1** | [AWS](/docs/reference/pre-built-policy-packs/cis/aws/), [Azure](/docs/reference/pre-built-policy-packs/cis/azure/), [Google Cloud](/docs/reference/pre-built-policy-packs/cis/google-cloud/) | Business Critical | Enforces CIS 8.1 controls to help organizations implement industry-recognized security best practices and benchmarks across multiple cloud providers. |
| **CIS Kubernetes** | [AWS (EKS)](/docs/reference/pre-built-policy-packs/cis-kubernetes/aws/), [Azure (AKS)](/docs/reference/pre-built-policy-packs/cis-kubernetes/azure/), [Google Cloud (GKE)](/docs/reference/pre-built-policy-packs/cis-kubernetes/google-cloud/) | Business Critical | Enforces CIS Kubernetes Benchmark controls for managed Kubernetes services, helping organizations secure their container orchestration platforms with industry-recognized best practices. |
+| **CMMC 2.0** | [AWS](/docs/reference/pre-built-policy-packs/cmmc/aws/) | Business Critical | Enforces CMMC 2.0 practices for AWS resources, helping defense-industrial-base organizations meet Department of Defense cybersecurity maturity requirements. |
| **HITRUST CSF 11.5** | [AWS](/docs/reference/pre-built-policy-packs/hitrust/aws/), [Azure](/docs/reference/pre-built-policy-packs/hitrust/azure/), [Google Cloud](/docs/reference/pre-built-policy-packs/hitrust/google-cloud/) | Business Critical | Provides predefined controls that align cloud resources with HITRUST CSF requirements, helping organizations enforce security and compliance baselines across multiple providers. |
-| **ISO/IEC 27001:2022** | [AWS](/docs/reference/pre-built-policy-packs/iso-27001/aws/) | Business Critical | Enforces ISO/IEC 27001:2022 Annex A controls for AWS resources, helping organizations align their cloud infrastructure with the international standard for information security management. |
-| **NIST SP 800-53** | [AWS](/docs/reference/pre-built-policy-packs/nist/aws/), [Google Cloud](/docs/reference/pre-built-policy-packs/nist/google-cloud/) | Business Critical | Enforces NIST SP 800-53 rev. 5 security and privacy controls for AWS and Google Cloud resources, helping federal agencies and organizations meet rigorous compliance requirements. |
-| **PCI DSS v4.0.1** | [AWS](/docs/reference/pre-built-policy-packs/pci-dss/aws/) | Business Critical | Enforces PCI DSS v4.0.1 compliance controls for AWS resources, ensuring payment card data security and helping organizations meet payment card industry standards. |
+| **ISO/IEC 27001:2022** | [AWS](/docs/reference/pre-built-policy-packs/iso-27001/aws/), [Azure](/docs/reference/pre-built-policy-packs/iso-27001/azure/), [Google Cloud](/docs/reference/pre-built-policy-packs/iso-27001/google-cloud/) | Business Critical | Enforces ISO/IEC 27001:2022 Annex A controls across multiple cloud providers, helping organizations align their cloud infrastructure with the international standard for information security management. |
+| **NIST SP 800-53** | [AWS](/docs/reference/pre-built-policy-packs/nist/aws/), [Azure](/docs/reference/pre-built-policy-packs/nist/azure/), [Google Cloud](/docs/reference/pre-built-policy-packs/nist/google-cloud/) | Business Critical | Enforces NIST SP 800-53 rev. 5 security and privacy controls across multiple cloud providers, helping federal agencies and organizations meet rigorous compliance requirements. |
+| **PCI DSS v4.0.1** | [AWS](/docs/reference/pre-built-policy-packs/pci-dss/aws/), [Azure](/docs/reference/pre-built-policy-packs/pci-dss/azure/), [Google Cloud](/docs/reference/pre-built-policy-packs/pci-dss/google-cloud/) | Business Critical | Enforces PCI DSS v4.0.1 compliance controls across multiple cloud providers, ensuring payment card data security and helping organizations meet payment card industry standards. |
| **Pulumi Best Practices** | [AWS](/docs/reference/pre-built-policy-packs/pulumi-best-practices/aws/), [Azure](/docs/reference/pre-built-policy-packs/pulumi-best-practices/azure/), [Google Cloud](/docs/reference/pre-built-policy-packs/pulumi-best-practices/google-cloud/) | Team and above | Offers a foundational set of recommended governance and security controls, serving as a strong starting point for organizations seeking comprehensive security coverage. |
| **AWS Organizations Tag Policies** | [AWS and AWS-Native](/docs/reference/pre-built-policy-packs/aws-organizations-tag-policies/aws/) | Team and above | Integrates with AWS Organizations Tag Policies to validate that infrastructure as code resources have required tags before deployment. [Learn more](/docs/insights/policy/integrations/aws-organizations-tag-policies/). |
diff --git a/content/docs/integrations/clouds/aws/_index.md b/content/docs/integrations/clouds/aws/_index.md
index a55750a28fd0..4d7930818027 100644
--- a/content/docs/integrations/clouds/aws/_index.md
+++ b/content/docs/integrations/clouds/aws/_index.md
@@ -89,6 +89,8 @@ For AWS:
- [NIST 800-53 for AWS](/docs/reference/pre-built-policy-packs/nist/aws/)
- [PCI DSS for AWS](/docs/reference/pre-built-policy-packs/pci-dss/aws/)
- [HITRUST CSF for AWS](/docs/reference/pre-built-policy-packs/hitrust/aws/)
+- [ISO/IEC 27001 for AWS](/docs/reference/pre-built-policy-packs/iso-27001/aws/)
+- [CMMC 2.0 for AWS](/docs/reference/pre-built-policy-packs/cmmc/aws/)
- [CIS Kubernetes Benchmark on AWS](/docs/reference/pre-built-policy-packs/cis-kubernetes/aws/) — for EKS clusters.
- [AWS Organizations Tag Policies](/docs/reference/pre-built-policy-packs/aws-organizations-tag-policies/aws/) — enforce Organizations tagging standards on Pulumi-managed resources.
- [AWS Organizations Tag Policies in Insights](/docs/insights/policy/integrations/aws-organizations-tag-policies/) — integration with Pulumi Insights.
diff --git a/content/docs/integrations/clouds/azure/_index.md b/content/docs/integrations/clouds/azure/_index.md
index 95c564a5f3a1..129cc329bab8 100644
--- a/content/docs/integrations/clouds/azure/_index.md
+++ b/content/docs/integrations/clouds/azure/_index.md
@@ -77,7 +77,10 @@ For Azure:
- [Pulumi best practices for Azure](/docs/reference/pre-built-policy-packs/pulumi-best-practices/azure/) — Pulumi-authored policies for common Azure misconfigurations.
- [CIS Microsoft Azure Foundations Benchmark](/docs/reference/pre-built-policy-packs/cis/azure/)
+- [NIST 800-53 for Azure](/docs/reference/pre-built-policy-packs/nist/azure/)
+- [PCI DSS for Azure](/docs/reference/pre-built-policy-packs/pci-dss/azure/)
- [HITRUST CSF for Azure](/docs/reference/pre-built-policy-packs/hitrust/azure/)
+- [ISO/IEC 27001 for Azure](/docs/reference/pre-built-policy-packs/iso-27001/azure/)
- [CIS Kubernetes Benchmark on Azure](/docs/reference/pre-built-policy-packs/cis-kubernetes/azure/) — for AKS clusters.
## Migration
diff --git a/content/docs/integrations/clouds/gcp/_index.md b/content/docs/integrations/clouds/gcp/_index.md
index b51a57857387..e03c4c33d47f 100644
--- a/content/docs/integrations/clouds/gcp/_index.md
+++ b/content/docs/integrations/clouds/gcp/_index.md
@@ -72,7 +72,10 @@ For Google Cloud:
- [Pulumi best practices for Google Cloud](/docs/reference/pre-built-policy-packs/pulumi-best-practices/google-cloud/) — Pulumi-authored policies for common Google Cloud misconfigurations.
- [CIS Google Cloud Platform Foundations Benchmark](/docs/reference/pre-built-policy-packs/cis/google-cloud/)
+- [NIST 800-53 for Google Cloud](/docs/reference/pre-built-policy-packs/nist/google-cloud/)
+- [PCI DSS for Google Cloud](/docs/reference/pre-built-policy-packs/pci-dss/google-cloud/)
- [HITRUST CSF for Google Cloud](/docs/reference/pre-built-policy-packs/hitrust/google-cloud/)
+- [ISO/IEC 27001 for Google Cloud](/docs/reference/pre-built-policy-packs/iso-27001/google-cloud/)
- [CIS Kubernetes Benchmark on Google Cloud](/docs/reference/pre-built-policy-packs/cis-kubernetes/google-cloud/) — for GKE clusters.
## Migration
diff --git a/data/policy_pack_lastmod.json b/data/policy_pack_lastmod.json
index ab244236ee2d..32842c2273a4 100644
--- a/data/policy_pack_lastmod.json
+++ b/data/policy_pack_lastmod.json
@@ -11,10 +11,9 @@
"cis-google-cloud": "2026-07-16",
"cis-kubernetes-aws": "2026-07-16",
"cis-kubernetes-azure": "2026-07-16",
- "cis-kubernetes-gcp": "2026-07-16",
+ "cis-kubernetes-google-cloud": "2026-07-16",
"cmmc-aws": "2026-07-16",
"hitrust-aws": "2026-07-16",
- "hitrust-awsnative": "2026-07-16",
"hitrust-azure": "2026-07-16",
"hitrust-google-cloud": "2026-07-16",
"iso-27001-aws": "2026-07-16",
diff --git a/data/policy_pack_policies/cis-kubernetes-gcp.json b/data/policy_pack_policies/cis-kubernetes-google-cloud.json
similarity index 99%
rename from data/policy_pack_policies/cis-kubernetes-gcp.json
rename to data/policy_pack_policies/cis-kubernetes-google-cloud.json
index c8002f8fbdf5..dc05b0b8738f 100644
--- a/data/policy_pack_policies/cis-kubernetes-gcp.json
+++ b/data/policy_pack_policies/cis-kubernetes-google-cloud.json
@@ -1,7 +1,7 @@
{
"applied": false,
"displayName": "",
- "name": "cis-kubernetes-gcp",
+ "name": "cis-kubernetes-google-cloud",
"policies": [
{
"configSchema": {
@@ -2006,6 +2006,6 @@
"severity": "high"
}
],
- "version": 2,
- "versionTag": "1.0.1"
+ "version": 1,
+ "versionTag": "1.0.0"
}
diff --git a/data/policy_pack_policies/hitrust-awsnative.json b/data/policy_pack_policies/hitrust-awsnative.json
deleted file mode 100644
index d5800f4e6f5a..000000000000
--- a/data/policy_pack_policies/hitrust-awsnative.json
+++ /dev/null
@@ -1,2746 +0,0 @@
-{
- "applied": false,
- "displayName": "",
- "name": "hitrust-awsnative",
- "policies": [
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures EC2 instances have anti-malware/EDR agents deployed",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "anti-malware-edr"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures API Gateway stages have access logging enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "api-gateway-access-logging"
- },
- {
- "configSchema": {
- "properties": {
- "allowedAuthorizationTypes": {
- "default": [
- "AWS_IAM",
- "COGNITO_USER_POOLS",
- "CUSTOM",
- "JWT"
- ],
- "description": "List of allowed authorization types for API Gateway methods",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures API Gateway methods use strong authorization instead of NONE",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "api-gateway-authorization"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 13.ac01.01 - Ensures public-facing API Gateways have WAF associations",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "api-gateway-waf-association"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that ApiGateway Domain Name Security Policy uses secure/modern TLS encryption.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "apigateway-domainname-configure-security-policy"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that any ApiGatewayV2 Domain Name Security Policy uses secure/modern TLS encryption.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "apigatewayv2-domainname-configure-domain-name-security-policy"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that any ApiGatewayV2 Domain Name Configuration is enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "apigatewayv2-domainname-enable-domain-name-configuration"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that AppFlow ConnectorProfile uses a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "appflow-connectorprofile-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that AppFlow Flow uses a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "appflow-flow-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that Athena Workgroups use a customer-managed-key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "athena-workgroup-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that Athena Workgroups are encrypted.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "athena-workgroup-disallow-unencrypted-workgroup"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that Athena Workgroups enforce their configuration to their clients.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "athena-workgroup-enforce-configuration"
- },
- {
- "configSchema": {
- "properties": {
- "allowedAgents": {
- "description": "Optional list of allowed logging agent strings to check for in userData. Defaults to common logging agents if not specified.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures EC2 instances have logging agents configured to forward OS/application logs to central system",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "centralized-os-app-logging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "requiredTags": {
- "description": "An optional array of required tag names that must be present on all resources",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "type": "object"
- },
- "description": "Enforces tagging for cloud asset inventory and management",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloud-asset-inventory"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that any CloudFront distributions have access logging configured.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-distribution-configure-access-logging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that CloudFront distributions uses secure/modern TLS encryption.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-distribution-configure-secure-tls"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that CloudFront distributions communicate with custom origins using TLS 1.2 encryption only.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-distribution-configure-secure-tls-to-origin"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that CloudFront distributions have a WAF ACL associated.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-distribution-configure-waf-acl"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that CloudFront distributions only allow encypted ingress traffic.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-distribution-disallow-unencrypted-traffic"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that any CloudFront distributions have access logging enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-distribution-enable-access-logging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that CloudFront distributions communicate with custom origins using TLS encryption.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-distribution-enable-tls-to-origin"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 13.ac01.01 - Ensures CloudFront distributions have WAF associations",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "cloudfront-waf-association"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS instances have strict network access controls",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "database-strict-network-access"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Enforces that all DynamoDB tables have Stream settings enabled to capture all changes",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "dynamodb-streams-enabled"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that EC2 instances do not have a public IP address.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-instance-disallow-public-ip"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that EC2 instances do not have unencrypted block devices.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-instance-disallow-unencrypted-block-device"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that EC2 instances does not have unencrypted root volumes.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-instance-disallow-unencrypted-root-block-device"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that encrypted EBS volume uses a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-launchtemplate-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that EC2 Launch Templates do not have public IP addresses.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-launchtemplate-disallow-public-ip"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that EC2 Launch Templates do not have unencrypted block device.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-launchtemplate-disallow-unencrypted-block-device"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that EC2 Security Groups do not allow inbound HTTP traffic.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-securitygroup-disallow-inbound-http-traffic"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that encrypted EBS volumes use a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-volume-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that EBS volumes are encrypted.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ec2-volume-disallow-unencrypted-volume"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 09.ac05.01 - Ensures ECR repositories have image scanning enabled for vulnerability management",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ecr-image-scanning"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that ECR repositories use a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ecr-repository-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that ECR repositories have 'scan-on-push' configured.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ecr-repository-configure-image-scan"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that ECR Repositories have immutable images enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ecr-repository-disallow-mutable-image"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that ECR Repositories are encrypted.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ecr-repository-disallow-unencrypted-repository"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that ECR repositories have 'scan-on-push' enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ecr-repository-enable-image-scan"
- },
- {
- "configSchema": {
- "properties": {
- "allowedImages": {
- "description": "An optional array of allowed image identifiers",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 09.ac05.01 - Ensures ECS task definitions use images from repositories with vulnerability scanning",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "ecs-task-definition-image-scanning"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that encrypted EFS File system uses a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "efs-filesystem-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that EFS File Systems do not have an unencrypted file system.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "efs-filesystem-disallow-unencrypted-file-system"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that EKS Clusters API Endpoint are not publicly accessible.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "eks-cluster-disallow-api-endpoint-public-access"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that EKS Cluster Encryption Config is enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "eks-cluster-enable-cluster-encryption-config"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that ELB Listeners do not allow unencrypted (HTTP) traffic.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "elb-listener-disallow-unencrypted-traffic"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that ELB Load Balancers do not allow unencrypted (HTTP) traffic.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "elb-loadbalancer-disallow-unencrypted-traffic"
- },
- {
- "configSchema": {
- "properties": {
- "allowedEnvironmentValues": {
- "description": "List of allowed environment values",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "environmentTagName": {
- "default": "Environment",
- "description": "Name of the tag used to specify environment",
- "type": "string"
- },
- "exemptResourceTypes": {
- "description": "Resource types exempt from environment tagging requirements",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "type": "object"
- },
- "description": "Ensures that resources are tagged to distinguish between production and non-production environments",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "environment-separation-tagging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM group policies follow least privilege principles",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-group-policy-least-privilege"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM managed policies follow least privilege principles",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-policy-least-privilege"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "privilegedActions": {
- "default": [
- "iam:CreateUser",
- "iam:DeleteUser",
- "iam:CreateRole",
- "iam:DeleteRole",
- "iam:CreatePolicy",
- "iam:DeletePolicy",
- "iam:AttachUserPolicy",
- "iam:DetachUserPolicy",
- "iam:AttachRolePolicy",
- "iam:DetachRolePolicy",
- "iam:PutUserPolicy",
- "iam:DeleteUserPolicy",
- "iam:PutRolePolicy",
- "iam:DeleteRolePolicy",
- "iam:CreateAccessKey",
- "iam:DeleteAccessKey",
- "iam:UpdateAccessKey",
- "iam:CreateLoginProfile",
- "iam:DeleteLoginProfile",
- "iam:UpdateLoginProfile",
- "iam:ChangePassword",
- "iam:CreateAccountPasswordPolicy",
- "iam:UpdateAccountPasswordPolicy",
- "iam:DeleteAccountPasswordPolicy",
- "organizations:*",
- "account:*",
- "billing:*",
- "cloudtrail:StopLogging",
- "cloudtrail:DeleteTrail",
- "config:DeleteConfigurationRecorder",
- "config:DeleteDeliveryChannel",
- "guardduty:DeleteDetector",
- "securityhub:DisableSecurityHub",
- "s3:DeleteBucket",
- "rds:DeleteDBInstance",
- "rds:DeleteDBCluster",
- "ec2:TerminateInstances",
- "lambda:DeleteFunction",
- "sts:AssumeRole"
- ],
- "description": "List of privileged actions that require MFA enforcement",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM policies require MFA for privileged actions",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-policy-mfa-enforcement"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM roles follow least privilege principles",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-role-least-privilege"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "privilegedActions": {
- "default": [
- "iam:CreateUser",
- "iam:DeleteUser",
- "iam:CreateRole",
- "iam:DeleteRole",
- "iam:CreatePolicy",
- "iam:DeletePolicy",
- "iam:AttachUserPolicy",
- "iam:DetachUserPolicy",
- "iam:AttachRolePolicy",
- "iam:DetachRolePolicy",
- "iam:PutUserPolicy",
- "iam:DeleteUserPolicy",
- "iam:PutRolePolicy",
- "iam:DeleteRolePolicy",
- "iam:CreateAccessKey",
- "iam:DeleteAccessKey",
- "iam:UpdateAccessKey",
- "iam:CreateLoginProfile",
- "iam:DeleteLoginProfile",
- "iam:UpdateLoginProfile",
- "iam:ChangePassword",
- "iam:CreateAccountPasswordPolicy",
- "iam:UpdateAccountPasswordPolicy",
- "iam:DeleteAccountPasswordPolicy",
- "organizations:*",
- "account:*",
- "billing:*",
- "cloudtrail:StopLogging",
- "cloudtrail:DeleteTrail",
- "config:DeleteConfigurationRecorder",
- "config:DeleteDeliveryChannel",
- "guardduty:DeleteDetector",
- "securityhub:DisableSecurityHub",
- "s3:DeleteBucket",
- "rds:DeleteDBInstance",
- "rds:DeleteDBCluster",
- "ec2:TerminateInstances",
- "lambda:DeleteFunction",
- "sts:AssumeRole"
- ],
- "description": "List of privileged actions that require MFA enforcement",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM roles require MFA for privileged actions",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-role-mfa-enforcement"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM role policies follow least privilege principles",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-role-policy-least-privilege"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "privilegedActions": {
- "default": [
- "iam:CreateUser",
- "iam:DeleteUser",
- "iam:CreateRole",
- "iam:DeleteRole",
- "iam:CreatePolicy",
- "iam:DeletePolicy",
- "iam:AttachUserPolicy",
- "iam:DetachUserPolicy",
- "iam:AttachRolePolicy",
- "iam:DetachRolePolicy",
- "iam:PutUserPolicy",
- "iam:DeleteUserPolicy",
- "iam:PutRolePolicy",
- "iam:DeleteRolePolicy",
- "iam:CreateAccessKey",
- "iam:DeleteAccessKey",
- "iam:UpdateAccessKey",
- "iam:CreateLoginProfile",
- "iam:DeleteLoginProfile",
- "iam:UpdateLoginProfile",
- "iam:ChangePassword",
- "iam:CreateAccountPasswordPolicy",
- "iam:UpdateAccountPasswordPolicy",
- "iam:DeleteAccountPasswordPolicy",
- "organizations:*",
- "account:*",
- "billing:*",
- "cloudtrail:StopLogging",
- "cloudtrail:DeleteTrail",
- "config:DeleteConfigurationRecorder",
- "config:DeleteDeliveryChannel",
- "guardduty:DeleteDetector",
- "securityhub:DisableSecurityHub",
- "s3:DeleteBucket",
- "rds:DeleteDBInstance",
- "rds:DeleteDBCluster",
- "ec2:TerminateInstances",
- "lambda:DeleteFunction",
- "sts:AssumeRole"
- ],
- "description": "List of privileged actions that require MFA enforcement",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM role policies require MFA for privileged actions",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-role-policy-mfa-enforcement"
- },
- {
- "configSchema": {
- "properties": {
- "administrativeMaxDuration": {
- "default": 7200,
- "description": "Maximum session duration in seconds for administrative roles",
- "maximum": 43200,
- "minimum": 3600,
- "type": "number"
- },
- "crossAccountMaxDuration": {
- "default": 14400,
- "description": "Maximum session duration in seconds for cross-account roles",
- "maximum": 43200,
- "minimum": 3600,
- "type": "number"
- },
- "defaultMaxDuration": {
- "default": 3600,
- "description": "Default maximum session duration in seconds for general roles",
- "maximum": 43200,
- "minimum": 3600,
- "type": "number"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "roleSpecificLimits": {
- "additionalProperties": {
- "type": "number"
- },
- "description": "Role name to maximum duration mapping for specific role overrides",
- "type": "object"
- },
- "serviceMaxDuration": {
- "default": 28800,
- "description": "Maximum session duration in seconds for service roles",
- "maximum": 43200,
- "minimum": 3600,
- "type": "number"
- }
- },
- "type": "object"
- },
- "description": "Enforces maximum session duration for IAM roles",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-role-session-duration"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM user policies follow least privilege principles",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "iam-user-policy-least-privilege"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures Kinesis Lambda event source mappings have DLQ configuration",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "kinesis-event-source-mapping-dlq"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures Kinesis streams have retention periods configured",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "kinesis-stream-retention"
- },
- {
- "configSchema": {
- "properties": {
- "allowedKeySpecs": {
- "description": "An optional array of allowed key specs (e.g., SYMMETRIC_DEFAULT, RSA_2048, etc.)",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "allowedKeyUsages": {
- "description": "An optional array of allowed key usages (e.g., ENCRYPT_DECRYPT, SIGN_VERIFY)",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "requireCustomerMasterKeys": {
- "description": "Whether to require customer-managed keys (not AWS managed keys)",
- "type": "boolean"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 09.ab01.01 - Validates KMS key creation with appropriate specifications and origins",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "kms-key-creation"
- },
- {
- "configSchema": {
- "properties": {
- "criticalKeyTags": {
- "description": "Tag keys that identify critical keys requiring special protection",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "maximumDeletionWindow": {
- "description": "Maximum deletion window in days (default: 30)",
- "type": "number"
- },
- "minimumDeletionWindow": {
- "description": "Minimum deletion window in days (default: 7)",
- "type": "number"
- },
- "requireDeletionProtection": {
- "description": "Whether to require deletion protection for critical keys",
- "type": "boolean"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 09.ac10.01, 06.ad04.01 - Validates KMS key deletion windows and lifecycle management",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "kms-key-deletion-lifecycle"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that KMS Keys have key rotation enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "kms-key-enable-key-rotation"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 07.ad01.01, 07.ad02.01 - Validates KMS key policies for least privilege and separation of duties",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "kms-key-policy-access-control"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures that all Lambda functions have their environment variables encrypted using AWS KMS",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "lambda-environment-variables-encryption"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "minimumDescriptionLength": {
- "default": 10,
- "description": "Minimum required length for Lambda function description",
- "minimum": 1,
- "type": "number"
- }
- },
- "type": "object"
- },
- "description": "Ensures all AWS Lambda functions have a documented description attribute",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "lambda-function-documentation"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "minimumLogLevel": {
- "default": "INFO",
- "description": "Minimum required log level",
- "enum": [
- "DEBUG",
- "INFO",
- "WARN"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures that all AWS Lambda functions have logging enabled to track output data processing",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "lambda-function-logging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that lambda function permissions have a source arn specified.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "lambda-permission-configure-source-arn"
- },
- {
- "configSchema": {
- "properties": {
- "approvedRuntimes": {
- "default": [
- "nodejs18.x",
- "nodejs20.x",
- "python3.9",
- "python3.10",
- "python3.11",
- "python3.12",
- "java11",
- "java17",
- "java21",
- "dotnet6",
- "dotnet8",
- "go1.x",
- "ruby3.2",
- "provided.al2",
- "provided.al2023"
- ],
- "description": "List of approved Lambda runtime versions",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures that AWS Lambda functions are created only with approved runtime versions",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "lambda-runtime-restrictions"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM policies follow least privilege principles",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "least-privilege"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "maxTimeoutSeconds": {
- "description": "Maximum allowed timeout in seconds for Lambda functions",
- "maximum": 900,
- "minimum": 1,
- "type": "number"
- }
- },
- "type": "object"
- },
- "description": "Ensures that AWS Lambda functions are configured to time out after a specified duration to prevent extended access",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "limit-lambda-execution-time"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 13.ac01.01 - Ensures public-facing Load Balancers have WAF associations",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "load-balancer-waf-association"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures EC2 instances do not contain hardcoded secrets",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "no-hardcoded-secrets"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures IAM policies follow least privilege principles for Pub/Sub services (SNS, SQS, Kinesis)",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "pubsub-least-privilege-iam"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS instances have audit logging enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-audit-logging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS clusters use secure credential management instead of hardcoded passwords",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-cluster-secure-master-credentials"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Cluster backup retention policy is configured.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbcluster-configure-backup-retention"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Cluster storage uses a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbcluster-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that RDS DB Cluster doesn't use single availability zone.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbcluster-disallow-single-availability-zone"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Cluster storage is encrypted.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbcluster-disallow-unencrypted-storage"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Clusters backup retention policy is enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbcluster-enable-backup-retention"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Instance storage uses a customer-managed KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbinstance-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Instances public access is not enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbinstance-disallow-public-access"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Instance storage is encrypted.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbinstance-disallow-unencrypted-storage"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that RDS DB Instances backup retention policy is enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-dbinstance-enable-backup-retention"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS instances have IAM database authentication enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-iam-authentication"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS instances have Multi-AZ deployment enabled for high availability",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-instance-high-availability"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS instances have automated minor version upgrades enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-managed-service-patching"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Validates that RDS DB subnet groups contain only private subnets",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-private-subnet-validation"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS instances use secure credential management instead of hardcoded passwords",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-secure-master-credentials"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures RDS instances have SSL/TLS encryption enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "rds-ssl-encryption"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "exemptResourceTypes": {
- "description": "Resource types exempt from tagging requirements",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "requiredTags": {
- "description": "List of required tags for all resources. If not specified, only requires at least one tag to exist.",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "type": "object"
- },
- "description": "Ensures all AWS resources must include tags for proper change tracking",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "resource-tagging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- },
- "restrictedUserNames": {
- "description": "List of restricted user names that cannot be used for IAM users",
- "items": {
- "type": "string"
- },
- "type": "array"
- }
- },
- "type": "object"
- },
- "description": "Ensures that default IAM user accounts are not allowed to be created",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "restrict-default-iam-user-creation"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures S3 buckets have access logging enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-access-logging"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that S3 Buckets Server-Side Encryption (SSE) is using a customer-managed KMS Key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-configure-server-side-encryption-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that S3 Buckets Server-Side Encryption (SSE) uses AWS KMS.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-configure-server-side-encryption-kms"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Checks that S3 Bucket ACLs don't allow 'PublicRead', 'PublicReadWrite' or 'AuthenticatedRead'.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-disallow-public-read"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that S3 Bucket Server-Side Encryption (SSE) is enabled.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-enable-server-side-encryption"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Prevents overly permissive S3 bucket policies",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-least-privilege"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures S3 buckets have lifecycle rules configured for retention/disposal",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-lifecycle"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures S3 buckets allow AWS Macie access for data classification and discovery",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-macie-access"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures S3 bucket public access blocks have all settings enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-public-access-block"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures S3 buckets have replication configured for enhanced availability",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-replication"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures S3 buckets have versioning enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "s3-bucket-versioning"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Check that Secrets Manager Secrets use a customer-manager KMS key.",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "secretsmanager-secret-configure-customer-managed-key"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures Security Groups follow default deny with explicit allow principle",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "security-group-default-deny"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures security groups do not allow SSH/RDP from the internet",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "security-group-ssh-rdp"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures security groups follow strict firewall rules with default deny",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "security-group-strict"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures SQS queues have dead letter queue configuration",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "sqs-dead-letter-queue"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures SQS queues have server-side encryption enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "sqs-encryption"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures SQS queues have message retention periods configured",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "sqs-message-retention"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures subnets are distributed across multiple availability zones",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "subnet-multi-az"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures that VPC endpoints are associated with security policies that limit access to specified resources",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "vpc-endpoint-security-policy"
- },
- {
- "configSchema": {
- "properties": {
- "approvedLogDestinations": {
- "description": "List of approved log destinations (S3 bucket ARNs, CloudWatch log group ARNs, or Kinesis stream ARNs)",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures VPC flow logs use approved destinations for centralized monitoring",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "vpc-flow-logs"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "Ensures all VPCs and subnets have flow logs enabled",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "vpc-subnet-flow-logs"
- },
- {
- "configSchema": {
- "properties": {
- "enforcementLevel": {
- "enum": [
- "advisory",
- "mandatory",
- "remediate",
- "disabled"
- ],
- "type": "string"
- }
- },
- "type": "object"
- },
- "description": "HITRUST 13.ac01.01 - Validates WAF Web ACL associations are properly configured",
- "displayName": "",
- "enforcementLevel": "advisory",
- "message": "",
- "name": "waf-association-validation"
- }
- ],
- "version": 5,
- "versionTag": "2.0.1"
-}
diff --git a/data/policy_packs.yaml b/data/policy_packs.yaml
index a8e740db5c8d..89fc78a20bc3 100644
--- a/data/policy_packs.yaml
+++ b/data/policy_packs.yaml
@@ -9,7 +9,28 @@
# Markdown file is needed.
#
# THE RULE: only document packs that are built from the pulumi/policy-packs-internal
-# repo, i.e. the ones we ship to customers as pre-built compliance packs.
+# repo AND are published as Pulumi products.
+#
+# Both halves matter, and the second is not implied by the first. A pack can be built
+# there, publish cleanly, and be returned by /api/orgs/pulumi/policypacks while still
+# being private to the org -- that endpoint lists what the org holds, not what we sell.
+# That is exactly how hitrust-awsnative got a public reference page (pulumi/docs#21055).
+#
+# Both halves are enforced by scripts/fetch-policy-packs.js, so a bad entry fails the
+# nightly job rather than publishing a page:
+#
+# built there every entry must carry a `source:` naming its packs/... directory.
+# a product the registry listing for the org must return the pack with
+# `source: "pulumi"`. Privately published packs come back
+# `source: "private"` instead, and a deleted one not at all.
+# complete the obligation also runs the other way: every product the registry
+# returns needs a page above, or an `undocumented:` entry below saying
+# why not. This file is no longer just an allowlist -- it is a claim
+# about the whole product set, and the check holds it to that.
+#
+# Note the pack's own `source:` key below is the repo directory, unrelated to the
+# registry's `source` field. And do not switch that check to `publisher`: every pack in
+# our org is published by pulumi, so it would admit the private ones too.
#
# This is why the file is an allowlist rather than "document everything in the org".
# The `pulumi` org is also where we publish policy packs for our OWN internal use,
@@ -86,10 +107,14 @@ sections:
h1: CIS Kubernetes - Azure (AKS)
metaDesc: Complete list of CIS Kubernetes Benchmark compliance policies for Azure AKS.
weight: 2
- # `cis-kubernetes-gcp`, not `cis-kubernetes-google-cloud`. Both are published,
- # but only this one is still built by policy-packs-internal; the -google-cloud
- # name is a stale earlier publish that no longer receives updates.
- - pack: cis-kubernetes-gcp
+ # `cis-kubernetes-google-cloud`, not `cis-kubernetes-gcp`. Both names exist in the
+ # org, and the obvious-looking one is the wrong one: policy-packs-internal builds
+ # and publishes under `cis-kubernetes-gcp`, but that name was never made a product,
+ # so every automated release has gone to a *private* pack. The public pack is
+ # `cis-kubernetes-google-cloud`, hand-uploaded once and left at 1.0.0. Same 50
+ # policies either way -- the 1.0.1 bump was a dependency update. @danbiwer is
+ # renaming the pack upstream so releases land on the public name (2026-08-25).
+ - pack: cis-kubernetes-google-cloud
source: packs/cis/kubernetes/gcp
slug: google-cloud
title: Google Cloud (GKE)
@@ -136,16 +161,6 @@ sections:
h1: HITRUST CSF 11.5 - Google Cloud
metaDesc: Complete list of HITRUST CSF 11.5 compliance policies for Google Cloud.
weight: 3
- # Targets the AWS Cloud Control provider (@pulumi/aws-native) rather than the
- # classic AWS provider. None of its policies carry HITRUST control metadata, so
- # the whole page renders as an unmapped policy list.
- - pack: hitrust-awsnative
- source: packs/hitrust/awsnative
- slug: aws-native
- title: AWS Native
- h1: HITRUST CSF 11.5 - AWS Native
- metaDesc: Complete list of HITRUST CSF 11.5 compliance policies for the AWS Native provider.
- weight: 4
- id: iso-27001
menu: reference-pre-built-policy-packs-iso-27001
@@ -255,14 +270,35 @@ sections:
metaDesc: Complete list of Pulumi Best Practices compliance policies for Google Cloud.
weight: 3
-# Every pack that policy-packs-internal builds AND publishes to the `pulumi` org now
-# has a page above, with two deliberate exceptions:
-#
-# aws-organizations-tag-policies Its page (aws-organizations-tag-policies/aws.md)
-# is hand-written prose pointing at the integration
-# guide, not a policy listing. Stays hand-maintained.
-# cis-kubernetes-google-cloud A stale earlier publish, superseded by
-# cis-kubernetes-gcp. See the cis-kubernetes section.
+# Product packs that deliberately get no generated page. The registry check treats any
+# other pack returned with source: "pulumi" as a page we are missing, so a new product
+# either gets an entry above or a reason here -- it cannot be ignored silently. Both
+# keys are required: an entry with no `why:` fails the check rather than silencing it.
+undocumented:
+ - pack: aws-organizations-tag-policies
+ why: >-
+ Its page (aws-organizations-tag-policies/aws.md) is hand-written prose pointing at
+ the integration guide, not a policy listing. Stays hand-maintained.
+
+# Packs the org holds that are NOT products, and so never appear in the registry listing
+# with source: "pulumi". Verified against the live listing on 2026-08-25, which returned 13
+# packs as source: "private" -- cis-kubernetes-gcp plus the 12 named further down.
+# hitrust-awsnative is the exception: it is no longer in the org at all. Recorded only so
+# an audit that diffs the repo against the org does not re-add them; the check already
+# keeps them out.
+# cis-kubernetes-gcp The name policy-packs-internal publishes under, and the
+# plausible-looking half of the pair -- but never a
+# product. Document `cis-kubernetes-google-cloud`
+# instead; see the cis-kubernetes section above.
+# @danbiwer is renaming upstream, after which this name
+# stops being published at all.
+# hitrust-awsnative Built here and resolvable through
+# /api/orgs/pulumi/policypacks, but never a product: it
+# was published privately to the org. Removed on
+# 2026-08-24 after @danbiwer flagged that it had been
+# documented in error (pulumi/docs#21055), and since
+# deleted from the org by @jkodroff. The registry check
+# under THE RULE now blocks this class of mistake.
#
# The org additionally holds packs this repo does not build — internal, experimental,
# or customer one-offs. They must NOT be documented:
diff --git a/scripts/fetch-policy-packs.js b/scripts/fetch-policy-packs.js
index 2d3e52e89d6e..d1cd37115566 100755
--- a/scripts/fetch-policy-packs.js
+++ b/scripts/fetch-policy-packs.js
@@ -14,7 +14,6 @@
const fs = require("fs");
const path = require("path");
-const yaml = require("js-yaml");
const API = process.env.PULUMI_API || "https://api.pulumi.com";
const TOKEN = process.env.PULUMI_ACCESS_TOKEN;
@@ -29,7 +28,9 @@ const OUTPUT_DIR = path.join(ROOT, "data", "policy_pack_policies");
// never on every run, and never to "today" just because the script ran.
const LASTMOD_FILE = path.join(ROOT, "data", "policy_pack_lastmod.json");
-if (!TOKEN) {
+// Guarded so the pure helpers below can be required by scripts/fetch-policy-packs.test.js
+// without a token; the fetching path still refuses to run without one.
+if (!TOKEN && require.main === module) {
console.error("error: PULUMI_ACCESS_TOKEN is not set. This script calls authenticated");
console.error(" Pulumi Cloud policy pack endpoints and cannot run without a token.");
process.exit(1);
@@ -130,7 +131,159 @@ function loadLastmodLedger() {
return JSON.parse(fs.readFileSync(LASTMOD_FILE, "utf8"));
}
+// The `pulumi` org holds two different things under one name: the packs we ship as
+// products, and packs published to the org privately (internal use, experiments,
+// customer one-offs). `/api/orgs/{org}/policypacks` returns both with nothing to tell
+// them apart, which is how the private hitrust-awsnative pack ended up with a public
+// reference page (pulumi/docs#21055).
+//
+// The registry listing does carry the distinction, in `source`. Verified against the
+// live listing for the `pulumi` org on 2026-08-25 (36 packs: 23 `pulumi`, 13 `private`):
+//
+// { "source": "private", "publisher": "pulumi",
+// "name": "approved-component-versions-bad", "version": "1.0.4", ... }
+// { "source": "pulumi", "publisher": "pulumi",
+// "name": "aws-organizations-tag-policies", "version": "1.0.0", ... }
+//
+// Note `publisher` is "pulumi" for everything in our org, so it is NOT the discriminator
+// -- do not "improve" this by filtering on it. `source` is, and it is set by an admin
+// command rather than by whoever publishes the pack, so an engineer testing something
+// cannot set it by accident.
+//
+// The response is `ListPolicyPacksResponse` in the published Pulumi Cloud OpenAPI spec:
+// a required `policyPacks` array plus an optional `continuationToken` for paging.
+//
+// This fails closed. If the listing cannot be read, looks different from the above, or
+// is only partly readable, the job stops rather than falling back to publishing
+// unverified pages: a broken check should cost us a nightly sync, not put a private pack
+// on the website.
+async function fetchRegistryProducts(org) {
+ const body = await fetchJSON(`/api/registry/policypacks?orgLogin=${encodeURIComponent(org)}`);
+ return parseRegistryListing(body, org);
+}
+
+// The pure half of the above, split out so the shape handling is testable without a token.
+function parseRegistryListing(body, org) {
+ const rows = body?.policyPacks;
+ if (!Array.isArray(rows)) {
+ throw new Error(
+ `could not read the registry policy pack listing for the "${org}" org: expected a\n` +
+ ` "policyPacks" array. The API shape may have changed -- see the comment above\n` +
+ ` fetchRegistryProducts in this file.`,
+ );
+ }
+ // The response pages, and no documented query parameter asks for page two. A partial
+ // read is worse than no read: it fails documented packs that are fine, and it hides
+ // products nobody documented from the check below, which is the one thing that check
+ // exists to catch. Stop rather than trust an incomplete list.
+ if (body.continuationToken) {
+ throw new Error(
+ `the registry policy pack listing for the "${org}" org is paginated\n` +
+ ` (continuationToken present) and this script only reads the first page. Teach it\n` +
+ ` to follow the token before trusting the result.`,
+ );
+ }
+ const products = new Set();
+ const nonProducts = new Map();
+ for (const row of rows) {
+ if (!row?.name) continue;
+ // Distinguish "present but carries no source" from "absent from the listing"; the
+ // caller reports the latter, and conflating them sends whoever reads the error
+ // looking for a deleted pack that is right there.
+ if (row.source === "pulumi") products.add(row.name);
+ else nonProducts.set(row.name, row.source ?? "(no source field)");
+ }
+ // A listing with rows but no products is a shape problem, never an allowlist problem.
+ // `source` going missing for this org's records would otherwise surface as the
+ // allowlist error below naming every documented pack as privately published, which is
+ // the most alarming possible misreading of an upstream schema change.
+ if (rows.length && !products.size) {
+ throw new Error(
+ `the registry listing for the "${org}" org returned ${rows.length} record(s) but not\n` +
+ ` one with source: "pulumi". That is far more likely a change to the API's\n` +
+ ` "source" field than every pack being unpublished at once -- see the comment\n` +
+ ` above fetchRegistryProducts.`,
+ );
+ }
+ return { products, nonProducts };
+}
+
+// Both directions of the gate, pure so it can be exercised without the network.
+// Forward: nothing documented may be a non-product. Reverse: no product may go
+// undocumented without a recorded reason.
+function auditAllowlist({ org, packs, allowlistUndocumented, products, nonProducts, warn = console.warn }) {
+ // The exemption list is the one input here with nothing behind it, so validate it or
+ // the reason it exists to record isn't actually required: `- pack: foo` with no `why:`
+ // would silence the check exactly as well as a real exemption, and it is the shortest
+ // path for whoever is unblocking a red nightly at 08:00. The bare-string form
+ // (`- foo`) is worse still -- `e.pack` is undefined, so the pack stays unaccounted for
+ // and the check fires naming a pack the author believes they just listed.
+ const undocumented = allowlistUndocumented || [];
+ const unexplained = undocumented.filter((e) => !e?.pack || !String(e?.why || "").trim());
+ if (unexplained.length) {
+ throw new Error(
+ `every entry under "undocumented:" in data/policy_packs.yaml needs both a "pack:" and\n` +
+ ` a "why:": ${JSON.stringify(unexplained)}\n` +
+ ` The list records why a product stays unlisted; it is not a way to silence the\n` +
+ ` check below.`,
+ );
+ }
+ const alsoDocumented = undocumented.map((e) => e.pack).filter((p) => packs.includes(p));
+ if (alsoDocumented.length) {
+ throw new Error(
+ `these packs are listed under "undocumented:" in data/policy_packs.yaml but also have\n` +
+ ` a page above: ${alsoDocumented.join(", ")}. Remove the "undocumented:" entry.`,
+ );
+ }
+
+ // Gate the allowlist on what the registry says we actually publish as products.
+ const notProducts = packs.filter((p) => !products.has(p));
+ if (notProducts.length) {
+ const detail = notProducts
+ .map((p) => `${p} (source: ${nonProducts.get(p) ?? "absent from the listing"})`)
+ .join(", ");
+ throw new Error(
+ `these entries in data/policy_packs.yaml are not published Pulumi products: ${detail}\n` +
+ ` Only packs the registry returns with source: "pulumi" may be documented. A pack\n` +
+ ` can be built in policy-packs-internal and resolvable through\n` +
+ ` /api/orgs/${org}/policypacks while still being private to the org.\n` +
+ ` Remove the entry, or publish the pack as a product before documenting it.`,
+ );
+ }
+
+ // A pack that loses product status leaves a permanent, invisible exemption behind --
+ // which is how hitrust-awsnative survived as long as it did. Harmless but stale, so
+ // this says something rather than throwing.
+ const staleExemptions = undocumented.map((e) => e.pack).filter((p) => !products.has(p));
+ if (staleExemptions.length) {
+ warn(
+ `warning: these "undocumented:" entries in data/policy_packs.yaml are no longer\n` +
+ ` published products: ${staleExemptions.join(", ")}. Drop them once confirmed.`,
+ );
+ }
+
+ // ...and the other way round: a pack we publish as a product but do not document is
+ // the same failure this check exists to catch, just pointing the other way. Anything
+ // the registry calls a product needs either an entry above or a line in
+ // `undocumented:` saying why not.
+ const accountedFor = new Set([...packs, ...undocumented.map((e) => e.pack)]);
+ const undocumentedProducts = [...products].filter((p) => !accountedFor.has(p));
+ if (undocumentedProducts.length) {
+ throw new Error(
+ `these packs are published as Pulumi products but have no reference page:\n` +
+ ` ${undocumentedProducts.join(", ")}\n` +
+ ` Add an entry to data/policy_packs.yaml, or list the pack under\n` +
+ ` "undocumented:" there with the reason it stays unlisted.`,
+ );
+ }
+
+}
+
async function main() {
+ // Required here rather than at the top so the pure helpers this module exports stay
+ // importable with no node_modules -- scripts/fetch-policy-packs.test.js runs in CI
+ // before anything is installed. Nothing above main() needs it.
+ const yaml = require("js-yaml");
const allowlist = yaml.load(fs.readFileSync(ALLOWLIST, "utf8"));
const org = allowlist.org;
const entries = allowlist.sections.flatMap((s) => s.packs);
@@ -151,6 +304,9 @@ async function main() {
const packs = entries.map((e) => e.pack);
+ const { products, nonProducts } = await fetchRegistryProducts(org);
+ auditAllowlist({ org, packs, allowlistUndocumented: allowlist.undocumented, products, nonProducts });
+
console.log(`Fetching ${packs.length} policy packs from ${API} (org: ${org})...`);
const list = await fetchJSON(`/api/orgs/${org}/policypacks`);
@@ -233,7 +389,11 @@ async function main() {
}
}
-main().catch((err) => {
- console.error(`error: ${err.message}`);
- process.exit(1);
-});
+if (require.main === module) {
+ main().catch((err) => {
+ console.error(`error: ${err.message}`);
+ process.exit(1);
+ });
+}
+
+module.exports = { parseRegistryListing, auditAllowlist, latestVersionTag, unclosedFences };
diff --git a/scripts/fetch-policy-packs.test.js b/scripts/fetch-policy-packs.test.js
new file mode 100644
index 000000000000..8aced8dbd938
--- /dev/null
+++ b/scripts/fetch-policy-packs.test.js
@@ -0,0 +1,185 @@
+// Regression coverage for the registry gate in fetch-policy-packs.js — the check that
+// decides which pre-built policy packs get a public reference page.
+//
+// It is worth testing because the failure is silent and outward-facing: getting it wrong
+// once already put a private pack (hitrust-awsnative) on the public site, and the reverse
+// direction fails *open* when the listing is read incompletely, so a product nobody
+// documented would slip past the check written to catch exactly that.
+//
+// Run: node --test scripts/fetch-policy-packs.test.js (also via scripts/run-unit-tests.sh)
+
+const test = require("node:test");
+const assert = require("node:assert");
+
+const { parseRegistryListing, auditAllowlist } = require("./fetch-policy-packs.js");
+
+// Shapes taken from the live listing for the `pulumi` org, 2026-08-25.
+const product = (name) => ({ name, source: "pulumi", publisher: "pulumi", version: "1.0.0" });
+const private_ = (name) => ({ name, source: "private", publisher: "pulumi", version: "1.0.0" });
+const listing = (...rows) => ({ policyPacks: rows });
+
+const audit = (opts) =>
+ auditAllowlist({
+ org: "pulumi",
+ packs: [],
+ allowlistUndocumented: [],
+ products: new Set(),
+ nonProducts: new Map(),
+ warn: () => {},
+ ...opts,
+ });
+
+test("parseRegistryListing separates products from privately published packs", () => {
+ const { products, nonProducts } = parseRegistryListing(
+ listing(product("cis-aws"), private_("super-policy")),
+ "pulumi",
+ );
+ assert.deepStrictEqual([...products], ["cis-aws"]);
+ assert.strictEqual(nonProducts.get("super-policy"), "private");
+});
+
+test("parseRegistryListing rejects an envelope it does not recognize", () => {
+ // The spec pins `policyPacks`; anything else means the API changed under us.
+ for (const body of [[product("cis-aws")], { items: [product("cis-aws")] }, {}, null]) {
+ assert.throws(() => parseRegistryListing(body, "pulumi"), /policyPacks/);
+ }
+});
+
+test("parseRegistryListing refuses a paginated response instead of reading page one", () => {
+ // A partial read fails documented packs that are fine AND hides undocumented products.
+ assert.throws(
+ () => parseRegistryListing({ ...listing(product("cis-aws")), continuationToken: "x" }, "pulumi"),
+ /paginated/,
+ );
+});
+
+test("parseRegistryListing blames the shape, not the allowlist, when no row is a product", () => {
+ // If `source` is renamed upstream, the honest error is "the API changed" — not the
+ // allowlist error naming every documented pack as privately published.
+ assert.throws(
+ () => parseRegistryListing(listing({ name: "cis-aws" }, { name: "nist-aws" }), "pulumi"),
+ /source/,
+ );
+});
+
+test("parseRegistryListing distinguishes a missing source field from an absent pack", () => {
+ const { nonProducts } = parseRegistryListing(
+ listing(product("cis-aws"), { name: "odd-one" }),
+ "pulumi",
+ );
+ assert.strictEqual(nonProducts.get("odd-one"), "(no source field)");
+});
+
+test("an empty listing is not treated as every pack being unpublished", () => {
+ const { products } = parseRegistryListing(listing(), "pulumi");
+ assert.strictEqual(products.size, 0);
+});
+
+test("forward: a documented pack that is not a product fails, naming its real source", () => {
+ // The hitrust-awsnative case, and the live cis-kubernetes-gcp one.
+ assert.throws(
+ () =>
+ audit({
+ packs: ["cis-kubernetes-gcp"],
+ products: new Set(["cis-kubernetes-google-cloud"]),
+ nonProducts: new Map([["cis-kubernetes-gcp", "private"]]),
+ }),
+ /cis-kubernetes-gcp \(source: private\)/,
+ );
+});
+
+test("forward: a documented pack absent from the listing says so", () => {
+ assert.throws(
+ () => audit({ packs: ["deleted-pack"], products: new Set(["cis-aws"]) }),
+ /absent from the listing/,
+ );
+});
+
+test("reverse: a product with no page and no exemption fails", () => {
+ assert.throws(
+ () => audit({ packs: ["cis-aws"], products: new Set(["cis-aws", "brand-new-product"]) }),
+ /brand-new-product/,
+ );
+});
+
+test("reverse: an exempted product passes", () => {
+ assert.doesNotThrow(() =>
+ audit({
+ packs: ["cis-aws"],
+ allowlistUndocumented: [{ pack: "aws-organizations-tag-policies", why: "hand-written page" }],
+ products: new Set(["cis-aws", "aws-organizations-tag-policies"]),
+ }),
+ );
+});
+
+test("an exemption without a why: cannot silence the check", () => {
+ assert.throws(
+ () =>
+ audit({
+ allowlistUndocumented: [{ pack: "quietly-hidden" }],
+ products: new Set(["quietly-hidden"]),
+ }),
+ /"why:"/,
+ );
+});
+
+test("a bare-string exemption is rejected rather than silently ignored", () => {
+ // `- foo` parses to a string, so `e.pack` is undefined: the pack stays unaccounted
+ // for and the check would otherwise fire naming a pack the author just listed.
+ assert.throws(
+ () => audit({ allowlistUndocumented: ["quietly-hidden"], products: new Set(["quietly-hidden"]) }),
+ /"pack:"/,
+ );
+});
+
+test("a malformed exemption is reported even when the forward check also fails", () => {
+ // Validate our own inputs before comparing them against the world: otherwise the
+ // registry mismatch throws first and the bad exemption ships unreported.
+ assert.throws(
+ () =>
+ audit({
+ packs: ["a-private-pack"],
+ allowlistUndocumented: [{ pack: "quietly-hidden" }],
+ products: new Set(["quietly-hidden"]),
+ nonProducts: new Map([["a-private-pack", "private"]]),
+ }),
+ /"why:"/,
+ );
+});
+
+test("a pack cannot be both documented and exempted", () => {
+ assert.throws(
+ () =>
+ audit({
+ packs: ["cis-aws"],
+ allowlistUndocumented: [{ pack: "cis-aws", why: "leftover" }],
+ products: new Set(["cis-aws"]),
+ }),
+ /also have\n?\s*a page above/,
+ );
+});
+
+test("an exemption for a pack that stopped being a product warns but does not fail", () => {
+ const warnings = [];
+ assert.doesNotThrow(() =>
+ audit({
+ allowlistUndocumented: [{ pack: "was-a-product", why: "superseded" }],
+ products: new Set(),
+ warn: (m) => warnings.push(m),
+ }),
+ );
+ assert.match(warnings.join("\n"), /no longer\n?\s*published products: was-a-product/);
+});
+
+test("the happy path stays quiet", () => {
+ const warnings = [];
+ assert.doesNotThrow(() =>
+ audit({
+ packs: ["cis-aws", "nist-aws"],
+ allowlistUndocumented: [{ pack: "aws-organizations-tag-policies", why: "hand-written page" }],
+ products: new Set(["cis-aws", "nist-aws", "aws-organizations-tag-policies"]),
+ warn: (m) => warnings.push(m),
+ }),
+ );
+ assert.deepStrictEqual(warnings, []);
+});
diff --git a/scripts/redirects/insights-policy-redirects.txt b/scripts/redirects/insights-policy-redirects.txt
index 94381504a9b8..315c3d0d2979 100644
--- a/scripts/redirects/insights-policy-redirects.txt
+++ b/scripts/redirects/insights-policy-redirects.txt
@@ -17,3 +17,4 @@ docs/using-pulumi/crossguard/awsguard/index.html|https://github.com/pulumi/pulum
docs/iac/packages-and-automation/crossguard/awsguard/index.html|https://github.com/pulumi/pulumi-policy-aws
docs/iac/using-pulumi/crossguard/awsguard/index.html|https://github.com/pulumi/pulumi-policy-aws
docs/insights/policy/awsguard/index.html|https://github.com/pulumi/pulumi-policy-aws
+docs/reference/pre-built-policy-packs/hitrust/aws-native/index.html|/docs/reference/pre-built-policy-packs/hitrust/aws/
diff --git a/scripts/run-unit-tests.sh b/scripts/run-unit-tests.sh
index 0561fa143502..551b627933a0 100755
--- a/scripts/run-unit-tests.sh
+++ b/scripts/run-unit-tests.sh
@@ -1,3 +1,12 @@
#!/bin/bash
-yarn --cwd components test
+# Unit tests that need no network and no Hugo build.
+#
+# This previously ran `yarn --cwd components test`, against a `components/` directory that
+# no longer exists — the script had been broken and unreferenced for some time, which is
+# why nothing noticed. It is now wired into `make test` (see the Makefile).
+
+set -o errexit -o pipefail
+
+# Node's built-in runner, so scripts/ tests need no new dependency.
+node --test scripts/*.test.js