Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
fe1e34e
feat: Changed intrinsic scanner in security guardian to exempt kms ke…
kumvprat Sep 18, 2025
bd14fd3
feat: Modified rules for catching more broad policies + changed intri…
kumvprat Sep 18, 2025
9f0e606
feat: Adding security guardian new rules and disabling intrinsic scan…
kumvprat Oct 28, 2025
648b4e1
Merge branch 'main' into security_guardian_changes
kumvprat Oct 28, 2025
ae8fc97
feat: Removed ecr image checks + reverted back for cfn guard to show …
kumvprat Oct 29, 2025
d4893bd
feat: Added cfn template resolution logic to resolve intrinsic functi…
kumvprat Oct 30, 2025
6f872d5
feat: Added cfn template resolution logic to resolve intrinsic functi…
kumvprat Oct 30, 2025
2250e2f
feat: Added code comments to explain the various intrinsic functions …
kumvprat Oct 30, 2025
d2db463
feat(security-guardian): enhance CFN intrinsic resolution and add com…
kumvprat Nov 3, 2025
9bfee59
feat: using junit format of cfn-guard to generate junit related test …
kumvprat Nov 5, 2025
02ae206
feat: Fixed with proper use of result piping from cfn guard output
kumvprat Nov 5, 2025
3adada6
chore: deleting yarn.lock file
kumvprat Nov 5, 2025
250e5b9
Merge branch 'main' into security_guardian_changes
kumvprat Nov 5, 2025
f92685f
feat: Added a complient cfn template + tests to check successful runs…
kumvprat Nov 5, 2025
23977ae
feat: Using unnormalized check of policy normalizer to ensure all the…
kumvprat Nov 6, 2025
51733b3
fix: config changes for junit publishing action(group by suites)
kumvprat Nov 6, 2025
f3d4ad1
fix: Fixing testsuite name in the junit report generated by security …
kumvprat Nov 6, 2025
f379db6
feat: abstract out postProcessXml function
kumvprat Nov 6, 2025
2a13551
feat: restore security-critical resource attributes for accurate poli…
kumvprat Nov 7, 2025
e86ada3
fix: copy name attribute to file attribute in testsuite since the jun…
kumvprat Nov 7, 2025
948717a
fix: Logic to handled file name mapping and returning the mapping ins…
kumvprat Nov 7, 2025
eaeb476
fix: Logic to handled file name mappings and resolving testsuite name…
kumvprat Nov 7, 2025
b249b28
feat: Added logic to convert failure message based on cfn template ty…
kumvprat Nov 7, 2025
4baf2a1
feat: Using proper summary table in GH workflow
kumvprat Nov 7, 2025
3145e90
feat: Eanble annotation update rather than new ones
kumvprat Nov 7, 2025
b594d29
feat: Added logic to creat 2 set ofsumaaries and use exact chagens in…
kumvprat Nov 7, 2025
fcb058b
fix: Added checks in no-root-principals guard rule that checks for sp…
kumvprat Nov 10, 2025
5ede549
fix: Added checks in no-root-principals guard rule that checks for sp…
kumvprat Nov 10, 2025
0309483
feat: Add example for suppressing a guard rule
kumvprat Nov 10, 2025
8af6924
fix: Removing datatrace.guard as it's source of lot of false positives
kumvprat Nov 10, 2025
462a413
feat: Uploading junit xmls as artifacts so that they can be used by m…
kumvprat Nov 10, 2025
1ea242a
feat: Added functionality to upload artifact and then use it in diffe…
kumvprat Nov 10, 2025
c224d48
fix: Removed checkout of specfic commit inside security-report workfl…
kumvprat Nov 10, 2025
2066bde
Merge branch 'main' into security_guardian_changes
kumvprat Nov 10, 2025
34d2fe9
feat: Fixed the pair of workflows to work with forked repo PRs as wel…
kumvprat Nov 10, 2025
7683e85
feat: Fixed the pair of workflows to work with forked repo PRs as wel…
kumvprat Nov 10, 2025
29fa0d7
feat: Fixed the pair of workflows to work with forked repo PRs as wel…
kumvprat Nov 10, 2025
5b4330c
fix :Unpacking artifacts in a single folder
kumvprat Nov 10, 2025
2504bbd
fix: Adding an exists check in iam.guard rule
kumvprat Nov 10, 2025
eb45325
fix: Remove un-neceassry tests fron guard-rules.test.ts
kumvprat Nov 11, 2025
7ca1ba2
feat: Added new tests in guard-rules.test.ts to cover the post proces…
kumvprat Nov 11, 2025
38093e3
fix: Small changes to iam.guard and also configuring test reporter to…
kumvprat Nov 12, 2025
c541aef
chore: Using v6 of the junit action reporter GH action
kumvprat Nov 12, 2025
3f23a2f
feat: Moved guard rules to different folder so that junit xml can hav…
kumvprat Nov 12, 2025
ada630c
feat: Added a toggable option to enable ehanced processing that conve…
kumvprat Nov 12, 2025
5f65cbb
feat: Added a toggable option to enable ehanced processing : Made it …
kumvprat Nov 13, 2025
b08f061
feat: Added a toggable option to enable ehanced processing : Made it …
kumvprat Nov 13, 2025
63a14c1
chore: Renamed guard files
kumvprat Nov 13, 2025
339dca7
chore: Renamed guard files => Test fixes
kumvprat Nov 13, 2025
049a606
chore: update README.md to reflect what the final raw output would lo…
kumvprat Nov 19, 2025
d82b62b
Merge branch 'main' into security_guardian_changes
kumvprat Nov 19, 2025
c4f44e4
Merge branch 'main' into security_guardian_changes
kumvprat Dec 4, 2025
a2ec1b8
feat: remove contents write permission for security-report workflow
kumvprat Dec 4, 2025
2965c05
Merge branch 'main' into security_guardian_changes
mergify[bot] Dec 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .github/workflows/security-guardian.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Security Guardian
on:
pull_request: {}
pull_request_target: {}

