Automate Cedar policy validation with AWS developer tools

Cedar is an open-source language that you can use to write policies and make authorization decisions based on those policies. AWS security services including AWS Verified Access and Amazon Verified Permissions use Cedar to define policies. Cedar supports schema declaration for the structure of entity types in those policies and policy validation with that schema.

   <p>In this post, we show you how to use <a href="https://aws.amazon.com/products/developer-tools/" target="_blank" rel="noopener">developer tools on AWS</a> to implement a build pipeline that validates the Cedar policy files against a schema and runs a suite of tests to isolate the Cedar policy logic. As part of the walkthrough, you will introduce a subtle policy error that impacts permissions to observe how the pipeline tests catch the error. Detecting errors earlier in the development lifecycle is often referred to as <em>shifting left</em>. When you <a href="https://aws.amazon.com/blogs/security/implement-an-early-feedback-loop-with-aws-developer-tools-to-shift-security-left/" target="_blank" rel="noopener">shift security left</a>, you can help prevent undetected security issues during the application build phase.</p> 
   <p>This post extends a hypothetical photo sharing application from the <a href="https://catalog.workshops.aws/cedar-policy-language-in-action" target="_blank" rel="noopener">Cedar policy language in action</a> workshop. By using that app, users organize their photos into albums and share them with groups of users. Figure 1 shows the entities from the photo application.</p> 
   <div id="attachment_32943" class="wp-caption aligncenter"> 
    <img aria-describedby="caption-attachment-32943" src="https://www.infracom.com.sg/wp-content/uploads/2024/01/fig1.png" alt="Figure 1: Photo application entities" width="936" height="438" class="size-full wp-image-32943"> 
    <p id="caption-attachment-32943" class="wp-caption-text">Figure 1: Photo application entities</p> 
   <p>For the purpose of this post, the important requirements are that user JohnDoe has view access to the album JaneVacation, which contains two photos that user JaneDoe owns:</p> 
    <li>Photo <span>sunset.jpg</span> has a <span>contest</span> label (indicating that the role PhotoJudge has view access)</li> 
    <li>Photo <span>nightclub.jpg</span> has a <span>private</span> label (indicating that only the owner has access)</li> 
   <p>Cedar policies separate application permissions from the code that retrieves and displays photos. The following Cedar policy explicitly permits the principal of user JohnDoe to take the action <span>viewPhoto</span> on resources in the album JaneVacation.</p> 
   <div class="hide-language"> 
    <pre><code class="lang-text">permit (

principal == PhotoApp::User::”JohnDoe”,
action == PhotoApp::Action::”viewPhoto”,
resource in PhotoApp::Album::”JaneVacation”

   The following Cedar policy forbids non-owners from accessing photos labeled as <span>private</span>, even if other policies permit access. In our example, this policy prevents John Doe from viewing the <span>nightclub.jpg</span> photo (denoted by an X in Figure 1).</p> 
   <div class="hide-language"> 
    <pre><code class="lang-text">forbid (

resource in PhotoApp::Application::”PhotoApp”
when { resource.labels.contains(“private”) }
unless { resource.owner == principal };

A Cedar authorization request asks the question: Can this principal take this action on this resource in this context? The request also includes attribute and parent information for the entities. If an authorization request is made with the following test data, against the Cedar policies and entity data described earlier, the authorization result should be DENY.

  "principal": "PhotoApp::User::"JohnDoe"",
  "action": "PhotoApp::Action::"viewPhoto"",
  "resource": "PhotoApp::Photo::"nightclub.jpg"",
  "context": {}

The project test suite uses this and other test data to validate the expected behaviors when policies are modified. An error intentionally introduced into the preceding forbid policy lets the first policy satisfy the request and ALLOW access. That unexpected test result compared to the requirements fails the build.

Developer tools on AWS

With AWS developer tools, you can host code and build, test, and deploy applications and infrastructure. AWS CodeCommit hosts the Cedar policies and a test suite, AWS CodeBuild runs the tests, and AWS CodePipeline automatically runs the CodeBuild job when a CodeCommit repository state change event occurs.

In the following steps, you will create a pipeline, commit policies and tests, run a passing build, and observe how a policy error during validation fails a test case.


To follow along with this walkthrough, make sure to complete the following prerequisites:

   <h2>Set up the local environment</h2> 
   <p>The first step is to set up your local environment.</p> 
   <p><strong>To set up the local environment</strong></p> 
    <li>Using Git, clone the <a href="https://github.com/aws-samples/cedar-policy-validation-pipeline" target="_blank" rel="noopener">GitHub repository</a> for this post:</li> 
    <p> <code>git clone git@github.com:aws-samples/cedar-policy-validation-pipeline.git</code></p> 
    <li>Before you commit this source code to a CodeCommit repository, run the test suite locally; this can help you shorten the feedback loop. To run the test suite locally, choose one of the following options:</li> 
    <p><strong>Option 1</strong>: <a href="https://www.rust-lang.org/tools/install" target="_blank" rel="noopener">Install Rust</a> and compile the Cedar CLI binary</p> 
    <ol type="a"> 
     <li>Install Rust by using the <span>rustup</span> tool.</li> 
     <p><code>curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y</code></p> 
     <li>Compile the Cedar CLI (version 2.4.2) binary by using <span>cargo</span>.</li> 
     <p><code>cargo install cedar-policy-cli@2.4.2</code></p> 
     <li>Run the <span>cedar_testrunner.sh</span> script, which tests authorize requests by using the Cedar CLI.</li> 
     <p><code>cd policystore/tests && ./cedar_testrunner.sh</code> </p> 
    <p><strong>Option 2</strong>: <a href="https://docs.aws.amazon.com/codebuild/latest/userguide/use-codebuild-agent.html#use-codebuild-agent.run-agent" target="_blank" rel="noopener">Run the CodeBuild agent</a></p> 
    <ol type="a"> 
     <li>Locally evaluate the <span>buildspec.yml</span> inside a CodeBuild container image by using the <span>codebuild_build.sh</span> script from <a href="https://github.com/aws/aws-codebuild-docker-images" target="_blank" rel="noopener">aws-codebuild-docker-images</a> with the following parameters:</li> 
     <p> <code>./codebuild_build.sh -i public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:5.0 -a .codebuild</code> </p> 
   <h2>Project structure</h2> 
   <p>The <span>policystore</span> directory contains one Cedar policy for each <span>.cedar</span> file. The Cedar schema is defined in the <span>cedarschema.json</span> file. A <span>tests</span> subdirectory contains a <span>cedarentities.json</span> file that represents the application data; its subdirectories (for example, album <span>JaneVacation</span>) represent the test suites. The test suite directories contain individual tests inside their <span>ALLOW</span> and <span>DENY</span> subdirectories, each with one or more JSON files that contain the authorization request that Cedar will evaluate against the policy set. A README file in the <span>tests</span> directory provides a summary of the test cases in the suite.</p> 
   <p>The <span>cedar_testrunner.sh</span> script runs the Cedar CLI to perform a <span>validate</span> command for each <span>.cedar</span> file against the Cedar schema, outputting either <span>PASS</span> or <span>ERROR</span>. The script also performs an <span>authorize</span> command on each test file, outputting either <span>PASS</span> or <span>FAIL</span> depending on whether the results match the expected authorization decision.</p> 
   <h2>Set up the CodePipeline</h2> 
   <p>In this step, you use <a href="https://aws.amazon.com/cloudformation/" target="_blank" rel="noopener">AWS CloudFormation</a> to provision the services used in the pipeline.</p> 
   <p><strong>To set up the pipeline</strong></p> 
    <li>Navigate to the directory of the cloned repository. <p><code>cd cedar-policy-validation-pipeline</code></p></li> 
    <li>Create a new CloudFormation stack from the template. 
     <div class="hide-language"> 
      <pre><code class="lang-text">aws cloudformation deploy 

–template-file template.yml
–stack-name cedar-policy-validation

    <li>Wait for the message <span>Successfully created/updated stack.</span></li> 

   <h2>Invoke CodePipeline</h2> 
   The next step is to commit the source code to a CodeCommit repository, and then configure and invoke CodePipeline.</p> 
   <p><strong>To invoke CodePipeline</strong></p> 
    <li>Add an additional <a href="https://git-scm.com/docs/git-remote" target="_blank" rel="noopener">Git remote</a> named <span>codecommit</span> to the repository that you previously cloned. The following command points the Git remote to the CodeCommit repository that CloudFormation created. The <span>CedarPolicyRepoCloneUrl</span> stack output is the HTTPS clone URL. Replace it with <span>CedarPolicyRepoCloneGRCUrl</span> to use the HTTPS (GRC) clone URL when you <a href="https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-git-remote-codecommit.html" target="_blank" rel="noopener">connect to CodeCommit with git-remote-codecommit</a>. 
     <div class="hide-language"> 
      <pre><code>git remote add codecommit $(aws cloudformation describe-stacks --stack-name cedar-policy-validation --query 'Stacks[0].Outputs[?OutputKey==`CedarPolicyRepoCloneUrl`].OutputValue' --output text)</code></pre> 
     </div> </li> 
    <li>Push the code to the CodeCommit repository. This starts a pipeline run. <p><code>git push codecommit main</code></p></li> 
    <li>Check the progress of the pipeline run. 
     <div class="hide-language"> 
      <pre><code class="lang-text">aws codepipeline get-pipeline-execution 

–pipeline-name cedar-policy-validation
–pipeline-execution-id $(aws codepipeline list-pipeline-executions –pipeline-name cedar-policy-validation –query ‘pipelineExecutionSummaries[0].pipelineExecutionId’ –output text)
–query ‘pipelineExecution.status’ –output text

The build installs Rust in CodePipeline in your account and compiles the Cedar CLI. After approximately four minutes, the pipeline run status shows Succeeded.

Refactor some policies

This photo sharing application sample includes overlapping policies to simulate a refactoring workflow, where after changes are made, the test suite continues to pass. The DoePhotos.cedar and JaneVacation.cedar static policies are replaced by the logically equivalent viewPhoto.template.cedar policy template and two template-linked policies defined in cedartemplatelinks.json. After you delete the extra policies, the passing tests illustrate a successful refactor with the same expected application permissions.

To refactor policies

  1. Delete DoePhotos.cedar and JaneVacation.cedar.
  2. Commit the change to the repository.
    git add .
    git commit -m "Refactor some policies"
    git push codecommit main

  3. Check the pipeline progress. After about 20 seconds, the pipeline status shows Succeeded.

The second pipeline build runs quicker because the build specification is configured to cache a version of the Cedar CLI. Note that caching isn’t implemented in the local testing described in Option 2 of the local environment setup.

Break the build

After you confirm that you have a working pipeline that validates the Cedar policies, see what happens when you commit an invalid Cedar policy.

To break the build

  1. Using a text editor, open the file policystore/Photo-labels-private.cedar.
  2. In the when clause, change resource.labels to resource.label (removing the “s”). This policy syntax is valid, but no longer validates against the Cedar schema.
  3. Commit the change to the repository.
    git add .
    git commit -m "Break the build"
    git push codecommit main

  4. Sign in to the AWS Management Console and open the CodePipeline console.
  5. Wait for the Most recent execution field to show Failed.
  6. Select the pipeline and choose View in CodeBuild.
  7. Choose the Reports tab, and then choose the most recent report.
  8. Review the report summary, which shows details such as the total number of Passed and Failed/Error test case totals, and the pass rate, as shown in Figure 2.
  9. Figure 2: CodeBuild test report summary

    Figure 2: CodeBuild test report summary

  10. To get the error details, in the Details section, select the Test case called validate Photo-labels-private.cedar that has a Status of Error.
  11. Figure 3: CodeBuild test report test cases

    Figure 3: CodeBuild test report test cases

    That single policy change resulted in two test cases that didn’t pass. The detailed error message shown in Figure 4 is the output from the Cedar CLI. When the policy was validated against the schema, Cedar found the invalid attribute label on the entity type PhotoApp::Photo. The Failed message of unexpected ALLOW occurred because the label attribute typo prevented the forbid policy from matching and producing a DENY result. Each of these tests helps you avoid deploying invalid policies.

        <div id="attachment_32946" class="wp-caption aligncenter"> 
         <img aria-describedby="caption-attachment-32946" loading="lazy" src="https://www.infracom.com.sg/wp-content/uploads/2024/01/fig4.png" alt="Figure 4: CodeBuild test case error message" width="1380" height="467" class="size-full wp-image-32946"> 
         <p id="caption-attachment-32946" class="wp-caption-text">Figure 4: CodeBuild test case error message</p> 
       <h2>Clean up</h2> 
       <p>To avoid ongoing costs and to clean up the resources that you deployed in your AWS account, complete the following steps:</p> 
       <p><strong>To clean up the resources</strong></p> 
        <li>Open the <strong>Amazon S3 </strong>console, select the bucket that begins with the phrase <strong>cedar-policy-validation-codepipelinebucket</strong>, and <strong>Empty</strong> the bucket.</li> 
        <li>Open the <strong>CloudFormation </strong>console, select the <strong>cedar-policy-validation</strong> stack, and then choose <strong>Delete</strong>.</li> 
        <li>Open the <strong>CodeBuild </strong>console, choose <strong>Build History</strong>, filter by <span>cedar-policy-validation</span>, select all results, and then choose <strong>Delete builds</strong>.</li> 
       <p>In this post, you learned how to use AWS developer tools to implement a pipeline that automatically validates and tests when Cedar policies are updated and committed to a source code repository. Using this approach, you can detect invalid policies and potential application permission errors earlier in the development lifecycle and before deployment.</p> 
       <p>To learn more about the Cedar policy language, see the <a href="https://docs.cedarpolicy.com/" target="_blank" rel="noopener">Cedar Policy Language Reference Guide</a> or browse the source code at the <a href="https://github.com/cedar-policy" target="_blank" rel="noopener">cedar-policy</a> organization on GitHub. For real-time validation of Cedar policies and schemas, install the <a href="https://marketplace.visualstudio.com/items?itemName=cedar-policy.vscode-cedar" target="_blank" rel="noopener">Cedar policy language for Visual Studio Code</a> extension.</p> 
       <p>If you have feedback about this post, submit comments in the<strong> Comments</strong> section below. If you have questions about this post, start a new thread on the <a href="https://repost.aws/tags/TA6AYmq2HJTX-wdzz5hHDvuA/amazon-verified-permissions" target="_blank" rel="noopener">Amazon Verified Permissions re:Post</a> or <a href="https://console.aws.amazon.com/support/home" target="_blank" rel="noopener">contact AWS Support</a>.</p> 
       <!-- '"` -->