Package: c7n-left
Custodian policies for Infrastructure CodeThis package allows cloud custodian to evaluate policies directly
against infrastructure as code source assets.It also provides a separate cli for better command line ux for
source asset evaluation.InstallWe currently only support python > 3.10 on mac and linux, to run on windows
we recommend using our docker images.pipinstallc7n_leftWe also provide signed docker images. These images are built on top of chainguard'swolfi linux
distributionwhich
is designed to be minimal, auditable, and secure.dockerpullcloudcustodian/c7n-left:devImages signatures can be verified usingcosignexport IMAGE=$(docker image inspect cloudcustodian/c7n-left:dev -f '{{index .RepoDigests 0}}')
cosign verify $IMAGE \
   --certificate-identity 'https://github.com/cloud-custodian/cloud-custodian/.github/workflows/docker.yml@refs/heads/main' \
   --certificate-oidc-issuer 'https://token.actions.githubusercontent.com'Usage❯c7n-leftrun--help
Usage:c7n-leftrun[OPTIONS]evaluatepoliciesagainstIaCsources.c7n-left-ppolicy_dir-dterraform_root--filters"severity=HIGH"WARNING-CLIinterfacesubjecttochange.

Options:--formatTEXT--filtersTEXTFilterpoliciesorresourcesask=vpairswithglobbing--warn-onTEXTSelectpoliciestologinsteadoffailonviak=vpairswithglobbing-p,--policy-dirPATHDirectorywithpolicies-d,--directoryPATHIaCdirectorytoevaluate-o,--output[cli|github|json]Outputformat(defaultcli)--output-fileFILENAMEOutputfile(defaultstdout)--var-fileFILELoadvariablesfromthegivenfile,canbeusedmorethanonce--output-queryTEXTUseajmespathexpressiontofilterjsonoutput--summary[policy|resource]--helpShowthismessageandexit.We'll create an empty directory with a policy in itpolicies:-name:testresource:terraform.aws_s3_bucketmetadata:severity:mediumfilters:-server_side_encryption_configuration:absentAnd now we can use it to evaluate a terraform root module❯c7n-leftrun-ppolicies-dmodule
Running1policieson1resourcestest-terraform.aws_s3_bucketFailedFile:s3.tf:1-81resource"aws_s3_bucket""example"{2bucket="my-custodian-test-bucket"3acl="private"45tags={6original-tag="original-value"7}8}Evaluationcomplete0.00seconds->1FailuresSummary-ByPolicy┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃Severity┃Policy┃Result┃
┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│medium│test│1failed0passed│
└──────────┴────────┴───────────────────┘0compliantof1total,1resourcehas1policyviolationFor running in docker, you'll need to use volume mounts to provide access to
the policy directory and terraform root module.dockerrun-ti--rm-v$(pwd)/policies:/policies-v$(pwd)/root-module:/module\cloudcustodian/c7n-left:devrun-p/policies-d/moduleIf the terraform root module has other remote module dependencies, you'll need to fetch those first using terraform
before running c7n-left.terraformget-updateCLI FiltersWhich policies and which resources are evaluated can be controlled via
command line via--filtersoption.Available filtersname- policy namecategory- policy categoryseverity- minimum policy severity (unknown, low, medium, high, critical)type- resource type, ie. aws_security_groupid- resource id  ie. aws_vpc.exampleMultiple values for a given filter can be specified as comma separate values, and all filters
except severity support globbing.Examples# run all encryption policies on ebs volumes and sqs queues
c7n-left run -p policy_dir -d terraform --filters="category=encryption type=aws_ebs_volume,aws_sqs_queue"