jobs:
log-skip:
Expand Down Expand Up @@ -34,10 +34,21 @@ jobs:
run: yarn install --frozen-lockfile && cd tools/@aws-cdk/security-guardian && yarn build

- name: Run Security Guardian
id: security-guardian
uses: ./tools/@aws-cdk/security-guardian
with:
base_sha: ${{ github.event.pull_request.base.sha }}
head_sha: ${{ github.event.pull_request.head.sha }}
rule_set_path: './tools/@aws-cdk/security-guardian/rules'
show_summary: 'fail'
output_format: 'json'
- name: Save PR info for security-report
if: always()
run: |
echo "${{ github.event.pull_request.number }}" > ./test-results/pr_number
echo "${{ github.event.pull_request.head.sha }}" > ./test-results/pr_sha

- name: Upload Security Guardian XML Reports
uses: actions/upload-artifact@v4
if: always()
with:
name: security-guardian-reports
path: test-results/
65 changes: 65 additions & 0 deletions .github/workflows/security-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Security Report
on:
workflow_run:
workflows: ["Security Guardian"]
types: [completed]

jobs:
report:
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
id-token: write
actions: read
steps:
- name: Download artifacts
uses: actions/download-artifact@v5
with:
name: security-guardian-reports
path: test-results/
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
repository: ${{ github.repository }}

- name: Get PR info
id: pr_info
run: |
echo "pr_number=$(cat test-results/pr_number)" >> "$GITHUB_OUTPUT"
echo "pr_sha=$(cat test-results/pr_sha)" >> "$GITHUB_OUTPUT"
echo "PR: $(cat test-results/pr_number), SHA: $(cat test-results/pr_sha)"
- name: Publish Security Test Results
uses: mikepenz/action-junit-report@v6
if: always()
with:
report_paths: 'test-results/**/cfn-guard-static.xml'
check_name: 'Security Guardian Results'
exclude_sources: 'node_modules,dist'
commit: ${{ steps.pr_info.outputs.pr_sha }}
check_annotations: true
comment: true
pr_id: ${{ steps.pr_info.outputs.pr_number }}
detailed_summary: true
include_passed: false
fail_on_failure: false
group_suite: true
include_skipped: false
check_title_template: '{{TEST_NAME}}'

- name: Publish Security Test Results for resolved templates
uses: mikepenz/action-junit-report@v6
if: always()
with:
report_paths: 'test-results/**/cfn-guard-resolved.xml'
check_name: 'Security Guardian Results with resolved templates'
exclude_sources: 'node_modules,dist'
commit: ${{ steps.pr_info.outputs.pr_sha }}
check_annotations: true
comment: true
pr_id: ${{ steps.pr_info.outputs.pr_number }}
detailed_summary: true
include_passed: false
fail_on_failure: false
group_suite: true
include_skipped: false
check_title_template: '{{TEST_NAME}}'
163 changes: 138 additions & 25 deletions tools/@aws-cdk/security-guardian/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ A GitHub Action and CLI tool that helps detect broadly scoped IAM principals in

