Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
25 changes: 21 additions & 4 deletions .github/scripts/sync-copywriter-changes/sync_md_to_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@

def extract_informations_from_md(
content: str,
) -> tuple[str | None, str | None, str | None, str | None]:
) -> tuple[str | None, str | None, str | None, str | None, str | None]:
"""
Extract the informations from markdown content.

Args:
content: Markdown content (full file including frontmatter)

Returns:
Query name, description, severity and category
Query name, description, severity, category and provider_url
"""

# Extract query name from title in frontmatter
Expand Down Expand Up @@ -64,7 +64,15 @@ def extract_informations_from_md(
if category_match:
category = category_match.group(1).strip()

return query_name, description, severity, category
# Extract Provider Reference URL from Learn More section
provider_url = None
provider_url_pattern = r'\[Provider Reference\]\((https?://[^)]+)\)'
provider_url_match = re.search(provider_url_pattern, content)

if provider_url_match:
provider_url = provider_url_match.group(1).strip()

return query_name, description, severity, category, provider_url


def find_json_paths(
Expand Down Expand Up @@ -130,6 +138,7 @@ def update_metadata_json(
description: str | None,
severity: str | None,
category: str | None,
provider_url: str | None = None,
dry_run: bool = False,
) -> bool:
"""
Expand Down Expand Up @@ -186,6 +195,13 @@ def update_metadata_json(
if not dry_run:
metadata["category"] = category

# Update providerUrl
if provider_url and metadata.get("providerUrl") != provider_url:
old_provider_url = metadata.get("providerUrl", "")
changes.append(f" providerUrl: '{old_provider_url}' -> '{provider_url}'")
if not dry_run:
metadata["providerUrl"] = provider_url

if changes:
print(f"\n{'[DRY RUN] ' if dry_run else ''}Updating {metadata_path}:")
for change in changes:
Expand Down Expand Up @@ -308,7 +324,7 @@ def sync_md_to_metadata(
content = f.read()

# Extract information from full markdown content
name, description, severity, category = extract_informations_from_md(
name, description, severity, category, provider_url = extract_informations_from_md(
content
)

Expand All @@ -334,6 +350,7 @@ def sync_md_to_metadata(
description,
severity,
category,
provider_url,
dry_run,
)
positive_updated = update_positive_expected_result_json(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"id": "f81d63d2-c5d7-43a4-a5b5-66717a41c895",
"queryName": "ALB Listening on HTTP",
"queryName": "ALB listening on HTTP",
"severity": "MEDIUM",
"category": "Networking and Firewall",
"descriptionText": "AWS Application Load Balancer (alb) should not listen on HTTP",
"descriptionText": "Application Load Balancers (ALB) must terminate TLS and use HTTPS listeners to protect traffic in transit and prevent interception or downgrade attacks. Serving application traffic over plain HTTP exposes credentials and sensitive data to eavesdropping.\n\nFor Ansible ALB resources (modules `amazon.aws.elb_application_lb` and `elb_application_lb`), ensure the `listeners[].Protocol` property is set to `\"HTTPS\"`. Resources missing the `Protocol` property or with `Protocol` set to any value other than `\"HTTPS\"` are flagged. When using HTTPS, also configure a valid TLS certificate (for example via `Certificates: - CertificateArn: ...`) or implement an HTTP listener only to perform redirects to HTTPS rather than serving plaintext.\n\nSecure configuration example:\n\n```yaml\n- name: Create ALB with HTTPS listener\n amazon.aws.elb_application_lb:\n name: my-alb\n state: present\n listeners:\n - Protocol: HTTPS\n Port: 443\n Certificates:\n - CertificateArn: arn:aws:acm:us-east-1:123456789012:certificate/abcdef01-2345-6789-abcd-ef0123456789\n DefaultActions:\n - Type: forward\n TargetGroupArn: arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/abcdef0123456789\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/alb_listening_on_http",
"platform": "Ansible",
"descriptionID": "3a7576e5",
"cloudProvider": "aws",
"cwe": "319",
"oldSeverity": "HIGH",
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/community/aws/elb_application_lb_module.html"
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/amazon/aws/elb_application_lb_module.html"
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[
{
"queryName": "ALB Listening on HTTP",
"queryName": "ALB listening on HTTP",
"severity": "MEDIUM",
"line": 11
},
{
"queryName": "ALB Listening on HTTP",
"queryName": "ALB listening on HTTP",
"severity": "MEDIUM",
"line": 29
}
]
]
4 changes: 2 additions & 2 deletions assets/queries/ansible/aws/ami_not_encrypted/metadata.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"id": "97707503-a22c-4cd7-b7c0-f088fa7cf830",
"queryName": "AMI Not Encrypted",
"queryName": "AMI not encrypted",
"severity": "MEDIUM",
"category": "Encryption",
"descriptionText": "AWS AMI Encryption is not enabled",
"descriptionText": "AMIs must have their block device mappings encrypted to protect data at rest and prevent sensitive information from being exposed if snapshots are copied, shared, or recovered on different storage.\n\nFor Ansible tasks using the `amazon.aws.ec2_ami` or `ec2_ami` modules, each entry in the `device_mapping` must include `encrypted: true`. Resources missing the `encrypted` attribute or with `encrypted: false` are flagged. Ensure every device mapping explicitly sets `encrypted: true` so AMI snapshots and derived volumes remain encrypted.\n\nSecure configuration example:\n\n```yaml\n- name: Create AMI with encrypted device mapping\n amazon.aws.ec2_ami:\n name: my-encrypted-ami\n device_mapping:\n - device_name: /dev/sda1\n encrypted: true\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/ami_not_encrypted",
"platform": "Ansible",
"descriptionID": "a4342f08",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[
{
"queryName": "AMI Not Encrypted",
"queryName": "AMI not encrypted",
"severity": "MEDIUM",
"line": 6
},
{
"queryName": "AMI Not Encrypted",
"queryName": "AMI not encrypted",
"severity": "MEDIUM",
"line": 13
}
]
]
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"id": "a19b2942-142e-4e2b-93b7-6cf6a6c8d90f",
"queryName": "AMI Shared With Multiple Accounts",
"queryName": "AMI shared with multiple accounts",
"severity": "MEDIUM",
"category": "Access Control",
"descriptionText": "Limits access to AWS AMIs by checking if more than one account is using the same image",
"descriptionText": "AMIs must not be broadly shared. Granting multiple AWS accounts or group-based access increases the attack surface and can expose embedded credentials, custom configurations, or vulnerable images to unintended parties.\n\nFor Ansible tasks using the `amazon.aws.ec2_ami` or `ec2_ami` modules, `launch_permissions` should be restricted to at most one explicit AWS account and must not include `group_names`. This rule flags tasks where `launch_permissions.group_names` is present or where `launch_permissions.user_ids` contains more than one entry. \n\nSecure example with a single allowed account:\n\n```yaml\n- name: Register AMI with restricted launch permissions\n amazon.aws.ec2_ami:\n name: my-ami\n image_id: ami-0123456789abcdef0\n launch_permissions:\n user_ids:\n - \"123456789012\"\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/ami_shared_with_multiple_accounts",
"platform": "Ansible",
"descriptionID": "2117f1c7",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[
{
"queryName": "AMI Shared With Multiple Accounts",
"queryName": "AMI shared with multiple accounts",
"severity": "MEDIUM",
"line": 6
},
{
"queryName": "AMI Shared With Multiple Accounts",
"queryName": "AMI shared with multiple accounts",
"severity": "MEDIUM",
"line": 13
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"id": "559439b2-3e9c-4739-ac46-17e3b24ec215",
"queryName": "API Gateway Endpoint Config is Not Private",
"queryName": "API Gateway endpoint config is not private",
"severity": "MEDIUM",
"category": "Networking and Firewall",
"descriptionText": "The API Endpoint type in API Gateway should be set to PRIVATE so it's not exposed to the public internet",
"descriptionText": "API Gateway endpoint type must be set to `PRIVATE` to prevent the API from being exposed to the public internet, which increases attack surface and can enable unauthorized access or data exfiltration.\n\nFor Ansible tasks using the `community.aws.api_gateway` or `api_gateway` modules, the `endpoint_type` property must be defined and set to `PRIVATE`. Tasks missing this property or with `endpoint_type` not set to `PRIVATE` are flagged. A `PRIVATE` endpoint restricts access to VPC endpoints, so ensure the required VPC endpoint and networking is configured to allow authorized clients to reach the API.\n\nSecure Ansible task example:\n\n```yaml\n- name: Create private API Gateway\n community.aws.api_gateway:\n name: my-private-api\n endpoint_type: PRIVATE\n state: present\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/api_gateway_endpoint_config_is_not_private",
"platform": "Ansible",
"descriptionID": "42fabc16",
"cloudProvider": "aws",
"cwe": "285",
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/community/aws/aws_api_gateway_module.html"
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/community/aws/api_gateway_module.html"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"queryName": "API Gateway Endpoint Config is Not Private",
"queryName": "API Gateway endpoint config is not private",
"severity": "MEDIUM",
"line": 9
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"id": "72a931c2-12f5-40d1-93cc-47bff2f7aa2a",
"queryName": "API Gateway With CloudWatch Logging Disabled",
"queryName": "API Gateway with CloudWatch Logs disabled",
"severity": "MEDIUM",
"category": "Observability",
"descriptionText": "AWS CloudWatch Logs for APIs is not enabled",
"descriptionText": "APIs must send request logs and execution traces to CloudWatch Logs so activity, errors, and suspicious behavior can be detected and investigated. Without a configured log group, you lose critical visibility for incident response and troubleshooting.\n\nIn Ansible, tasks using the `amazon.aws.cloudwatchlogs_log_group` or `cloudwatchlogs_log_group` modules must include the `log_group_name` property to create or reference a specific CloudWatch Logs group. Tasks missing `log_group_name` (or with it unset) are flagged. Set `log_group_name` to a stable, descriptive string and ensure API Gateway access logging or tracing is pointed to that group.\n\nSecure configuration example:\n\n```yaml\n- name: Create CloudWatch log group for API Gateway\n amazon.aws.cloudwatchlogs_log_group:\n log_group_name: \"/aws/apigateway/my-api\"\n state: present\n retention_in_days: 30\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/api_gateway_with_cloudwatch_logging_disabled",
"platform": "Ansible",
"descriptionID": "313709e8",
"cloudProvider": "aws",
"cwe": "778",
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/community/aws/cloudwatchlogs_log_group_module.html#ansible-collections-community-aws-cloudwatchlogs-log-group-module"
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/amazon/aws/cloudwatchlogs_log_group_module.html#ansible-collections-community-aws-cloudwatchlogs-log-group-module"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"queryName": "API Gateway With CloudWatch Logging Disabled",
"queryName": "API Gateway with CloudWatch Logs disabled",
"severity": "MEDIUM",
"line": 3
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"id": "b16cdb37-ce15-4ab2-8401-d42b05d123fc",
"queryName": "API Gateway Without Configured Authorizer",
"queryName": "API Gateway without configured authorizer",
"severity": "MEDIUM",
"category": "Access Control",
"descriptionText": "API Gateway REST API should have an API Gateway Authorizer",
"descriptionText": "API Gateway REST APIs must have an API Gateway authorizer configured so that requests are authenticated before reaching backend integrations. Without an authorizer, APIs can be invoked anonymously, increasing the risk of unauthorized access, data exposure, and abuse of backend services.\n\nFor Ansible resources using `community.aws.api_gateway` or `api_gateway`, ensure the API's Swagger/OpenAPI definition—provided via the `swagger_file`, `swagger_dict`, or `swagger_text` property—includes an `x-amazon-apigateway-authorizer` entry in `components.securitySchemes` and that operations reference the authorizer (via `security` at the operation or global level).\n\nResources that omit all three swagger properties, or whose Swagger/OpenAPI content does not contain `x-amazon-apigateway-authorizer`, are flagged as missing an authorizer. Include a valid authorizer definition and reference it from your paths to remediate the finding.\n\nSecure example with an OpenAPI components authorizer and operation-level security:\n\n```yaml\nopenapi: \"3.0.1\"\ncomponents:\n securitySchemes:\n MyLambdaAuthorizer:\n x-amazon-apigateway-authorizer:\n type: token\n authorizerUri: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:MyAuthFunction/invocations\nsecurity:\n - MyLambdaAuthorizer: []\npaths:\n /resource:\n get:\n security:\n - MyLambdaAuthorizer: []\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/api_gateway_without_configured_authorizer",
"platform": "Ansible",
"descriptionID": "e7b28671",
"cloudProvider": "aws",
"cwe": "284",
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/community/aws/aws_api_gateway_module.html"
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/community/aws/api_gateway_module.html"
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
[
{
"queryName": "API Gateway Without Configured Authorizer",
"queryName": "API Gateway without configured authorizer",
"severity": "MEDIUM",
"line": 4,
"fileName": "positive1.yaml"
},
{
"queryName": "API Gateway Without Configured Authorizer",
"queryName": "API Gateway without configured authorizer",
"severity": "MEDIUM",
"line": 2,
"fileName": "positive2.yaml"
},
{
"queryName": "API Gateway Without Configured Authorizer",
"queryName": "API Gateway without configured authorizer",
"severity": "MEDIUM",
"line": 4,
"fileName": "positive3.yaml"
},
{
"queryName": "API Gateway Without Configured Authorizer",
"queryName": "API Gateway without configured authorizer",
"severity": "MEDIUM",
"line": 4,
"fileName": "positive4.yaml"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"id": "b47b98ab-e481-4a82-8bb1-1ab39fd36e33",
"queryName": "API Gateway Without SSL Certificate",
"queryName": "API Gateway without SSL certificate",
"severity": "MEDIUM",
"category": "Insecure Configurations",
"descriptionText": "SSL Client Certificate should be enabled",
"descriptionText": "API Gateway integrations must validate TLS/SSL certificates to ensure backend endpoints are authentic and prevent man-in-the-middle attacks that can expose credentials or sensitive data.\n\nThe `validate_certs` property in Ansible `community.aws.api_gateway` and `api_gateway` tasks must be defined and set to a truthy value (Ansible `yes` or `true`). Resources missing this property or with `validate_certs` set to `no` or `false` are flagged.\n\nIf your backend uses self-signed certificates, prefer adding the CA to a trusted store or using proper certificate management rather than disabling certificate validation.\n\nSecure example Ansible task:\n\n```yaml\n- name: Create API Gateway with TLS validation\n community.aws.api_gateway:\n name: my-api\n state: present\n validate_certs: yes\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/api_gateway_without_ssl_certificate",
"platform": "Ansible",
"descriptionID": "82608f36",
"cloudProvider": "aws",
"cwe": "295",
"providerUrl": "https://docs.ansible.com/ansible/2.8/modules/aws_api_gateway_module.html"
"providerUrl": "https://docs.ansible.com/ansible/latest/collections/community/aws/api_gateway_module.html"
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
[
{
"queryName": "API Gateway Without SSL Certificate",
"severity": "MEDIUM",
"line": 7
},

{
"queryName": "API Gateway Without SSL Certificate",
"severity": "MEDIUM",
"line": 9
},

{
"queryName": "API Gateway Without SSL Certificate",
"severity": "MEDIUM",
"line": 24
},

{
"queryName": "API Gateway Without SSL Certificate",
"severity": "MEDIUM",
"line": 26
}
{
"queryName": "API Gateway without SSL certificate",
"severity": "MEDIUM",
"line": 7
},
{
"queryName": "API Gateway without SSL certificate",
"severity": "MEDIUM",
"line": 9
},
{
"queryName": "API Gateway without SSL certificate",
"severity": "MEDIUM",
"line": 24
},
{
"queryName": "API Gateway without SSL certificate",
"severity": "MEDIUM",
"line": 26
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"queryName": "API Gateway without WAF",
"severity": "MEDIUM",
"category": "Networking and Firewall",
"descriptionText": "API Gateway should have WAF (Web Application Firewall) enabled",
"descriptionText": "API Gateway stages should be protected by an AWS WAF Web ACL to block common web threats (for example SQL injection, XSS, and malicious request patterns) before they reach backend services. Ensure your IaC defines a WAFv2 WebACLAssociation that links a Web ACL to the API Gateway stage. The association's `ResourceArn` (or Terraform `resource_arn`) must reference the API Gateway stage ARN (for REST APIs: arn:aws:apigateway:<region>::/restapis/<api-id>/stages/<stage-name>).\n\nThis rule checks Ansible API Gateway resources (modules `community.aws.api_gateway` or `api_gateway`) and expects a corresponding WAFv2 association (for example, `community.aws.wafv2_resources`/`wafv2_resources`) that targets the same stage. Resources missing a WebACLAssociation or where `ResourceArn` does not point to the stage are flagged.\n\nSecure CloudFormation example:\n\n```yaml\nWebACLAssociation:\n Type: AWS::WAFv2::WebACLAssociation\n Properties:\n ResourceArn: !Sub \"arn:aws:apigateway:${AWS::Region}::/restapis/${ApiId}/stages/${StageName}\"\n WebACLArn: !Ref MyWebACL\n```",
"descriptionUrl": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/ansible/aws/api_gateway_without_waf",
"platform": "Ansible",
"descriptionID": "8e789062",
Expand Down
Loading
Loading