# run all medium and higher level policies cost policies
c7n-left run -p policy_dir -d terraform --filters="severity=medium category=cost"policy values for severity and category are specified in its metadata section. iepolicies:-name:check-encryptionresource:[terraform.aws_ebs_volume,terraform.aws_sqs_queue]metadata:category:[encryption,security]severity:highfilters:-kms_master_key_id:absentOutputsif your using this in github actions, we have special output mode for
reporting annotations directly into pull requests with--output githubWe also display a summary output after displaying resource matches,
there are two summary displays available, the default policy summary,
and a resource summary which can be enabled via--summary resource.By default any policy matches cause a run to exit code 1 to mark failure,
this behavior can be controlled via the--warn-oncli flag. ie. given a policy
withpolicies:-name:check-encryptionresource:[terraform.aws_ebs_volume,terraform.aws_sqs_queue]metadata:category:[beta,security]severity:highrunning the policy with--warn-on category=betawill cause matches to be logged only instead
of causing an exit code 1.Policy LanguageStandard Custodian filters (value,list-item,and,or,not,reduceandevent) are availablePolicies for c7n-left support a few additional capabilities beyond what's common for custodian policies.Policies can be specified against multiple resource types either as an array or glob.policies:-name:check-encryptionresource:[aws_ebs_volume,aws_sqs_queue]taggable filterAtaggablefilter is available that allows filtering to only resources that support tagging.In combination with resource wild card support, this allows using a single policy to enforce
an organization's tag standards.policies:-name:check-tag-policyresource:"terraform.aws*"filters:-taggable-or:-tag:Env:absent-tag:Owner:absent-tag:App:absentThis filter supports resources from several terraform providers including aws, azure, gcp, oci, tencentcloud.terraform providers that support default_tags have those values automatically available on the applicable resources.traverse filterAtraversefilter is available that allows for multi-hop graph traversal from a resource
to any related resource.ie, here's a policy against an aws ec2 instance, that checks if any of the security
groups attached to the instance, have a permission defined that allows access from
0.0.0.0/0policies:-name:check-security-group-open-cidrresource:terraform.aws_instancedescription:"EC2shouldnotbeopentoworldonssh"filters:-type:traverseresources:-aws_security_group-aws_security_ingress_permissionattrs:-Ipv4:0.0.0.0/0terraform data resourcesPolicies can also be written against data resources, note data
resources are prefixed withdata..policies:-name:check-ami-dataresource:terraform.data.aws_amifilters:-type:valuekey:ownersop:containsvalue:"099720109477"# Canonical/ubuntuand you cantraversefrom a resource to its data usage as well.policies:-name:check-owner-specifiedresource:terraform.aws_instancefilters:-type:traverseresources:data.aws_amiattrs:-owners:presentenvironment variablesc7n-left is typically run in CI systems, which provide a wealth
of information in environment variables. Policies can make use
of these environment variables in two different ways.They can be used to interpolate the content of a policy, where they
will they will be substituted prior to the policy execution. Note this
uses python'sformat capabiltiespolicies:-name:"check-{env[REPO]}-{env[PR_NUMBER]}"resource:terraform.aws*Additionally they can be evaluated by the policy using theeventfilterpolicies:-name:check-awsresource:terraform.aws*filters:-type:eventkey:env.REPOvalue:"cloud-custodian/cloud-custodian"Policy Testingc7n-left supports writing and running tests for policies.To create a test for a policy, create a tests directory next to your policy files.Within that tests directory, create a sub directory with the policy name.Next add terraform files to this sub directory. Typically you would add
both terraform files that would match the policy and those that should not.Finally you add assertions in aleft.plan[.yaml|.json]file. The
format of the file is an array of dictionaries. The dictionaries are
used to match against the policy findings. The data its matching
against is what is found by usingc7n-left run --output json. Each
key/value pair in the dictionary is matched against the finding.So putting it all together, we've setup our tests as follows❯treepolicy-dir-a/
policy-dir-a/
├──alb.yaml
└──tests└──alb-deletion-protection-disabled├──left.plan.yaml├──negative1.tf└──positive1.tf3directories,4files

❯catpolicy-dir-a/alb.yaml
policies:-name:alb-deletion-protection-disabledresource:[terraform.aws_lb,terraform.aws_alb]description:|ApplicationLoadBalancershouldhavedeletionprotectionenabledmetadata:severity:lowcategory:"Insecure Configurations"filters:-enable_deletion_protection:empty

❯catpolicy-dir-a/tests/alb-deletion-protection-disabled/left.plan.yaml
-"resource.__tfmeta.filename":"positive1.tf"and now we can run a test❯c7n-lefttest-ppolicy-dir-a/
Discovered1Tests
Failurealb-deletion-protection-disabled[{'resource.__tfmeta.filename':'positive1.tf'}]checksnotused1TestComplete(0.05s)1FailureA test fails if either an assertion in the plan file does not match one policy finding, or if a policy finding is not matched by an assertion.