From de26750fa2d97099272156238041968abeb3b95b Mon Sep 17 00:00:00 2001 From: Josh Fried <112121129+joshfried-aws@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:09:00 -0500 Subject: [PATCH] chore: migrating from inflector to cruet + updating references from 3.1.1 to 3.1.2 (#588) --- ATTRIBUTION | 26 +++ Cargo.lock | 26 +-- README.md | 160 ++++++++++-------- guard-examples/library/Cargo.toml | 2 +- guard-ffi/Cargo.toml | 4 +- guard-lambda/Cargo.toml | 4 +- guard/Cargo.toml | 4 +- .../validate/output-dir/structured.sarif | 4 +- guard/src/rules/eval_context.rs | 16 +- guard/tests/validate.rs | 2 +- 10 files changed, 149 insertions(+), 99 deletions(-) diff --git a/ATTRIBUTION b/ATTRIBUTION index 2d99a8811..eca652c3c 100644 --- a/ATTRIBUTION +++ b/ATTRIBUTION @@ -2863,3 +2863,29 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-- +chrislearn/cruet + +Copyright 2017 Josh Teeter + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Cargo.lock b/Cargo.lock index 2452a787b..cd0ab733e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "aho-corasick" version = "0.7.20" @@ -211,13 +201,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfn-guard" -version = "3.1.1" +version = "3.1.2" dependencies = [ - "Inflector", "chrono", "clap", "clap_complete", "colored", + "cruet", "enumflags2", "enumflags2_derive", "fancy-regex", @@ -248,7 +238,7 @@ dependencies = [ [[package]] name = "cfn-guard-ffi" -version = "3.1.1" +version = "3.1.2" dependencies = [ "cfn-guard", "ffi-support", @@ -354,6 +344,16 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cruet" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6132609543972496bc97b1e01f1ce6586768870aeb4cabeb3385f4e05b5caead" +dependencies = [ + "once_cell", + "regex", +] + [[package]] name = "diff" version = "0.1.13" diff --git a/README.md b/README.md index dfbb2d2e3..aa93e08bc 100644 --- a/README.md +++ b/README.md @@ -4,24 +4,24 @@ AWS CloudFormation Guard is an open-source general-purpose policy-as-code evaluation tool. It provides developers with a simple-to-use, yet powerful and expressive domain-specific language (DSL) to define policies and enables developers to validate JSON- or YAML- formatted structured data with those policies. -The Guard 3.0 release introduces support for stateful rules through built-in functions, SAM CLI as an alternative deployment method for `cfn-guard-lambda`, command auto-completions for shell, advanced regular expressions, improved handling of intrinsic functions for the test command, as well as the `--structured` flag to the validate command to emit JSON/YAML parseable output. Note that previously written tests may have to be reviewed due to the corrected behavior of intrinsic function handling. Please see the release notes for full details and examples. +The Guard 3.0 release introduces support for stateful rules through built-in functions, SAM CLI as an alternative deployment method for `cfn-guard-lambda`, command auto-completions for shell, advanced regular expressions, improved handling of intrinsic functions for the test command, as well as the `--structured` flag to the validate command to emit JSON/YAML parseable output. Note that previously written tests may have to be reviewed due to the corrected behavior of intrinsic function handling. Please see the release notes for full details and examples. Guard can be used for the following domains: 1. **Preventative Governance and Compliance (shift left):** validate Infrastructure-as-code (IaC) or infrastructure/service compositions such as CloudFormation Templates, CloudFormation ChangeSets, Terraform JSON configuration files, Kubernetes configurations, and more against Guard policies representing your organizational best practices for security, compliance, and more. For example, developers can use Guard policies with - 1. Terraform plan (**in JSON format**) for deployment safety assessment checks or Terraform state files to detect live state deviations. - 2. Static assessment of IaC templates to determine network reachability like Amazon Redshift cluster deployed inside a VPC and prevent the provision of such stacks. + 1. Terraform plan (**in JSON format**) for deployment safety assessment checks or Terraform state files to detect live state deviations. + 2. Static assessment of IaC templates to determine network reachability like Amazon Redshift cluster deployed inside a VPC and prevent the provision of such stacks. 2. **Detective Governance and Compliance:** validate conformity of Configuration Management Database (CMDB) resources such as AWS Config-based configuration items (CIs). For example, developers can use Guard policies against AWS Config CIs to continuously monitor state of deployed AWS and non-AWS resources, detect violations from policies, and trigger remediation. 3. **Deployment Safety:** validate CloudFormation ChangeSets to ensure changes are safe before deployment. For example, renaming an Amazon DynamoDB Table will cause a replacement of the Table. With Guard 3.0, you can prevent such changes in your CI/CD pipelines. > **NOTE**: If you are using Guard 1.0 rules, we highly recommend adopting the latest version of Guard to simplify your current policy-as-code experience. Guard 2.0 and higher versions are backward incompatible with your Guard 1.0 rules and can result in breaking changes. The Guard 2.0 release was a complete re-write of the earlier 1.0 version to make the tool general-purpose. > > To migrate from Guard 1.0 rules to use the updated grammar, follow the steps given below. +> > 1. Pull the release artifacts for the latest `2.x.x` release from the corresponding release page listed [here](https://github.com/aws-cloudformation/cloudformation-guard/releases). > 2. Use `migrate` command to transition your existing 1.0 rules to use the updated grammar > 3. Read about all new Guard features from the latest release, and modify your rules for enhanced experience - **Guard In Action** ![Guard In Action](images/guard-demo.gif) @@ -30,48 +30,55 @@ Guard can be used for the following domains: Take this [survey](https://amazonmr.au1.qualtrics.com/jfe/form/SV_bpyzpfoYGGuuUl0) to provide feedback about cfn-guard ## Table of Contents + -* [FAQs](#faqs) -* [Guard DSL](#guard-dsl) - * [Tenets](#tenets) - * [Features of Guard DSL](#features-of-guard-dsl) -* [Guard CLI](#guard-cli) - * [Installation](#installation) - * [How does Guard CLI work?](#how-does-guard-cli-work?) -* [Rule authoring references](#references) -* [Built-in functions & stateful rules](#functions) -* [AWS Rule Registry](#registry) -* [Use Guard as a Docker Image](#docker) -* [Use Guard as a Github Action](https://github.com/aws-cloudformation/cloudformation-guard/tree/main/action#readme) -* [Use Guard as a CI tool](#ci) -* [Use Guard as a pre-commit hook](#pre-commit-hook) -* [Contribute using the DevContainer in VSCode](#devcontainer) -* [License](#license) + +- [FAQs](#faqs) +- [Guard DSL](#guard-dsl) + - [Tenets](#tenets) + - [Features of Guard DSL](#features-of-guard-dsl) +- [Guard CLI](#guard-cli) + - [Installation](#installation) + - [How does Guard CLI work?](#how-does-guard-cli-work?) +- [Rule authoring references](#references) +- [Built-in functions & stateful rules](#functions) +- [AWS Rule Registry](#registry) +- [Use Guard as a Docker Image](#docker) +- [Use Guard as a Github Action](https://github.com/aws-cloudformation/cloudformation-guard/tree/main/action#readme) +- [Use Guard as a CI tool](#ci) +- [Use Guard as a pre-commit hook](#pre-commit-hook) +- [Contribute using the DevContainer in VSCode](#devcontainer) +- [License](#license) ## FAQs **1) What is Guard?** + > Guard is an open-source command line interface (CLI) that provides developers a general purpose domain-specific language (DSL) to express policy-as-code and then validate their JSON- and YAML-formatted data against that code. Guard’s DSL is a simple, powerful, and expressive declarative language to define policies. It is built on the foundation of clauses, which are assertions that evaluate to `true` or `false`. Examples clauses can include simple validations like all Amazon Simple Storage Service (S3) buckets must have versioning enabled, or combined to express complex validations like preventing public network reachability of Amazon Redshift clusters placed in a subnet. Guard has support for looping, queries with filtering, cross query joins, single shot variable assignments, conditional executions, and composable rules. These features help developers to express simple and advanced policies for various domains. **2) What Guard is not?** + > Guard **is not** a general-purpose programming language. It is a **purpose-built** DSL that is designed for policy definition and evaluation. Both non-technical people and developers can easily pick up Guard. Guard is human-readable and machine enforceable. **3) Where can I use Guard?** + > You can use Guard to define any type of policy for evaluation. You can apply Guard in the context of multiple domains: a) validating IaC/service compositions such as [CloudFormation Templates](https://aws.amazon.com/cloudformation/resources/templates/), Terraform JSON configuration files, and Kubernetes configurations, b) verifying conformity of CMDB resources such as AWS Config-based CIs, and c) assessing security postures across resources like AWS Security Hub. The policy language and expression is common to all of them, based on simple Guard clauses. **3) What is a clause in Guard?** -> Clause is an assertion that evaluates to true or false. Clauses can either use binary operations to compare two values (e.g `==, >` and `in`), or unary operations that takes only one value (e.g. `exists, empty,` and `is_list`). Here is a sample clause that compares `Type` to be a `AWS::S3::Bucket` : + +> Clause is an assertion that evaluates to true or false. Clauses can either use binary operations to compare two values (e.g `==, >` and `in`), or unary operations that takes only one value (e.g. `exists, empty,` and `is_list`). Here is a sample clause that compares `Type` to be a `AWS::S3::Bucket` : ``` Type == /AWS::S3::Bucket/ ``` **4) What are the supported** **types** **that can I use to define clauses?** + > Guard supports all primitives `string, integer (64), float (64), bool, char, regex` and specialized range expression like `r(10, 200)`, for specifying ranges of values. It supports general key value pair maps (a.k.a associative arrays/struct) like `{ "my-map": { "nested-maps": [ { "key": 10, "value": 20 } ] } },` and arrays of primitives or key-value pair maps like `[10, 20, 30] or [{ Key: "MyApp", Value: "PROD}, ..]`. **5) What binary and unary comparison operators can I use?** -> *Unary Operators:* `exists, empty, is_string, is_list, is_struct, is_bool, is_int, is_float, not(!)` -> *Binary Operators:* `==, !=, >, >=, <, <=, IN ` + +> _Unary Operators:_ `exists, empty, is_string, is_list, is_struct, is_bool, is_int, is_float, not(!)` > _Binary Operators:_ `==, !=, >, >=, <, <=, IN ` > > Most operators are self-explanatory. A few important points: > @@ -85,6 +92,7 @@ Properties.SslPolicy IN ["ELBSecurityPolicy-TLS-1-2-2017-01", "ELBSecurityPolicy ``` **6) How can I define advanced policy rules?** + > You can define advanced policy rules using Conjunctive normal form. For example, here is a clause that asserts that all S3 buckets have a) names that start with a common prefix, b) encryption turned on, and c) only KMS-based algorithm is used (to know more about the query part read [Guard: Query and Filtering](docs/QUERY_AND_FILTERING.md)) for IaC template. ``` @@ -109,25 +117,30 @@ rule s3_bucket_name_encryption_check when %s3_buckets !empty { ``` **7) Can I easily test policy rules?** + > Yes. Guard supports a built-in unit testing framework to test policy rules and clauses. This gives customers confidence that their guard policy rules work as intended. You can learn more about this unit testing framework in this doc [Guard: Unit Testing](docs/UNIT_TESTING.md) **8)** **Does Guard support rule categories?** + > Yes. Guard supports running several rule-sets together for validating policies. You can create multiple rule files, each with its own intended purpose. For example, you can create one rules file for S3, second one for Dynamo DB, third one for access management, and so on. Alternatively, you can create a rules file for all your security related rules, second one for cost compliance, and so on. You can run Guard against all these rule files at once for evaluation. Refer example rules file [Guard: Clauses](docs/CLAUSES.md), [Guard: Complex Composition](docs/COMPLEX_COMPOSITION.md). **9) Where can I evaluate Guard policies?** + > Guard supports the entire spectrum of end-to-end evaluation of policy checks. The tool supports bringing in shift-left practices as close as running it directly at development time, integrated into code repositories via hooks like GitHub Actions for pull requests, and into CI/CD pipelines such as AWS CodePipeline pipelines and Jenkins (just exec process). **10) What are you not telling me? This sounds too good to be true.** + > Guard is a DSL and an accompanying CLI tool that allows easy-to-use definitions for declaring and enforcing policies. Today the tool supports local file-based execution of a category of policies. Guard doesn’t support the following things today, along with workarounds for some: > > 1. Sourcing of rules from external locations such as GitHub Release and S3 bucket. If you want this feature natively in Guard, please raise an issue or +1 an existing issue. > 2. Ability to import Guard policy file by reference (local file or GitHub, S3, etc.). It currently only supports a directory on disk of policy files, that it would execute. > 3. Parameter/Vault resolution for IaC tools such as CloudFormation or Terraform. Before you ask, the answer is NO. We will not add native support in Guard as the engine is general-purpose. If you need CloudFormation resolution support, raise an issue and we might have a solution for you. We do not support HCL natively. We do, however, support Terraform Plan in JSON to run policies against for deployment safety. If you need HCL support, raise an issue as well. > 4. Ability to reference variables like `%s3_buckets`, inside error messages. Both JSON/Console output for evaluation results contain some of this information for inference. We also do not support using variable references to create dynamic regex expressions. However, we support variable references inside queries for cross join support, like `Resources.%references.Properties.Tags`. -> 5. Support for specifying variable names when accessing map or list elements to capture these values. For example, consider this check `Resources[resource_name].Properties.Tags not empty`, here `resource_name` captures the key or index value. The information is tracked as a part of the evaluation context today and present in both console/JSON outputs. This support will be extended to regex expression variable captures as well. +> 5. Support for specifying variable names when accessing map or list elements to capture these values. For example, consider this check `Resources[resource_name].Properties.Tags not empty`, here `resource_name` captures the key or index value. The information is tracked as a part of the evaluation context today and present in both console/JSON outputs. This support will be extended to regex expression variable captures as well. > 6. There are [known issues](docs/KNOWN_ISSUES.md) with potential workarounds that we are tracking towards resolution **11) What are we really thankful about?** + > Where do we start? Hmm.... we want to thank Rust [language’s forums](https://users.rust-lang.org/), [build management, and amazing ecosystem](https://crates.io/) without which none of this would have been possible. We are not the greatest Rust practitioners, so if we did something that is not idiomatic Rust, please raise a PR. > > We want to make a special mention to [nom](https://github.com/Geal/nom) combinator parser framework to write our language parser in. This was an excellent decision that improved readability, testability, and composition. We highly recommend it. There are some rough edges, but it’s just a wonderful, awesome library. Thank you. Apart from that, we are consumers of many crates including [hyper](https://crates.io/crates/hyper) for HTTP handling, [simple logger](https://crates.io/crates/simple_logger), and many more. We also want to thank the open-source community for sharing their feedback with us through GitHub issues/PRs. @@ -142,25 +155,25 @@ rule s3_bucket_name_encryption_check when %s3_buckets !empty { These tenets help guide the development of the Guard DSL: -* **Simple**: The language must be simple for customers to author policy rules, simple to integrate with an integrated development environment (IDE), readable for human comprehension, and machine enforceable. +- **Simple**: The language must be simple for customers to author policy rules, simple to integrate with an integrated development environment (IDE), readable for human comprehension, and machine enforceable. -* **Unambiguous**: The language must not allow for ambiguous interpretations that make it hard for customers to comprehend the policy evaluation. The tool is targeted for security and compliance related attestations that need the auditor to consistently and unambiguously understand rules and their evaluations. +- **Unambiguous**: The language must not allow for ambiguous interpretations that make it hard for customers to comprehend the policy evaluation. The tool is targeted for security and compliance related attestations that need the auditor to consistently and unambiguously understand rules and their evaluations. -* **Deterministic**: The language design must allow language implementations to have deterministic, consistent, and isolated evaluations. Results for repeated evaluations for the same context and rules must evaluate to the same result every time. Time to evaluate results inside near-identical environments must be within acceptable tolerance limits. +- **Deterministic**: The language design must allow language implementations to have deterministic, consistent, and isolated evaluations. Results for repeated evaluations for the same context and rules must evaluate to the same result every time. Time to evaluate results inside near-identical environments must be within acceptable tolerance limits. -* **Composable**: The language must support composition to help build higher order functionality such as checks for PCI compliance, by easily combining building blocks together. Composition should not increase the complexity for interpreting outcomes, syntax, or navigation. +- **Composable**: The language must support composition to help build higher order functionality such as checks for PCI compliance, by easily combining building blocks together. Composition should not increase the complexity for interpreting outcomes, syntax, or navigation. ### Features of Guard DSL -* **Clauses:** Provides the foundational underpinning for Guard. They are assertions that evaluate to true or false. You can combine clauses using [Conjunctive Normal Form](https://en.wikipedia.org/wiki/Conjunctive_normal_form). You can use them for direct assertions, as part of filters to select values, or for conditional evaluations. To learn more read [Guard: Clauses](docs/CLAUSES.md) +- **Clauses:** Provides the foundational underpinning for Guard. They are assertions that evaluate to true or false. You can combine clauses using [Conjunctive Normal Form](https://en.wikipedia.org/wiki/Conjunctive_normal_form). You can use them for direct assertions, as part of filters to select values, or for conditional evaluations. To learn more read [Guard: Clauses](docs/CLAUSES.md) -* **Context-Aware Evaluations, `this` binding and Loops:** Automatic binding for context values when traversing hierarchical data with support for implicit looping over collections with an easy-to-use syntax. Collections can arise from accessing an array of elements, values for a map along with a filter, or from a query. To learn more read [Guard: Context-Aware Evaluations, this and Loops](docs/CONTEXTAWARE_EVALUATIONS_AND_LOOPS.md) +- **Context-Aware Evaluations, `this` binding and Loops:** Automatic binding for context values when traversing hierarchical data with support for implicit looping over collections with an easy-to-use syntax. Collections can arise from accessing an array of elements, values for a map along with a filter, or from a query. To learn more read [Guard: Context-Aware Evaluations, this and Loops](docs/CONTEXTAWARE_EVALUATIONS_AND_LOOPS.md) -* **Query & Filtering:** Queries support simple decimal dotted format syntax to access properties in the hierarchical data. Arrays/Collections are accessed using `[]` . Map or Struct’s values can use `*` for accessing values for all keys. All collections can be further narrowed to target specific instances inside the collection using filtering. To learn more read [Guard: Query and Filtering](docs/QUERY_AND_FILTERING.md) +- **Query & Filtering:** Queries support simple decimal dotted format syntax to access properties in the hierarchical data. Arrays/Collections are accessed using `[]` . Map or Struct’s values can use `*` for accessing values for all keys. All collections can be further narrowed to target specific instances inside the collection using filtering. To learn more read [Guard: Query and Filtering](docs/QUERY_AND_FILTERING.md) -* **Variables, Projections, and Query Interpolation:** Guard supports single shot assignment to variables using a **`let`** keyword for assignment. All variable assignments resulting from a query is a list (result set). One can also assign static literals to variables. Variables are assessed using a prefix **`%`** and can be used inside the Query for interpolation. To learn more read [Guard: Query, Projection and Interpolation](docs/QUERY_PROJECTION_AND_INTERPOLATION.md) +- **Variables, Projections, and Query Interpolation:** Guard supports single shot assignment to variables using a **`let`** keyword for assignment. All variable assignments resulting from a query is a list (result set). One can also assign static literals to variables. Variables are assessed using a prefix **`%`** and can be used inside the Query for interpolation. To learn more read [Guard: Query, Projection and Interpolation](docs/QUERY_PROJECTION_AND_INTERPOLATION.md) -* **Complex Composition**: As stated earlier, clauses can be expressed in Conjunctive Normal Form. Clauses on separates lines are ANDs. Disjunctions are expressed using the `or|OR` keyword. You can group clauses in a named rule. You can then use named rules in other rules to create more advanced compositions. Furthermore, you can have multiple files containing named rules that together form a category of checks for a specific compliance like “ensure encryption at rest”. To learn more read [Guard: Complex Composition](docs/COMPLEX_COMPOSITION.md) +- **Complex Composition**: As stated earlier, clauses can be expressed in Conjunctive Normal Form. Clauses on separates lines are ANDs. Disjunctions are expressed using the `or|OR` keyword. You can group clauses in a named rule. You can then use named rules in other rules to create more advanced compositions. Furthermore, you can have multiple files containing named rules that together form a category of checks for a specific compliance like “ensure encryption at rest”. To learn more read [Guard: Complex Composition](docs/COMPLEX_COMPOSITION.md) ## Guard CLI @@ -173,10 +186,12 @@ These tenets help guide the development of the Guard DSL: By default this is built for macOS-12 (Monterey). See [OS Matrix](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#github-hosted-runners) 1. Open terminal of your choice. Default `Cmd+Space`, type `terminal` -2. Cut-n-paste the commands below (default latest, add `-s -- -v ` for other versions ie. `-s -- -v 3.1.1`) +2. Cut-n-paste the commands below (default latest, add `-s -- -v ` for other versions ie. `-s -- -v 3.1.2`) + ```bash $ curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.sh | sh ``` + Remember to add `~/.guard/bin/` to your `$PATH`. Alternatively, you can install the latest version with [Homebrew](https://brew.sh/). @@ -190,7 +205,8 @@ You would not need to modify `$PATH` this way. ##### Ubuntu 1. Open any terminal of your choice -2. Cut-n-paste the commands below (default latest, add `-s -- -v ` for other versions ie. `-s -- -v 3.1.1`) +2. Cut-n-paste the commands below (default latest, add `-s -- -v ` for other versions ie. `-s -- -v 3.1.2`) + ```bash $ curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.sh | sh ``` @@ -201,6 +217,7 @@ Remember to add `~/.guard/bin/` to your `$PATH`. 1. Open PowerShell as Administrator 2. Cut-n-paste the command below + ```shell $GuardWindowsInstallScript = Invoke-WebRequest https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.ps1; Invoke-Expression $($GuardWindowsInstallScript.Content) ``` @@ -237,6 +254,7 @@ If building on `Ubuntu`, it is recommended to run `sudo apt-get update; sudo apt 10. Run it and accept the defaults. #### Cargo-based Installation + Now that you have [rust and cargo installed](https://doc.rust-lang.org/cargo/getting-started/installation.html), installation of cfn-guard is easy: ```bash @@ -247,7 +265,7 @@ Check `help` to see if it is working. ```bash $ cfn-guard help -cfn-guard 3.1.1 +cfn-guard 3.1.2 Guard is a general-purpose tool that provides a simple declarative syntax to define policy-as-code as rules to validate against any structured hierarchical data (like JSON/YAML). @@ -370,7 +388,7 @@ metadata: spec: containers: - name: app - image: 'images.my-company.example/app:v4' + image: "images.my-company.example/app:v4" resources: requests: memory: 64Mi @@ -378,7 +396,7 @@ spec: limits: memory: 128Mi - name: log-aggregator - image: 'images.my-company.example/log-aggregator:v6' + image: "images.my-company.example/log-aggregator:v6" resources: requests: memory: 64Mi @@ -453,6 +471,7 @@ cfn-guard validate -r security_groups.guard -i network.yaml -d security_groups_f ``` In this command: + - `-r` specifies the rule file - `-i` specifies the input parameter file - `-d` specifies the data file (template) to be validated @@ -524,7 +543,7 @@ rule check_rest_api_has_vpc_access when check_rest_api_is_private { check_rest_api_is_private: SKIP check_rest_api_has_vpc_access: SKIP - input: - Resources: {} + Resources: {} expectations: rules: check_rest_api_is_private: SKIP @@ -569,10 +588,10 @@ rule check_rest_api_has_vpc_access when check_rest_api_is_private { Policy: Statement: - Action: Allow - Resource: '*' + Resource: "*" Condition: StringLike: - 'aws:sourceVPC': vpc-12345678 + "aws:sourceVPC": vpc-12345678 expectations: rules: check_rest_api_is_private: PASS @@ -596,6 +615,7 @@ As a starting point for writing Guard rules for yourself or your organisation we ### Quick links: [Writing AWS CloudFormation Guard rules](https://docs.aws.amazon.com/cfn-guard/latest/ug/writing-rules.html) + 1. [Clauses](https://docs.aws.amazon.com/cfn-guard/latest/ug/writing-rules.html#clauses) 2. [Using queries in clauses](https://docs.aws.amazon.com/cfn-guard/latest/ug/writing-rules.html#clauses-queries) 3. [Using operators in clauses](https://docs.aws.amazon.com/cfn-guard/latest/ug/writing-rules.html#clauses-operators) @@ -625,6 +645,7 @@ Resources: - a - b ``` + ### Sample rule We can write a rule to check this condition as follows: @@ -651,7 +672,6 @@ As a reference for Guard rules and rule-sets that contain (on a best-effort basi to the industry best practices around usages across AWS resources, we have recently launched [AWS Guard Rules Registry](https://github.com/aws-cloudformation/aws-guard-rules-registry). - ## Use Guard as a Docker Image Guard is also published as an ECR image in [ECR public gallery](https://gallery.ecr.aws/aws-cloudformation/cloudformation-guard) and can be used as an image in a docker container. @@ -664,22 +684,26 @@ Guard is also published as an ECR image in [ECR public gallery](https://gallery. ### Usage Guide To use the binary, we should pull the latest docker image, we may do so using the following command: + ```bash docker pull public.ecr.aws/aws-cloudformation/cloudformation-guard:latest ``` + Now go ahead and run the docker image, using the files from directory we have our templates and rules file in, using: + ```bash docker run \ --mount src=/path/to/guard-files,target=/container/guard-files,type=bind \ -it public.ecr.aws/aws-cloudformation/cloudformation-guard:latest \ ./cfn-guard validate -d /container/guard-files/template.yml -r /container/guard-files/rule.guard ``` + We should see the evaluation result emitted out on the console. ### Tagging convention -* We use the tag `latest` for the most recent docker image that gets published in sync with `main` branch of the `cloudformation-guard` GitHub repository. -* We use the convention `.` for tags of historical docker images +- We use the tag `latest` for the most recent docker image that gets published in sync with `main` branch of the `cloudformation-guard` GitHub repository. +- We use the convention `.` for tags of historical docker images ## Use Guard as a CI tool @@ -717,28 +741,28 @@ Guard is available as a pre-commit hook and offers both the `test` and `validate # the pre-commit-update hook. If you don't require the autoupdate # feature, you may skip the pre-commit-update hook. repos: -# pre-commit-update hook -- repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update + # pre-commit-update hook + - repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update rev: v0.5.0 hooks: - - id: pre-commit-update + - id: pre-commit-update args: [--tag-prefix, cloudformation-guard, pre-commit-v] -# cfn-guard pre-commit hook -- repo: https://github.com/aws-cloudformation/cloudformation-guard + # cfn-guard pre-commit hook + - repo: https://github.com/aws-cloudformation/cloudformation-guard rev: pre-commit-v0.0.2 hooks: - # Validate - - id: cfn-guard - args: - - --operation=validate # Specify the validate operation - - --rules=path/to/rules/ # Rules directory - files: ^path/to/data/.* # Directory to watch for changes and validate against - # Test - - id: cfn-guard - args: - - --operation=test # Specify the test operation - - --dir=path/to/resources/ # Directory that contains rules & their tests - files: ^path/to/resources.* # Same directory supplied to the --dir arg so that rule and test file changes trigger a run + # Validate + - id: cfn-guard + args: + - --operation=validate # Specify the validate operation + - --rules=path/to/rules/ # Rules directory + files: ^path/to/data/.* # Directory to watch for changes and validate against + # Test + - id: cfn-guard + args: + - --operation=test # Specify the test operation + - --dir=path/to/resources/ # Directory that contains rules & their tests + files: ^path/to/resources.* # Same directory supplied to the --dir arg so that rule and test file changes trigger a run ``` **NOTE**: The args for the pre-commit hook are not identical to the flags you would pass directly to Guard. In the case of this hook, you cannot pass a `data` flag to `validate` as it depends on the `filenames` from the hook and you can only use the `dir` flag for the `test` hook. @@ -746,6 +770,7 @@ repos: ## Contribute using the DevContainer in VSCode ### Setup + **NOTE**: The devcontainer supports zsh and bash with theming and aliases. You can choose which shell you like in the terminal options in your VSCode session. 1. Follow the prerequisite instructions [here](https://code.visualstudio.com/learn/develop-cloud/containers). @@ -755,13 +780,12 @@ repos: ### Aliases -* `gval`: `cargo run --bin cfn-guard validate` -* `gtest`: `cargo run --bin cfn-guard test` -* `gparse`: `cargo run --bin cfn-guard parse-tree` -* `cb`: `cargo build` -* `ct`: `cargo nextest run` -* `cn`: `cargo +nightly` - +- `gval`: `cargo run --bin cfn-guard validate` +- `gtest`: `cargo run --bin cfn-guard test` +- `gparse`: `cargo run --bin cfn-guard parse-tree` +- `cb`: `cargo build` +- `ct`: `cargo nextest run` +- `cn`: `cargo +nightly` ## License diff --git a/guard-examples/library/Cargo.toml b/guard-examples/library/Cargo.toml index 41d7ace83..46d9f33a1 100644 --- a/guard-examples/library/Cargo.toml +++ b/guard-examples/library/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cfn-guard = { version = "3.1.1", path = "../../guard" } +cfn-guard = { version = "3.1.2", path = "../../guard" } anyhow = "1.0.81" diff --git a/guard-ffi/Cargo.toml b/guard-ffi/Cargo.toml index fb5f27d8d..22e720b62 100644 --- a/guard-ffi/Cargo.toml +++ b/guard-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cfn-guard-ffi" -version = "3.1.1" +version = "3.1.2" edition = "2018" authors = ["Diwakar Chakravarthy", "John Tompkins", "Omkar Hegde", "Priya Padmanaban", "aws-cloudformation-developers ", "Tyler Southwick"] description = "AWS CloudFormation Guard is an open-source general-purpose policy-as-code evaluation tool. It provides developers with a simple-to-use, yet powerful and expressive domain-specific language (DSL) to define policies and enables developers to validate JSON- or YAML- formatted structured data with those policies." @@ -14,5 +14,5 @@ keywords = ["policy-as-code", "guard", "cfn-guard", "security", "compliance"] crate-type = ["rlib", "dylib"] [dependencies] -cfn-guard = { version = "3.1.1", path = "../guard" } +cfn-guard = { version = "3.1.2", path = "../guard" } ffi-support = "0.4.4" diff --git a/guard-lambda/Cargo.toml b/guard-lambda/Cargo.toml index 517924922..f45327754 100644 --- a/guard-lambda/Cargo.toml +++ b/guard-lambda/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cfn-guard-lambda" -version = "3.1.1" +version = "3.1.2" authors = ["Diwakar Chakravarthy", "John Tompkins", "Omkar Hegde", "Priya Padmanaban", "Bryan Ayala", "Kexiang Wang", "Akshay Rane", "Josh Fried", "aws-cloudformation-developers "] description = "Lambda version of cfn-guard. Checks JSON- or YAML- formatted structured data for policy compliance using a simple, policy-as-code, declarative syntax" @@ -17,4 +17,4 @@ serde_derive = "1.0.92" simple_logger = "4.0.0" log = "0.4.6" tokio = "1.24.2" -cfn-guard = { version = "3.1.1", path = "../guard" } +cfn-guard = { version = "3.1.2", path = "../guard" } diff --git a/guard/Cargo.toml b/guard/Cargo.toml index b1ea89845..8794a40b7 100644 --- a/guard/Cargo.toml +++ b/guard/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cfn-guard" -version = "3.1.1" +version = "3.1.2" edition = "2018" authors = ["Diwakar Chakravarthy", "John Tompkins", "Omkar Hegde", "Priya Padmanaban", "Bryan Ayala", "Kexiang Wang", "Akshay Rane", "Tyler Southwick", "Josh Fried", "aws-cloudformation-developers "] @@ -33,7 +33,7 @@ itertools = "0.4.7" string-builder = "0.2.0" enumflags2 = "0.7.7" enumflags2_derive = "0.7.7" -Inflector = "0.11.4" +cruet = "0.14.0" urlencoding = "2.1.0" grep-searcher = "0.1.8" grep-matcher = "0.1.5" diff --git a/guard/resources/validate/output-dir/structured.sarif b/guard/resources/validate/output-dir/structured.sarif index 115a9660e..3fa93af6b 100644 --- a/guard/resources/validate/output-dir/structured.sarif +++ b/guard/resources/validate/output-dir/structured.sarif @@ -6,8 +6,8 @@ "tool": { "driver": { "name": "cfn-guard", - "semanticVersion": "3.1.1", - "fullName": "cfn-guard 3.1.1", + "semanticVersion": "3.1.2", + "fullName": "cfn-guard 3.1.2", "organization": "Amazon Web Services", "downloadUri": "https://github.com/aws-cloudformation/cloudformation-guard", "informationUri": "https://github.com/aws-cloudformation/cloudformation-guard", diff --git a/guard/src/rules/eval_context.rs b/guard/src/rules/eval_context.rs index 91912c8ac..e9f0d38dc 100644 --- a/guard/src/rules/eval_context.rs +++ b/guard/src/rules/eval_context.rs @@ -19,7 +19,7 @@ use crate::rules::{ QueryResult, RecordTracer, RecordType, Status, TypeBlockCheck, UnResolved, UnaryValueCheck, ValueCheck, }; -use inflector::cases::*; +use cruet::case::{camel, class, kebab, pascal, snake, title, train}; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::collections::{BTreeSet, HashMap}; @@ -316,13 +316,13 @@ type Converters = &'static [(fn(&str) -> bool, fn(&str) -> String)]; lazy_static! { #[allow(clippy::type_complexity)] static ref CONVERTERS: Converters = &[ - (camelcase::is_camel_case, camelcase::to_camel_case), - (classcase::is_class_case, classcase::to_class_case), - (kebabcase::is_kebab_case, kebabcase::to_kebab_case), - (pascalcase::is_pascal_case, pascalcase::to_pascal_case), - (snakecase::is_snake_case, snakecase::to_snake_case), - (titlecase::is_title_case, titlecase::to_title_case), - (traincase::is_train_case, traincase::to_train_case), + (camel::is_camel_case, camel::to_camel_case), + (class::is_class_case, class::to_class_case), + (kebab::is_kebab_case, kebab::to_kebab_case), + (pascal::is_pascal_case, pascal::to_pascal_case), + (snake::is_snake_case, snake::to_snake_case), + (title::is_title_case, title::to_title_case), + (train::is_train_case, train::to_train_case), ]; } diff --git a/guard/tests/validate.rs b/guard/tests/validate.rs index 1b4683bce..a951aa951 100644 --- a/guard/tests/validate.rs +++ b/guard/tests/validate.rs @@ -599,7 +599,7 @@ mod validate_tests { #[case("yaml")] #[case("json")] #[case("junit")] - #[case("sarif")] + #[case::sarif("sarif")] fn test_structured_output(#[case] output: &str) { let mut reader = Reader::default(); let mut writer = Writer::new(WBVec(vec![])).expect("Failed to create writer.");