- Validating **changed** `*.template.json` files in pull requests using custom [cfn-guard v3](https://github.com/aws-cloudformation/cloudformation-guard) rules.
- Detecting **broadly scoped IAM principals** using CloudFormation **intrinsic functions** (e.g., `Fn::Join` with `:root`).
- **Cross-account wildcard detection** - Flags dangerous patterns like `"*"` or `"arn:aws:iam::*:root"`.
- **Extensible exemption system** - Smart exemptions for AWS service default policies.

---

Expand All @@ -12,32 +14,92 @@ A GitHub Action and CLI tool that helps detect broadly scoped IAM principals in
Validates **only changed** templates in a PR
Supports **cfn-guard v3** with rule sets
Scans for **broad IAM principals using intrinsics**
**Flags cross-account wildcards** - Always dangerous
**Extensible exemptions** for AWS service defaults
**Future-proof validation** - Detects when AWS changes default policies
Runs locally and in GitHub Actions
Outputs human-readable and machine-parsable summaries

---

## Security Checks

### Rule Organization
Guard rules are organized in service-specific directories for granular control:

```
rules/
├── codepipeline/
│ └── cross-account-role-trust-scope.guard
├── documentdb/
│ └── encryption-enabled.guard
├── ec2/
│ ├── ebs-encryption-enabled.guard
│ └── no-open-security-groups.guard
├── guard-hooks/
│ └── no-root-principals-except-kms-secrets.guard
├── iam/
│ ├── no-overly-permissive-passrole.guard
│ ├── no-wildcard-actions.guard
│ ├── no-world-accessible-trust-policy.guard
│ ├── policy-no-broad-principals.guard
│ └── role-no-broad-principals.guard
├── s3/
│ ├── encryption-enabled.guard
│ ├── no-world-readable.guard
│ └── secure-transport.guard
└── [other services...]
```

### Always Flagged (High Risk)
- **Cross-account wildcards**: `"*"` or `"arn:aws:iam::*:root"`
- **Custom policies with root access**: Non-default policies granting root
- **Broad principals in sensitive resources**: IAM roles, S3 buckets, etc.

### Smart Exemptions (Configurable)
- **AWS KMS default policies**: Standard root access for IAM integration
- **Individual rule control**: Enable/disable specific rules per service
- **Metadata-based suppression**: Use CDK metadata to suppress specific rules

---

## Inputs (GitHub Action)

| Name | Description | Required | Default |
|------------------|------------------------------------------------------|----------|-----------------------|
| `rule_set_path` | Local path to the cfn-guard rules file | No | `./rules` |
| `show_summary` | Show summary (`none`, `all`, `pass`, `fail`, `skip`) | No | `fail` |
| `output_format` | Output format (`single-line-summary`, `json`, etc.) | No | `single-line-summary` |
| `base_sha` | Commit SHA to compare against | No | `origin/main` |
| `head_sha` | The commit SHA for the head (current) branch or PR | No | `HEAD` |
| Name | Description | Required | Default |
|-----------------|-------------------------------------------|----------|---------------|
| `rule_set_path` | Local path to the cfn-guard rules file | Yes | N/A |
| `base_sha` | Commit SHA to compare against | No | `origin/main` |
| `head_sha` | The commit SHA for the head branch or PR | No | `HEAD` |
| `enhance_xml` | Enable XML enhancement for individual failure annotations | No | `true` |

## Outputs (GitHub Action)

| Name | Description |
|--------------|-------------------------------------------|
| `junit_files`| Comma-separated list of JUnit XML files |
| `all_passed` | Whether all validations passed |

---

## Usage (GitHub Action)

```yaml
- name: Run Security Guardian
id: security-guardian
uses: ./tools/@aws-cdk/security-guardian
with:
rule_set_path: './tools/@aws-cdk/security-guardian/rules'
show_summary: 'fail'
output_format: 'single-line-summary'
enhance_xml: 'true' # Optional: Enable individual failure annotations (default: true)

- name: Publish Security Test Results
uses: mikepenz/action-junit-report@e08919a3b1fb83a78393dfb775a9c37f17d8eea6 # v6.0.1
if: always()
with:
report_paths: 'test-results/*.xml'
check_name: 'Security Guardian Results'
detailed_summary: true
include_passed: true
fail_on_failure: true
```

---
Expand All @@ -58,32 +120,83 @@ yarn security-guardian

> You can override defaults using:
> - `--base_sha=origin/main`
> - `--output_format=json`
> - `--show_summary=warn`
> - `--rule_set_path=./custom-rules`

---

## Output

In addition to validation results from `cfn-guard`, the tool logs detailed findings from the intrinsic scan (if applicable), such as:
The tool generates JUnit XML reports that can be consumed by GitHub Actions:

- `test-results/cfn-guard-static.xml` - Results from original templates
- `test-results/cfn-guard-resolved.xml` - Results from templates with resolved intrinsics

Use `mikepenz/action-junit-report@e08919a3b1fb83a78393dfb775a9c37f17d8eea6` (v6.0.1) to display rich test results in GitHub PRs with:
- **Enhanced failure messages** - Automatically parses and formats concatenated CFN Guard failures
- **Precise line numbers** - Exact file locations for each violation
- **Resource identification** - Clear resource names and property paths
- **Rule descriptions** - Detailed explanations and remediation guidance

### Enhanced Failure Formatting

The tool automatically enhances CFN Guard failure messages by:
- Splitting concatenated failure messages into individual violations
- Extracting exact line numbers and column positions
- Identifying specific CloudFormation resources and properties
- Formatting output for better readability in CI/CD reports

**Before (Raw CFN Guard Output):**
```
IAM_NO_WILDCARD_ACTIONS_INLINE for Type: ResolvedCheck was not compliant as property [Policies[*].PolicyDocument.Statement[*]] is missing. Value traversed to [Path=/Resources/Role1/Properties[L:324,C:20]]Check was not compliant as property [Policies[*].PolicyDocument.Statement[*]] is missing. Value traversed to [Path=/Resources/Role2/Properties[L:485,C:20]]
```

**After (Enhanced Format):**
```
detailed_output File: changed_templates/example.template.json
{
"Action": "kms:*",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
["arn:", { "Ref": "AWS::Partition" }, ":iam::", { "Ref": "AWS::AccountId" }, ":root"]
]
}
},
"Resource": "*"
Rule: IAM_NO_WILDCARD_ACTIONS_INLINE (Type: Resolved)
==================================================

- Check was not compliant as property [Policies[*].PolicyDocument.Statement[*]] is missing. Value traversed to [Path=/Resources/Role1/Properties[L:324,C:20]]
- Check was not compliant as property [Policies[*].PolicyDocument.Statement[*]] is missing. Value traversed to [Path=/Resources/Role2/Properties[L:485,C:20]]
```

---

## Suppressing Rules

For integration tests or specific resources that generate false positives, you can suppress individual rules using CDK metadata:

```typescript
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class MyTestStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);

const testBucket = new Bucket(this, 'TestBucket', {
autoDeleteObjects: true,
removalPolicy: RemovalPolicy.DESTROY,
});

// Suppress S3 encryption rule for integration test
testBucket.node.addMetadata('guard', {
SuppressedRules: ['S3_ENCRYPTION_ENABLED']
});
}
}
```

**Available Rules to Suppress:**
- `S3_ENCRYPTION_ENABLED`
- `IAM_ROLE_NO_BROAD_PRINCIPALS`
- `IAM_NO_WILDCARD_ACTIONS`
- `NO_ROOT_PRINCIPALS_EXCEPT_KMS_SECRETS`
- `EBS_ENCRYPTION_ENABLED`
- `SNS_ENCRYPTION_ENABLED`
- `SQS_ENCRYPTION_ENABLED`
- And others (see individual `.guard` files in service subdirectories)

---

## Acknowledgments
Expand Down
25 changes: 15 additions & 10 deletions tools/@aws-cdk/security-guardian/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@ name: 'Security Guardian'
description: 'Security Guardian for custom or granular guard rules'

inputs:
data_directory:
description: "Path to CloudFormation templates"
required: true
base_sha:
description: "Base commit id"
required: false
head_sha:
description: "Current changes commit id"
required: false
rule_set_path:
description: "Path to a single .guard file locally"
required: true
show_summary:
description: "cfn-guard summary output. Options are all, pass, fail, skip or none"
enhance_xml:
description: "Enable XML enhancement for individual failure annotations"
required: false
default: "fail"
output_format:
description: "cfn-guard output format. Options: json, yaml, single-line-summary"
required: false
default: "single-line-summary"
default: 'true'

outputs:
junit_files:
description: "Comma-separated list of JUnit XML files"
all_passed:
description: "Whether all validations passed"

runs:
using: node20
Expand Down
Loading
Loading