docs: Add Regal docs to OPA site (#7874)
This will show the project under /projects/, where some other upcoming additions will have their docs too. Signed-off-by: Charlie Egan <charlieegan3@users.noreply.github.com> Co-authored-by: Charlie Egan <charlieegan3@users.noreply.github.com>
@@ -8,7 +8,10 @@ ci:
|
||||
|
||||
.PHONY: dev
|
||||
dev:
|
||||
npx docusaurus start
|
||||
# --no-open means that the browser will not be opened on start.
|
||||
# This is done to avoid opening many tabs repeatedly when editing
|
||||
# docusaurus.config.js.
|
||||
npx docusaurus start --no-open
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1
|
||||
then
|
||||
echo "curl could not be found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v unzip >/dev/null 2>&1
|
||||
then
|
||||
echo "unzip could not be found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
download_regal() {
|
||||
ref="heads/main"
|
||||
if [[ -v VERSION ]]; then
|
||||
ref="tags/$VERSION";
|
||||
fi
|
||||
|
||||
# examples
|
||||
# https://github.com/open-policy-agent/regal/archive/refs/heads/main.zip
|
||||
# https://github.com/open-policy-agent/regal/archive/refs/tags/v0.35.1.zip
|
||||
url="https://github.com/open-policy-agent/regal/archive/refs/$ref.zip"
|
||||
|
||||
curl --silent -L -o regal.zip "$url"
|
||||
}
|
||||
|
||||
if [[ ! -e regal.zip ]]; then
|
||||
download_regal
|
||||
else
|
||||
echo "Using existing regal.zip"
|
||||
fi
|
||||
|
||||
tempdir=$(mktemp -d)
|
||||
|
||||
unzip regal.zip -d "$tempdir" 2>&1 > /dev/null
|
||||
|
||||
mv $tempdir/*/* $tempdir
|
||||
|
||||
regal_docs_src="$tempdir/docs"
|
||||
regal_docs_dest="projects/regal"
|
||||
|
||||
rm -rf "$regal_docs_dest"
|
||||
mkdir -p "$regal_docs_dest"
|
||||
|
||||
# copy assets
|
||||
rsync -ah "$regal_docs_src/assets/." "$regal_docs_dest/assets" --delete
|
||||
|
||||
# generate index
|
||||
readme_sections_dir="$regal_docs_src/readme-sections"
|
||||
manifest="$readme_sections_dir/website-manifest"
|
||||
|
||||
tmpfile=$(mktemp)
|
||||
|
||||
while IFS= read -r file; do
|
||||
section_path="$readme_sections_dir/$file"
|
||||
|
||||
if [[ -f "$section_path" ]]; then
|
||||
cat "$section_path" >> "$tmpfile"
|
||||
echo -e "\n" >> "$tmpfile"
|
||||
else
|
||||
echo "Section file not found: $section_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
done < "$manifest"
|
||||
|
||||
mv $tmpfile "$regal_docs_dest/index.md"
|
||||
|
||||
# copy in rules
|
||||
cp -r "$regal_docs_src/rules" "$regal_docs_dest/"
|
||||
|
||||
# generate other files
|
||||
find "$regal_docs_src" -type f -name '*.md.yaml' | while read -r yaml_file; do
|
||||
md_file="$(dirname $yaml_file)/$(basename "$yaml_file" .yaml)"
|
||||
md_file_rel=${md_file#"$regal_docs_src/"}
|
||||
dest_md_file="$regal_docs_dest/$md_file_rel"
|
||||
|
||||
mkdir -p "$(dirname $dest_md_file)"
|
||||
|
||||
if [[ ! -e $md_file ]]; then
|
||||
echo "Warning: $md_file missing"
|
||||
else
|
||||
echo -e "---\n$(cat $yaml_file)\n---\n\n" > $dest_md_file
|
||||
cat $md_file >> $dest_md_file
|
||||
fi
|
||||
done
|
||||
@@ -6,6 +6,7 @@ import fs from "fs/promises";
|
||||
const path = require("path");
|
||||
|
||||
const { loadPages } = require("./src/lib/ecosystem/loadPages");
|
||||
const { loadRules } = require("./src/lib/projects/regal/loadRules");
|
||||
|
||||
const baseUrl = "/";
|
||||
|
||||
@@ -136,13 +137,28 @@ const baseUrl = "/";
|
||||
{ href: "https://blog.openpolicyagent.org/", label: "Blog" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "dropdown",
|
||||
label: "Projects",
|
||||
position: "right",
|
||||
items: [
|
||||
{ to: "/docs", label: "OPA" },
|
||||
{ to: "/projects/regal", label: "Regal" },
|
||||
{
|
||||
type: "html",
|
||||
value: "<hr style=\"margin: 0.3rem 1rem\">",
|
||||
},
|
||||
{ href: "https://open-policy-agent.github.io/gatekeeper/website/", label: "OPA Gatekeeper" },
|
||||
{ href: "https://www.conftest.dev", label: "Conftest" },
|
||||
],
|
||||
},
|
||||
{ to: "/ecosystem/", label: "Ecosystem", position: "right" },
|
||||
{ href: "https://play.openpolicyagent.org/", label: "Play", position: "right" },
|
||||
{
|
||||
type: "html",
|
||||
position: "right",
|
||||
value: `
|
||||
<a href="https://github.com/open-policy-agent/opa"
|
||||
<a href="https://github.com/open-policy-agent"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="GitHub repository">
|
||||
@@ -267,6 +283,15 @@ The Linux Foundation has registered trademarks and uses trademarks. For a list o
|
||||
},
|
||||
|
||||
plugins: [
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "regal",
|
||||
path: "projects/regal",
|
||||
routeBasePath: "projects/regal",
|
||||
sidebarPath: require.resolve("./src/lib/sidebar-regal.js"),
|
||||
},
|
||||
],
|
||||
[
|
||||
require.resolve("@easyops-cn/docusaurus-search-local"),
|
||||
{
|
||||
@@ -489,6 +514,22 @@ The Linux Foundation has registered trademarks and uses trademarks. For a list o
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
async function ecosystemData(context, options) {
|
||||
return {
|
||||
name: "regal",
|
||||
|
||||
async loadContent() {
|
||||
const rules = await loadRules();
|
||||
|
||||
return { rules };
|
||||
},
|
||||
|
||||
async contentLoaded({ content, actions }) {
|
||||
await actions.createData("rules.json", JSON.stringify(content.rules, null, 2));
|
||||
},
|
||||
};
|
||||
},
|
||||
],
|
||||
clientModules: [
|
||||
require.resolve("./src/lib/playground.js"),
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.8.1",
|
||||
"@docusaurus/plugin-content-docs": "^3.8.1",
|
||||
"@docusaurus/plugin-google-gtag": "^3.8.1",
|
||||
"@docusaurus/preset-classic": "^3.8.1",
|
||||
"@docusaurus/theme-mermaid": "^3.8.1",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.8.1",
|
||||
"@docusaurus/plugin-content-docs": "^3.8.1",
|
||||
"@docusaurus/plugin-google-gtag": "^3.8.1",
|
||||
"@docusaurus/preset-classic": "^3.8.1",
|
||||
"@docusaurus/theme-mermaid": "^3.8.1",
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
sidebar_position: 13
|
||||
---
|
||||
|
||||
|
||||
# Adopters
|
||||
|
||||
We're happy to be trusted by the following projects and organizations.
|
||||
|
||||
If you're using Regal, please consider opening a pull request to add your project or organization to this list!
|
||||
|
||||
## Open Source Projects
|
||||
|
||||
Public open source projects integrating Regal for linting in their CI/CD pipelines.
|
||||
|
||||
<!-- cspell:disable -->
|
||||
- [Brainiac](https://github.com/carbonetes/brainiac)
|
||||
- [Cloudbeat](https://github.com/elastic/cloudbeat)
|
||||
- [Conftest](https://github.com/open-policy-agent/conftest)
|
||||
- [CVAT](https://github.com/opencv/cvat)
|
||||
- [GKE Policy Automation](https://github.com/google/gke-policy-automation)
|
||||
- [Konstraint](https://github.com/plexsystems/konstraint)
|
||||
- [Kubescape](https://github.com/kubescape/regolibrary)
|
||||
- [Legitify](https://github.com/Legit-Labs/legitify)
|
||||
- [Matrix Authentication Service](https://github.com/element-hq/matrix-authentication-service/)
|
||||
- [Minder](https://github.com/stacklok/minder)
|
||||
- [Modernisation Platform](https://github.com/ministryofjustice/modernisation-platform)
|
||||
- [OPA Library](https://github.com/open-policy-agent/library)
|
||||
- [Red Hat COP](https://github.com/redhat-cop/rego-policies)
|
||||
- [ScubaGear](https://github.com/cisagov/ScubaGear)
|
||||
- [ScubaGoggles](https://github.com/cisagov/ScubaGoggles)
|
||||
- [Spacelift Policy Library](https://github.com/spacelift-io/spacelift-policies-example-library)
|
||||
- [Trino Operator](https://github.com/stackabletech/trino-operator)
|
||||
- [Trivy](https://github.com/aquasecurity/trivy-checks)
|
||||
- [Phylum](https://github.com/phylum-dev/policy)
|
||||
<!-- cspell:enable -->
|
||||
|
||||
## Integrations
|
||||
|
||||
Projects and products that integrate Regal into their offerings.
|
||||
|
||||
<!-- cspell:disable -->
|
||||
- [Dependency Management Data](https://gitlab.com/tanna.dev/dependency-management-data)
|
||||
- [Enterprise OPA](https://github.com/styrainc/enterprise-opa)
|
||||
- [The Rego Playground](https://play.openpolicyagent.org)
|
||||
- [Trunk Check](https://trunk.io/check)
|
||||
- [reviewdog/action-regal](https://github.com/reviewdog/action-regal)
|
||||
<!-- cspell:enable-->
|
||||
|
||||
## Packaging
|
||||
|
||||
The following package managers include Regal in their repositories, either natively or via plugins.
|
||||
|
||||
- [Homebrew](https://brew.sh/) via the [regal](https://formulae.brew.sh/formula/regal) formula
|
||||
- [asdf](https://asdf-vm.com/) via [asdf-regal](https://github.com/asdf-community/asdf-regal)
|
||||
- [mise](https://mise.jdx.dev/) via its [aqua](https://aquaproj.github.io/) backend and [aqua's regal definition](https://github.com/aquaproj/aqua-registry/tree/main/pkgs/StyraInc/regal)
|
||||
- [pkgsrc](https://www.pkgsrc.se/) and the [regal](https://pkgsrc.se/devel/regal) package
|
||||
- [Nix](https://nixos.org/): [regal](https://search.nixos.org/packages?channel=24.05&show=regal&from=0&size=50&sort=relevance&type=packages&query=regal)
|
||||
- [mason.vim](https://github.com/williamboman/mason.nvim): [regal](https://github.com/mason-org/mason-registry/blob/main/packages/regal/package.yaml)
|
||||
|
||||
## Companies and Organizations
|
||||
|
||||
Some companies and organizations using Regal.
|
||||
|
||||
<!-- cspell:disable -->
|
||||
- [ARMO](https://www.armosec.io)
|
||||
- [Aqua Security](https://www.aquasec.com)
|
||||
- [Atlassian](https://www.atlassian.com)
|
||||
- [Bankdata](https://www.bankdata.dk)
|
||||
- [CISA](https://www.cisa.gov)
|
||||
- [Elastic](https://www.elastic.co)
|
||||
- [Google](https://www.google.com)
|
||||
- [Microsoft](https://www.microsoft.com)
|
||||
- [Ministry of Justice](https://www.gov.uk/government/organisations/ministry-of-justice)
|
||||
- [Miro](https://miro.com)
|
||||
- [OpenCV](https://opencv.org)
|
||||
- [Red Hat](https://www.redhat.com)
|
||||
- [Spacelift](https://www.spacelift.io)
|
||||
- [Stacklok](https://stacklok.com)
|
||||
- [Styra](https://www.styra.com)
|
||||
- [UNIwise](https://uniwise.eu/)
|
||||
- [VodafoneZiggo](https://www.vodafoneziggo.nl)
|
||||
<!-- cspell:enable-->
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
|
||||
# Architecture
|
||||
|
||||
Or "How does Regal work?"
|
||||
|
||||
As you might have [read](https://www.styra.com/blog/guarding-the-guardrails-introducing-regal-the-rego-linter/), Regal
|
||||
[uses Rego for linting Rego](https://www.styra.com/blog/linting-rego-with-rego/) — or rather, Rego policies turned into
|
||||
a JSON representation of their abstract syntax tree (AST).
|
||||
|
||||
## High-level Overview
|
||||
|
||||
When running Regal against a directory, like `regal lint my-policies/`, Regal does the following:
|
||||
|
||||
- For each source file provided for linting, Regal parses the Rego into its AST representation. This AST representation
|
||||
is then turned into JSON and provided as the **input** variable to the linter rules.
|
||||
- Each linter rule (and there are almost 40 of them at the time of writing this) uses the **input**, which contains
|
||||
information such as the package name, what imports are used, and all the rules and the expressions they contain, to
|
||||
determine whether the Rego policy linted contains any violations against the rule. An example could be a rule that
|
||||
[forbids shadowing](https://openpolicyagent.org/projects/regal/rules/bugs/rule-shadows-builtin) (i.e. using the same name as)
|
||||
built-in functions and operators.
|
||||
- Since rule bodies aren’t necessarily flat, but may contain nested bodies of constructs such as
|
||||
[comprehensions](https://www.openpolicyagent.org/docs/policy-language/#comprehensions) or
|
||||
[every](https://www.openpolicyagent.org/docs/policy-language/#every-keyword) blocks, many linter rules need to
|
||||
traverse all expressions in order to find what they are looking for. This is normally done with the help of the
|
||||
built-in [walk](https://www.openpolicyagent.org/docs/policy-reference/#graph) function.
|
||||
- Traversing huge AST structures — and some policies contain millions of AST nodes! — takes time. This isn’t noticeable
|
||||
when linting a single file, but for some of the largest policy repositories out there, with several thousands of
|
||||
policy files and tests, the cost may be prohibitive. To alleviate this, Regal is implemented to process files
|
||||
concurrently to minimize the impact of IO bound tasks, and to make use of multiple cores when available.
|
||||
- The result of linting each file is eventually collected and compiled into a linter report, which is presented to the
|
||||
user in one of the available [output formats](https://openpolicyagent.org/projects/regal#output-formats).
|
||||
|
||||
## Rego Rules Evaluation
|
||||
|
||||
The main entrypoint for Rego rule evaluation is unsurprisingly found in
|
||||
[main.rego](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/main.rego), in which we query the `report`
|
||||
rule from the [Go](https://github.com/open-policy-agent/regal/blob/main/pkg/linter/linter.go) application.
|
||||
|
||||
The `report` rule in turn uses
|
||||
[dynamic policy composition](https://www.styra.com/blog/dynamic-policy-composition-for-opa/) to query all rules named
|
||||
`report` under `data.regal.rules[category][title]` for built-in rules, and `data.custom.regal.rules[category][title]`
|
||||
for custom rules. The violations reported from each rule is added to the `report` set and sent back to the application,
|
||||
which will compile a final report and present it to the user.
|
||||
|
After Width: | Height: | Size: 805 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 684 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,62 @@
|
||||
---
|
||||
sidebar_label: CI/CD
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
|
||||
# Using Regal in your build pipeline
|
||||
|
||||
Its possible to use Regal to lint your Rego policies in your CI/CD pipeline(s)!
|
||||
|
||||
This document will guide you on how to do so. Please also review the
|
||||
[CLI](./cli) documentation for more information on the available options.
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
If you'd like to run Regal in GitHub actions, please consider using
|
||||
[`setup-regal`](https://github.com/open-policy-agent/setup-regal). A simple `.github/workflows/lint.yml` to run regal
|
||||
on PRs could look like this, where `policy` contains Rego files:
|
||||
|
||||
```yaml
|
||||
name: Regal Lint
|
||||
on:
|
||||
pull_request:
|
||||
jobs:
|
||||
lint-rego:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: open-policy-agent/setup-regal@v1
|
||||
with:
|
||||
# For production workflows, use a specific version, like v0.22.0
|
||||
version: latest
|
||||
|
||||
- name: Lint
|
||||
run: regal lint --format=github ./policy
|
||||
```
|
||||
|
||||
Please see [`setup-regal`](https://github.com/open-policy-agent/setup-regal) for more information.
|
||||
|
||||
## GitLab CI/CD
|
||||
|
||||
To use Regal in GitLab CI/CD, you could for example use the following stage in your `.gitlab-ci.yml`:
|
||||
|
||||
```yaml
|
||||
regal_lint_policies:
|
||||
stage: regal-lint
|
||||
image:
|
||||
# For production workflows, use a specific version, like v0.22.0
|
||||
name: ghcr.io/open-policy-agent/regal:latest
|
||||
entrypoint: ['/bin/sh', '-c']
|
||||
script:
|
||||
- regal lint ./policy --format junit > regal-results.xml
|
||||
artifacts:
|
||||
reports:
|
||||
junit: regal-results.xml
|
||||
when: always
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
```
|
||||
|
||||
The above will run Regal on the `policy` directory when a merge request is created or updated and will show linting
|
||||
violations as part of the merge request.
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
sidebar_label: CLI
|
||||
---
|
||||
|
||||
|
||||
# CLI
|
||||
|
||||
Regal's CLI is the main way to interact with Regal. In order to support
|
||||
different use cases (Local, CI, etc.) Regal's CLI is designed with a number of
|
||||
different formats and exit behaviors.
|
||||
|
||||
## Output Formats
|
||||
|
||||
The `regal lint` command allows specifying the output format by using the `--format` flag. The available output formats
|
||||
are:
|
||||
|
||||
- `pretty` (default) - Human-readable table-like output where each violation is printed with a detailed explanation
|
||||
- `compact` - Human-readable output where each violation is printed on a single line
|
||||
- `json` - JSON output, suitable for programmatic consumption
|
||||
- `github` - GitHub [workflow command](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions)
|
||||
output, ideal for use in GitHub Actions. Annotates PRs and creates a
|
||||
[job summary](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary)
|
||||
from the linter report
|
||||
- `sarif` - [SARIF](https://sarifweb.azurewebsites.net/) JSON output, for consumption by tools processing code analysis
|
||||
reports
|
||||
- `junit` - JUnit XML output, e.g. for CI servers like GitLab that show these results in a merge request.
|
||||
|
||||
## Exit Codes
|
||||
|
||||
Exit codes are used to indicate the result of the `lint` command. The `--fail-level` provided for `regal lint` may be
|
||||
used to change the exit code behavior, and allows a value of either `warning` or `error` (default).
|
||||
|
||||
If `--fail-level error` is supplied, exit code will be zero even if warnings are present:
|
||||
|
||||
- `0`: no errors were found
|
||||
- `0`: one or more warnings were found
|
||||
- `3`: one or more errors were found
|
||||
|
||||
This is the default behavior.
|
||||
|
||||
If `--fail-level warning` is supplied, warnings will result in a non-zero exit code:
|
||||
|
||||
- `0`: no errors or warnings were found
|
||||
- `2`: one or more warnings were found
|
||||
- `3`: one or more errors were found
|
||||
|
||||
## OPA Check and Strict Mode
|
||||
|
||||
OPA itself provides a "linter" of sorts, via the `opa check` command and its `--strict` flag. This checks the provided
|
||||
Rego files not only for syntax errors, but also for OPA
|
||||
[strict mode](https://www.openpolicyagent.org/docs/policy-language/#strict-mode) violations. Most of the strict
|
||||
mode checks from before OPA 1.0 have now been made default checks in OPA, and only two additional checks are currently
|
||||
provided by the `--strict` flag. Those are both important checks not covered by Regal though, so our recommendation is
|
||||
to run `opa check --strict` against your policies before linting with Regal.
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
|
||||
# Capabilities
|
||||
|
||||
By default, Regal will lint your policies using the
|
||||
[capabilities](https://www.openpolicyagent.org/docs/deployments/#capabilities) of the latest version of OPA
|
||||
known to Regal (i.e. the latest version of OPA at the time Regal was released). Sometimes you might want to tell Regal
|
||||
that some rules aren't applicable to your project (yet!). As an example, if you're running OPA v0.46.0, you likely won't
|
||||
be helped by the [custom-has-key](https://openpolicyagent.org/projects/regal/rules/idiomatic/custom-has-key-construct) rule, as it
|
||||
suggests using the `object.keys` built-in function introduced in OPA v0.47.0. The opposite could also be true —
|
||||
sometimes new versions of OPA will invalidate rules that applied to older versions. An example of this is the upcoming
|
||||
introduction of `import rego.v1`, which will make
|
||||
[implicit-future-keywords](https://openpolicyagent.org/projects/regal/rules/imports/implicit-future-keywords) obsolete, as importing
|
||||
`rego.v1` automatically imports all "future" functions.
|
||||
|
||||
Capabilities help you tell Regal which features to take into account, and rules with dependencies to capabilities
|
||||
not available or not applicable in the given version will be skipped.
|
||||
|
||||
If you'd like to target a specific version of OPA, you can include a `capabilities` section in your configuration,
|
||||
providing either a specific `version` of an `engine` (currently only `opa` supported):
|
||||
|
||||
```yaml
|
||||
capabilities:
|
||||
from:
|
||||
engine: opa
|
||||
version: v0.58.0
|
||||
```
|
||||
|
||||
You can also choose to import capabilities from a file:
|
||||
|
||||
```yaml
|
||||
capabilities:
|
||||
from:
|
||||
file: build/capabilities.json
|
||||
```
|
||||
|
||||
You can use `plus` and `minus` to add or remove built-in functions from the given set of capabilities:
|
||||
|
||||
```yaml
|
||||
capabilities:
|
||||
from:
|
||||
engine: opa
|
||||
version: v0.58.0
|
||||
minus:
|
||||
builtins:
|
||||
# exclude rules that depend on the http.send built-in function
|
||||
- name: http.send
|
||||
plus:
|
||||
builtins:
|
||||
# make Regal aware of a custom "ldap.query" function
|
||||
- name: ldap.query
|
||||
type: function
|
||||
decl:
|
||||
args:
|
||||
- type: string
|
||||
result:
|
||||
type: object
|
||||
```
|
||||
|
||||
## Loading Capabilities from URLs
|
||||
|
||||
Starting with Regal version v0.26.0, Regal can load capabilities from URLs with the `http`, or `https` schemes using
|
||||
the `capabilities.from.url` config key. For example, to load capabilities from `https://example.org/capabilities.json`,
|
||||
this configuration could be used:
|
||||
|
||||
```yaml
|
||||
capabilities:
|
||||
from:
|
||||
url: https://example.org/capabilities.json
|
||||
```
|
||||
|
||||
## Supported Engines
|
||||
|
||||
Regal includes capabilities files for the following engines:
|
||||
|
||||
| Engine | Website | Description |
|
||||
| ------ | --------------------------------------------------------------- | -------------------- |
|
||||
| `opa` | [OPA website](https://www.openpolicyagent.org/) | Open Policy Agent |
|
||||
| `eopa` | [Enterprise OPA website](https://www.styra.com/enterprise-opa/) | Styra Enterprise OPA |
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
|
||||
# Ignoring Rules
|
||||
|
||||
If one of Regal's rules doesn't align with your team's preferences, don't worry! Regal is not meant to be the law,
|
||||
and some rules may not make sense for your project, or parts of it.
|
||||
Regal provides several different methods to ignore rules with varying precedence.
|
||||
The available methods are (ranked highest to lowest precedence):
|
||||
|
||||
- [Inline Ignore Directives](#inline-ignore-directives) cannot be overridden by any other method.
|
||||
- Enabling or Disabling Rules with CLI flags.
|
||||
- Enabling or Disabling Rules with `--enable` and `--disable` CLI flags.
|
||||
- Enabling or Disabling Rules with `--enable-category` and `--disable-category` CLI flags.
|
||||
- Enabling or Disabling All Rules with `--enable-all` and `--disable-all` CLI flags.
|
||||
- See [Ignoring Rules via CLI Flags](#ignoring-rules-via-cli-flags) for more details.
|
||||
- [Ignoring a Rule In Config](#ignoring-a-rule-in-config)
|
||||
- [Ignoring a Category In Config](#ignoring-a-category-in-config)
|
||||
- [Ignoring All Rules In Config](#ignoring-all-rules-in-config)
|
||||
|
||||
In summary, the CLI flags will override any configuration provided in the file, and inline ignore directives for a
|
||||
specific line will override any other method.
|
||||
|
||||
It's also possible to ignore messages on a per-file basis. The available methods are (ranked High to Lowest precedence):
|
||||
|
||||
<!-- markdownlint-disable MD051 -->
|
||||
|
||||
- Using the `--ignore-files` CLI flag.
|
||||
See [Ignoring Rules via CLI Flags](#ignoring-rules-via-cli-flags).
|
||||
- [Ignoring Files Globally](#ignoring-files-globally) or
|
||||
[Ignoring a Rule in Some Files](#ignoring-a-rule-in-some-files).
|
||||
|
||||
## Ignoring a Rule in Config
|
||||
|
||||
If you want to ignore a rule, set its level to `ignore` in the configuration file:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
style:
|
||||
prefer-snake-case:
|
||||
# At example.com, we use camel case to comply with our naming conventions
|
||||
level: ignore
|
||||
```
|
||||
|
||||
## Ignoring a Category in Config
|
||||
|
||||
If you want to ignore a category of rules, set its default level to `ignore` in the configuration file:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
style:
|
||||
default:
|
||||
level: ignore
|
||||
```
|
||||
|
||||
## Ignoring All Rules in Config
|
||||
|
||||
If you want to ignore all rules, set the default level to `ignore` in the configuration file:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
default:
|
||||
level: ignore
|
||||
# then you can re-enable specific rules or categories
|
||||
testing:
|
||||
default:
|
||||
level: error
|
||||
style:
|
||||
opa-fmt:
|
||||
level: error
|
||||
```
|
||||
|
||||
**Tip**: providing a comment on ignored rules is a good way to communicate why the decision was made.
|
||||
|
||||
## Ignoring a Rule in Some Files
|
||||
|
||||
You can use the `ignore` attribute inside any rule configuration to provide a list of files, or patterns, that should
|
||||
be ignored for that rule:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
style:
|
||||
line-length:
|
||||
level: error
|
||||
ignore:
|
||||
files:
|
||||
# ignore line length in test files to accommodate messy test data
|
||||
- "*_test.rego"
|
||||
# specific file used only for testing
|
||||
- "scratch.rego"
|
||||
```
|
||||
|
||||
## Ignoring Files Globally
|
||||
|
||||
**Note**: Ignoring files will disable most language server features
|
||||
for those files. Only formatting will remain available.
|
||||
Ignored files won't be used for completions, linting, or definitions
|
||||
in other files.
|
||||
|
||||
If you want to ignore certain files for all rules, you can use the global ignore attribute in your configuration file:
|
||||
|
||||
```yaml
|
||||
ignore:
|
||||
files:
|
||||
- file1.rego
|
||||
- "*_tmp.rego"
|
||||
```
|
||||
|
||||
## Inline Ignore Directives
|
||||
|
||||
If you'd like to ignore a specific violation in a file, you can add an ignore directive above the line in question, or
|
||||
alternatively on the same line to the right of the expression:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# regal ignore:prefer-snake-case
|
||||
camelCase := "yes"
|
||||
|
||||
list_users contains user if { # regal ignore:avoid-get-and-list-prefix
|
||||
some user in data.db.users
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
The format of an ignore directive is `regal ignore:<rule-name>,<rule-name>...`, where `<rule-name>` is the name of the
|
||||
rule to ignore. Multiple rules may be added to the same ignore directive, separated by commas.
|
||||
|
||||
Note that at this point in time, Regal only considers the same line or the line following the ignore directive, i.e. it
|
||||
does not apply to entire blocks of code (like rules, functions or even packages). See [configuration](#configuration)
|
||||
if you want to ignore certain rules altogether.
|
||||
|
||||
## Ignoring Rules via CLI Flags
|
||||
|
||||
For development and testing, rules or classes of rules may quickly be enabled or disabled using the relevant CLI flags
|
||||
for the `regal lint` command:
|
||||
|
||||
- `--disable-all` disables **all** rules
|
||||
- `--disable-category` disables all rules in a category, overriding `--enable-all` (may be repeated)
|
||||
- `--disable` disables a specific rule, overriding `--enable-all` and `--enable-category` (may be repeated)
|
||||
- `--enable-all` enables **all** rules
|
||||
- `--enable-category` enables all rules in a category, overriding `--disable-all` (may be repeated)
|
||||
- `--enable` enables a specific rule, overriding `--disable-all` and `--disable-category` (may be repeated)
|
||||
- `--ignore-files` ignores files using glob patterns, overriding `ignore` in the config file (may be repeated)
|
||||
|
||||
**Note:** all CLI flags override configuration provided in file.
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
|
||||
# Configuration
|
||||
|
||||
A custom configuration file may be used to override the [default configuration](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/config/provided/data.yaml)
|
||||
options provided by Regal. The most common use case for this is to change the severity level of a rule. These three
|
||||
levels are available:
|
||||
|
||||
- `ignore` — disable the rule entirely
|
||||
- `warning` — report the violation without changing the exit code of the lint command
|
||||
- `error` — report the violation and have the lint command exit with a non-zero exit code (default)
|
||||
|
||||
Additionally, some rules may have configuration options of their own. See the documentation page for a rule to learn
|
||||
more about it.
|
||||
|
||||
**.regal/config.yaml** or **.regal.yaml**
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
style:
|
||||
todo-comment:
|
||||
# don't report on todo comments
|
||||
level: ignore
|
||||
line-length:
|
||||
# custom rule configuration
|
||||
max-line-length: 100
|
||||
# warn on too long lines, but don't fail
|
||||
level: warning
|
||||
opa-fmt:
|
||||
# not needed as error is the default, but
|
||||
# being explicit won't hurt
|
||||
level: error
|
||||
# files can be ignored for any individual rule
|
||||
# in this example, test files are ignored
|
||||
ignore:
|
||||
files:
|
||||
- "*_test.rego"
|
||||
custom:
|
||||
# custom rule configuration
|
||||
naming-convention:
|
||||
level: error
|
||||
conventions:
|
||||
# ensure all package names start with "acmecorp" or "system"
|
||||
- pattern: '^acmecorp\.[a-z_\.]+$|^system\.[a-z_\.]+$'
|
||||
targets:
|
||||
- package
|
||||
|
||||
capabilities:
|
||||
from:
|
||||
# optionally configure Regal to target a specific version of OPA
|
||||
# this will disable rules that has dependencies to e.g. built-in
|
||||
# functions or features not supported by the given version
|
||||
#
|
||||
# if not provided, Regal will use the capabilities of the latest
|
||||
# version of OPA available at the time of the Regal release
|
||||
engine: opa
|
||||
version: v0.58.0
|
||||
|
||||
ignore:
|
||||
# files can be excluded from all lint rules according to glob-patterns
|
||||
files:
|
||||
- file1.rego
|
||||
- "*_tmp.rego"
|
||||
|
||||
project:
|
||||
roots:
|
||||
# declares the 'main' and 'lib/jwt' directories as project roots
|
||||
- main
|
||||
- lib/jwt
|
||||
# may also be provided as an object with additional options
|
||||
- path: lib/legacy
|
||||
rego-version: 0
|
||||
```
|
||||
|
||||
Regal will automatically search for a configuration file (`.regal/config.yaml`
|
||||
or `.regal.yaml`) in the current directory, and if not found, traverse the
|
||||
parent directories either until either one is found, or the top of the directory
|
||||
hierarchy is reached. If no configuration file is found, and no file is found at
|
||||
`~/.config/regal/config.yaml` either, Regal will use the default configuration.
|
||||
|
||||
A custom configuration may be also be provided using the `--config-file`/`-c`
|
||||
option for `regal lint`, which when provided will be used to override the
|
||||
default configuration.
|
||||
|
||||
## User-level Configuration
|
||||
|
||||
Generally, users will want to commit their Regal configuration file to the repo
|
||||
containing their Rego source code. This allows configurations to be shared
|
||||
among team members and makes the configuration options available to Regal when
|
||||
running as a [CI linter](https://openpolicyagent.org/projects/regal/cicd) too.
|
||||
|
||||
Sometimes however it can be handy to have some user defaults when a project
|
||||
configuration file is not found, hasn't been created yet or is not applicable.
|
||||
|
||||
In such cases Regal will check for a configuration file at
|
||||
`~/.config/regal/config.yaml` instead.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
|
||||
# Project Roots
|
||||
|
||||
While many projects consider the project's root directory (in editors often referred to as **workspace**) their
|
||||
"main" directory for policies, some projects may contain code from other languages, policy "subprojects", or multiple
|
||||
[bundles](https://www.openpolicyagent.org/docs/management-bundles/). While most of Regal's features works
|
||||
independently of this — linting, for example, doesn't consider where in a workspace policies are located as long as
|
||||
those locations aren't [ignored](./ignore-rules) — some features, like automatically
|
||||
[fixing](https://openpolicyagent.org/projects/regal/fixing) violations, benefit from knowing when a project contains multiple roots.
|
||||
|
||||
To provide an example, consider the
|
||||
[directory-package-mismatch](https://openpolicyagent.org/projects/regal/rules/idiomatic/directory-package-mismatch) rule, which states
|
||||
that a file declaring a `package` path like `policy.permissions.users` should also be located in a directory structure
|
||||
that mirrors that package, i.e. `policy/permissions/users`. When a violation against this rule is reported, the
|
||||
`regal fix` command, or its equivalent [Code Action](https://openpolicyagent.org/projects/regal#regal-language-server) in editors,
|
||||
may when invoked remediate the issue by moving the file to the correct location.
|
||||
But where should the `policy/permissions/users` directory _itself_ reside?
|
||||
|
||||
Normally, the answer to that question would be the **project**, or **workspace** root. But if the file was found
|
||||
in a subdirectory containing a **bundle**, the directory naturally belongs under that _bundle's root_ instead. The
|
||||
`roots` configuration option under the top-level `project` object allows you to tell Regal where these roots are,
|
||||
and have features like the `directory-package-mismatch` fixer work as you'd expect.
|
||||
|
||||
```yaml
|
||||
project:
|
||||
roots:
|
||||
- bundle1
|
||||
- bundle2
|
||||
```
|
||||
|
||||
The configuration file is not the only way Regal may determine project roots. Other ways include:
|
||||
|
||||
- A directory containing a `.manifest` file will automatically be registered as a root
|
||||
- A directory containing a `.regal` directory will be registered as a root (this is normally the project root)
|
||||
|
||||
If a feature that depends on project roots fails to identify any, it will either fail or fall back on the directory
|
||||
in which the command was run.
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
sidebar_label: Rego Version
|
||||
---
|
||||
|
||||
|
||||
# Configuring Rego Version
|
||||
|
||||
From OPA 1.0 and onwards, it is no longer necessary to include `import rego.v1` in your policies in order to use
|
||||
keywords like `if` and `contains`. Since Regal works with with both 1.0+ policies and older versions of Rego, the linter
|
||||
will first try to parse a policy as 1.0 and if that fails, parse using "v0" rules. This process isn't 100% foolproof,
|
||||
as some policies are valid in both versions. Additionally, parsing the same file multiple times adds some overhead that
|
||||
can be skipped if the version is known beforehand. To help Regal determine (and enforce) the version of your policies,
|
||||
the `rego-version` attribute can be set in the `project` configuration:
|
||||
|
||||
```yaml
|
||||
project:
|
||||
# Rego version 1.0, set to 0 for pre-1.0 policies
|
||||
rego-version: 1
|
||||
```
|
||||
|
||||
It is also possible to set the Rego version for individual project roots (see below for more information):
|
||||
|
||||
```yaml
|
||||
project:
|
||||
roots:
|
||||
- path: lib/legacy
|
||||
rego-version: 0
|
||||
- path: main
|
||||
rego-version: 1
|
||||
```
|
||||
|
||||
Additionally, Regal will scan the project for any `.manifest` files, and user any `rego_version` found in the manifest
|
||||
for all policies under that directory.
|
||||
|
||||
Note: the `rego-version` attribute in the configuration file has precedence over `rego_version` found in manifest files.
|
||||
@@ -0,0 +1,381 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
|
||||
# Custom Rules
|
||||
|
||||
Regal is built to be easily extended. Using custom rules is a great way to enforce naming conventions, best practices
|
||||
or more opinionated rules across teams and organizations.
|
||||
|
||||
There are two types of custom rules to be aware of — those that are included with Regal in the `custom` category, and
|
||||
those that you write yourself. The rules in the `custom` category provide a way to enforce common organizational
|
||||
requirements, like naming conventions, by means of _configuration_ rather than code. If your requirements aren't
|
||||
fulfilled by the rules in this category, your other option is to write your own custom rules using Rego.
|
||||
|
||||
## Your Own Custom Rules
|
||||
|
||||
If you'd like to provide your own linter rules for a project, you may do so by placing them in a `rules` directory
|
||||
inside the `.regal` directory preferably placed in the root of your project (which is also where custom configuration
|
||||
resides). The directory structure of a policy repository with custom linter rules might then look something like this:
|
||||
|
||||
```text
|
||||
.
|
||||
├── .regal
|
||||
│ ├── config.yaml
|
||||
│ └── rules
|
||||
│ ├── naming.rego
|
||||
│ └── naming_test.rego
|
||||
└── policy
|
||||
├── authz.rego
|
||||
└── authz_test.rego
|
||||
```
|
||||
|
||||
If you so prefer, custom rules may also be provided using the `--rules` option for `regal lint`, which may point either
|
||||
to a Rego file, or a directory containing Rego files and potentially data (JSON or YAML).
|
||||
|
||||
## Creating a New Rule
|
||||
|
||||
The simplest way to create a new rule is to use the `regal new rule` command. This command provides scaffolding for
|
||||
quickly creating a new rule, including a file for testing. The command has two required arguments: `--category` and
|
||||
`--name`, which should be self-explanatory. To create a new custom rule:
|
||||
|
||||
```shell
|
||||
regal new rule --category naming --name foo-bar-baz
|
||||
```
|
||||
|
||||
This will create a `.regal/rules` directory in the current working directory, if one does not already exist, and place
|
||||
a starter policy and a test in a directory structure based on `--category` and `--name` in it. Following the above
|
||||
example would create the following directory structure under `.regal/rules`:
|
||||
|
||||
```text
|
||||
custom/regal/rules/naming/foo-bar-baz/foo_bar_baz.rego
|
||||
custom/regal/rules/naming/foo-bar-baz/foo_bar_baz_test.rego
|
||||
```
|
||||
|
||||
If you'd rather create this directory structure in some other place than the current working directory, you may use the
|
||||
`--output` flag to specify a different location. The generated rule includes a simple example, which can be verified by
|
||||
running `regal test .regal/rules`. Modify the rule and the test to suit your needs!
|
||||
|
||||
If you'd like to create a new built-in rule for submitting a PR in Regal, you may add the `--type builtin` flag to the
|
||||
command (the default is `custom`). This will create a similar scaffolding under `bundle/regal/rules` in the Regal
|
||||
repository.
|
||||
|
||||
## Developing Rules
|
||||
|
||||
Regal rules works primarily with the [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST)
|
||||
provided by OPA's parser as input. The top level item in the AST is the
|
||||
[module](https://pkg.go.dev/github.com/open-policy-agent/opa/ast#Module), which contains nodes for everything found
|
||||
in a policy, like the package declaration, imports and rules.
|
||||
|
||||
Since OPA expects JSON as input, the module and all its child nodes are serialized, and then made available as `input`
|
||||
in Regal linter rule policies. The `opa parse --format json` command can be used to get an idea of what the structure
|
||||
of the serialized AST looks like. However, recent versions of Regal leverage an optimized AST JSON representation called
|
||||
[roast](roast.md), which is both more compact and performant to traverse as part of
|
||||
linting. See the roast docs for more information on how the format differs from the "normal" OPA AST.
|
||||
|
||||
In order to view the JSON AST representation of a policy, use the `regal parse` command. This works similarly to
|
||||
`opa parse`, but only outputs the roast JSON format, including additions made by Regal.
|
||||
|
||||
If we were to write the simplest policy possible, and parse it using `regal parse`, it would contain nothing but a
|
||||
package declaration:
|
||||
|
||||
**policy.rego**
|
||||
|
||||
```rego
|
||||
package policy
|
||||
```
|
||||
|
||||
Using `regal parse policy.rego`, we're provided with the AST of the above policy:
|
||||
|
||||
```json
|
||||
{
|
||||
"package": {
|
||||
"location": "1:1:1:8",
|
||||
"path": [
|
||||
{
|
||||
"type": "var",
|
||||
"value": "data"
|
||||
},
|
||||
{
|
||||
"location": "1:9:1:15",
|
||||
"type": "string",
|
||||
"value": "policy"
|
||||
}
|
||||
]
|
||||
},
|
||||
"regal": {
|
||||
"file": {
|
||||
"name": "policy.rego",
|
||||
"lines": [
|
||||
"package policy",
|
||||
""
|
||||
],
|
||||
"abs": "/Users/anderseknert/tmp/custom/policy.rego"
|
||||
},
|
||||
"environment": {
|
||||
"path_separator": "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As trivial as may be, it's enough to build our first linter rule! Let's say we'd like to enforce a uniform naming
|
||||
convention on any policy in a repository. Packages may be named anything, but must start with the name of the
|
||||
organization (Acme Corp). So `package acme.corp.policy` should be allowed, but not `package policy` or
|
||||
`package policy.acme.corp`. One exception: policy authors should be allowed to write policy for the `system.log` package
|
||||
provided by OPA to allow
|
||||
[masking](https://www.openpolicyagent.org/docs/management-decision-logs/#masking-sensitive-data) sensitive data
|
||||
from decision logs.
|
||||
|
||||
An example policy to implement this requirement might look something like this:
|
||||
|
||||
```rego
|
||||
# METADATA
|
||||
# description: All packages must use "acme.corp" base name
|
||||
# related_resources:
|
||||
# - description: documentation
|
||||
# ref: https://www.acmecorp.example.org/docs/regal/package
|
||||
# schemas:
|
||||
# - input: schema.regal.ast
|
||||
package custom.regal.rules.naming["acme-corp-package"]
|
||||
|
||||
import data.regal.result
|
||||
|
||||
report contains violation if {
|
||||
not acme_corp_package
|
||||
not system_log_package
|
||||
|
||||
violation := result.fail(rego.metadata.chain(), result.location(input.package.path[1]))
|
||||
}
|
||||
|
||||
acme_corp_package if {
|
||||
input.package.path[1].value == "acme"
|
||||
input.package.path[2].value == "corp"
|
||||
}
|
||||
|
||||
system_log_package if {
|
||||
input.package.path[1].value == "system"
|
||||
input.package.path[2].value == "log"
|
||||
}
|
||||
```
|
||||
|
||||
Starting from top to bottom, these are the components comprising our custom rule:
|
||||
|
||||
1. The package of custom rules **must** start with `custom.regal.rules`, followed by the category of the rule, and the
|
||||
title (which is commonly quoted as rule names use `-` for spaces).
|
||||
1. The `data.regal.result` provides some helpers for formatting the result of a violation for inclusion in a report.
|
||||
1. Regal rules make heavy use of [metadata annotations](https://www.openpolicyagent.org/docs/policy-language/#annotations)
|
||||
in order to document the purpose of the rule, along with any other
|
||||
information that could potentially be useful. All rule packages **must** have
|
||||
a `description`. Providing links to additional documentation under
|
||||
`related_resources` is recommended, but not required.
|
||||
1. Note the `schema` attribute present in the metadata annotation. Adding this is optional, but highly recommended, as
|
||||
it will make the compiler aware of the structure of the input, i.e. the AST. This allows the compiler to fail when
|
||||
unknown attributes are referenced, due to typos or other mistakes. The compiler will also fail when an attribute is
|
||||
referenced using a type it does not have, like referring to a string as if it was a number. Set to `schema.regal.ast`
|
||||
to use the AST schema provided by Regal.
|
||||
1. Regal will evaluate any rule named `report` in each linter policy, so at least one `report` rule **must** be present.
|
||||
1. In our example `report` rule, we evaluate another rule (`acme_corp_package`) in order to know if the package name
|
||||
starts with `acme.corp`, and another rule (`system_log_package`) to know if it starts with `system.log`. If neither
|
||||
of the conditions are true, the rule fails and violation is created.
|
||||
1. The violation is created by calling `result.fail`, which takes the metadata from the package (using
|
||||
`rego.metadata.chain` which conveniently also includes the path of the package) and returns a result, which
|
||||
will later be included in the final report provided by Regal.
|
||||
1. The `result.location` helps extract the location from the element failing the test. Make sure to use it!
|
||||
|
||||
### Rule Development Workflow
|
||||
|
||||
In addition to making use of the `regal parse` command to inspect the AST of a policy, using Regal's
|
||||
[language server](https://openpolicyagent.org/projects/regal/language-server) for rule development provides the absolute best rule
|
||||
development experience.
|
||||
|
||||
#### Code Lens for Evaluation
|
||||
|
||||
If you're using VS Code and the [OPA VS Code extension](https://github.com/open-policy-agent/vscode-opa), you may
|
||||
use the [Code Lens for Evaluation](https://openpolicyagent.org/projects/regal/language-server#code-lenses-evaluation) to directly
|
||||
evaluate packages and rules using the `input.json` file as input, and see the result directly in your editor on the
|
||||
line you clicked to evaluate.
|
||||
|
||||
To start evaluating a policy against your custom rule. First turn the parse result of the policy into an input file:
|
||||
|
||||
```shell
|
||||
regal parse path/to/policy.rego > input.json
|
||||
```
|
||||
|
||||
You should now be able evaluate your custom rule against the `input.json` AST:
|
||||
|
||||

|
||||
|
||||
**Tips:**
|
||||
|
||||
- You can hover the inlined result to see the full output
|
||||
- Calls to `print` inside rule bodies will have the print output displayed on the same line
|
||||
|
||||
As another convenience, any `.rego` file where the first comment in the policy is `# regal eval:use-as-input` will have
|
||||
the evaluation feature automatically use the AST of the file as input. This allows building queries against the AST of
|
||||
the policy you're working on, providing an extremely fast feedback loop for developing new rules!
|
||||
|
||||

|
||||
|
||||
#### Test-Driven Development
|
||||
|
||||
Using a test-driven approach to custom rule development is a great way to both understand how your rule works, and to
|
||||
assert that it works as expected even as you make changes to the code. Use the `regal test` command the same way as you
|
||||
would use `opa test`:
|
||||
|
||||
```shell
|
||||
regal test .regal/rules
|
||||
```
|
||||
|
||||
To debug failures in your test, the `--var-values` flag can help by providing more information about which values
|
||||
failed to match the expected output. You can also use the `print` function anywhere in your policy, which will have
|
||||
it's output printed by the test runner.
|
||||
|
||||
Tests commonly first parse a policy, then provide that as input to the rule being tested. You can either use the
|
||||
built-in `regal.parse_module(name, policy)` function to parse a policy, or one of the provided helpers in the
|
||||
`regal.ast` package:
|
||||
|
||||
Using `ast.with_rego_v1(policy)` will have a package declararation and `import rego.v1` added to the policy, allowing
|
||||
you to get straight to what you actually want to test for:
|
||||
|
||||
```rego
|
||||
test_fail_constant_condition if {
|
||||
module := ast.with_rego_v1(`allow if true`)
|
||||
report := rule.report with input as module
|
||||
|
||||
count(report) == 1
|
||||
some violation in report
|
||||
violation.title == "constant-conditoon"
|
||||
}
|
||||
```
|
||||
|
||||
The `ast.policy(policy)` adds only a package declaration and not `import rego.v1`.
|
||||
|
||||
## Aggregate Rules
|
||||
|
||||
Aggregate rules are a special type of rule that allows you to collect data from multiple files before making a decision.
|
||||
This is needed in some cases where a single policy file won't be enough for a linter rule to make a decision. For
|
||||
example, you may want to enforce that in a policy repository, there must be at least one package annotated with an
|
||||
`authors` attribute. This requires first collecting annotation data from all the provided policies, and then have the
|
||||
rule use this data to make a decision. The structure of an aggregate rule is similar to a regular rule, but with a few
|
||||
notable differences. The most significant one is that linting happens in two separate phases — one that aggregates data
|
||||
from files, and one that actually lints and reports violations using that data.
|
||||
|
||||
```rego
|
||||
# METADATA
|
||||
# description: |
|
||||
# There must be at least one boolean rule named `allow`, and it must
|
||||
# have a default value of `false`
|
||||
# related_resources:
|
||||
# - description: documentation
|
||||
# ref: https://www.acmecorp.example.org/docs/regal/aggregate-allow
|
||||
# schemas:
|
||||
# - input: schema.regal.ast
|
||||
package custom.regal.rules.organizational["at-least-one-allow"]
|
||||
|
||||
import data.regal.ast
|
||||
import data.regal.result
|
||||
|
||||
aggregate contains entry if {
|
||||
# ast.rules is input.rules with functions filtered out
|
||||
some rule in ast.rules
|
||||
|
||||
# search for rule named allow
|
||||
ast.ref_to_string(rule.head.ref) == "allow"
|
||||
|
||||
# make sure it's a default assignment
|
||||
# ideally we'll want more than that, but the *requirement* is only
|
||||
# that such a rule exists...
|
||||
rule["default"] == true
|
||||
|
||||
# ...and that it defaults to false
|
||||
rule.head.value.type == "boolean"
|
||||
rule.head.value.value == false
|
||||
|
||||
# if found, collect the result into our aggregate collection
|
||||
# we don't really need the location here, but showing for demonstration
|
||||
entry := result.aggregate(rego.metadata.chain(), {
|
||||
# optional metadata here
|
||||
"package": input.package,
|
||||
})
|
||||
}
|
||||
|
||||
# METADATA
|
||||
# description: |
|
||||
# This is called once all aggregates have been collected. Note the use of a
|
||||
# different schema here for type checking, as the input is no longer the AST
|
||||
# of a Rego policy, but our collected data.
|
||||
# schemas:
|
||||
# - input: schema.regal.aggregate
|
||||
aggregate_report contains violation if {
|
||||
# input.aggregate contains only the entries collected by *this* aggregate rule,
|
||||
# so you don't need to worry about counting entries from other sources here!
|
||||
count(input.aggregate) == 0
|
||||
|
||||
# no aggregated data found, so we'll report a violation
|
||||
# another rule may of course want to make use of the data collected in the aggregation
|
||||
violation := result.fail(rego.metadata.chain(), {
|
||||
"message": "At least one rule named `allow` must exist, and it must have a default value of `false`",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, the aggregate rule is split into two parts — one that collects data (`aggregate`), and one that reports
|
||||
violations (`aggregate_report`).
|
||||
|
||||
Use of helper functions like `result.aggregate` is optional, but **highly** recommended, as it will have any aggregate
|
||||
entry contain information like file, location and package, which is useful both for reporting, but also for debugging.
|
||||
Use a `print` or two in the `aggregate_report` rule to see exactly what's included!
|
||||
|
||||
## Parsing and Testing
|
||||
|
||||
Regal provides a few tools mirrored from OPA in order to help test and debug custom rules. These are necessary since OPA
|
||||
is not aware of the custom [built-in functions](#built-in-functions) included in Regal, and will fail when encountering
|
||||
e.g. `regal.parse_module` in a custom linter policy. The following commands are included with Regal to help you author
|
||||
custom rules:
|
||||
|
||||
- `regal parse` works similarly to `opa parse`, but will always output JSON and include location information, and any
|
||||
additional data added to the AST by Regal. Use this if you want to know exactly what the `input` will look like for
|
||||
any given policy, when provided to Regal for linting.
|
||||
- `regal test` works like `opa test`, but aware of any custom Regal additions, and the schema used for the AST. Use this
|
||||
to test custom linter rules, e.g. `regal test .regal/rules`.
|
||||
|
||||
Note that the `print` built-in function is enabled for `regal test`. Good to use for quick debugging!
|
||||
|
||||
## Built-in Functions
|
||||
|
||||
Regal provides a few custom built-in functions tailor-made for linter policies.
|
||||
|
||||
### `regal.parse_module(filename, policy)`
|
||||
|
||||
Works just like `rego.parse_module`, but provides an AST including location information, and custom additions added
|
||||
by Regal, like the text representation of each line in the original policy. This is useful for authoring tests to assert
|
||||
linter rules work as expected. This is the built-in function equivalent of the `regal parse` command.
|
||||
|
||||
If the `filename` provided ends with `_v0.rego`, the policy will be parsed as a Rego v0 module.
|
||||
|
||||
### `regal.last(array)`
|
||||
|
||||
This built-in function is a much more performant way to express `array[count(array) - 1]`. This performance difference
|
||||
is almost always irrelevant in "normal" Rego policies, but can have a significant impact in linter rules where it's
|
||||
sometimes called thousands of times as part of traversing the input AST.
|
||||
|
||||
## Rego Library
|
||||
|
||||
In addition to this, Regal provides many helpful functions, rules and utilities in Rego. Browsing the source code of the
|
||||
[regal.ast](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/ast/ast.rego) package to see what's
|
||||
available is recommended!
|
||||
|
||||
Note however that at this point in time, the Rego API is not considered stable, and breaking changes are likely to
|
||||
occur between versions. If you need stable versions of rules and functions found here, consider copying them into a
|
||||
library of your own, and use in your custom rules. Or engage with the Regal community and tell us what you need and
|
||||
depend on, and we'll try to take it into account, or at least help you find ways to make it work!
|
||||
|
||||
## Language server
|
||||
|
||||
When using custom rules with the Regal language server, rules will be loaded
|
||||
from the `.regal/rules` directory relative to the selected workspace root.
|
||||
|
||||
Custom rules will be loaded each time a lint is run for a file or the workspace.
|
||||
If your custom rules are broken, fixes will be used on the next linting run.
|
||||
@@ -0,0 +1,228 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
|
||||
# Roast (Regal's Optimized AST)
|
||||
|
||||
Roast is an optimized JSON format for [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) ASTs, as well
|
||||
as common utilities for working with the both the Roast format and OPA's AST APIs.
|
||||
|
||||
Roast is used by [Regal](https://openpolicyagent.org/projects/regal), where the JSON representation of Rego's AST is used input for
|
||||
static analysis [performed by Rego itself](https://www.styra.com/blog/linting-rego-with-rego/) to determine whether
|
||||
policies conform to Regal's linter rules.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This library changes frequently and provides no guarantees for what its API looks like. Depending on this library
|
||||
> directly is not recommended! If you find any code here useful, feel free to use it as your own in your projects, but
|
||||
> be aware that it may change at any time, and you may be better off copy-pasting whatever code you need.
|
||||
|
||||
## Goals
|
||||
|
||||
- Fast to traverse and process in Rego
|
||||
- Usable without having to deal with quirks and inconsistencies
|
||||
- As easy to read as the original AST JSON format
|
||||
|
||||
While this module provides a way to encode an `ast.Module` to an optimized JSON format, it does not provide a decoder.
|
||||
In other words, there's currently no way to turn optimized AST JSON back into an `ast.Module` (or other AST types).
|
||||
While this would be possible to do, there's no real need for that given our current use-case for this format, which is
|
||||
to help work with the AST efficiently in Rego. Roast should not be considered a general purpose format for serializing
|
||||
the Rego AST.
|
||||
|
||||
## Differences
|
||||
|
||||
The following section outlines the differences between the original AST JSON format and the Roast format.
|
||||
|
||||
### Compact `location` format
|
||||
|
||||
The perhaps most visually apparent change to the AST JSON format is how `location` attributes are represented. These
|
||||
attributes are **everywhere** in the AST, so optimizing these for fast traversal has a huge impact on both the size of
|
||||
the format and the speed at which it can be processed.
|
||||
|
||||
In the original AST, a `location` is represented as an object:
|
||||
|
||||
```json
|
||||
{
|
||||
"file": "p.rego",
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"text": "Y29sbGVjdGlvbg=="
|
||||
}
|
||||
```
|
||||
|
||||
And in the optimized format as a string:
|
||||
|
||||
```json
|
||||
"5:1:5:11"
|
||||
```
|
||||
|
||||
The first two numbers are present in both formats, i.e. `row` and `col`. In the optimized format, the third and fourth
|
||||
number is the end location, determined by the length of the `text` attribute decoded. In this case `Y29sbGVjdGlvbg==`
|
||||
decodes to `collection`, which is 10 characters long. The end location is therefore `5:11`. The text can later be
|
||||
retrieved when needed using the original source document as a lookup table of sorts.
|
||||
|
||||
While this may come with a small cost for when the `location` is actually needed, it's a huge win for when it's not.
|
||||
Having to `split` the result and parse the row and column values when needed occurs some overhead, but only a small
|
||||
percentage of `location` attributes are commonly used in practice.
|
||||
|
||||
Note that the `file` attribute is omitted entirely in the optimized format, as this would otherwise have to be repeated
|
||||
for each `location` value. This can easily be retrieved by other means.
|
||||
|
||||
### "Empty" rule and `else` bodies
|
||||
|
||||
Rego rules don't necessarily have a body, or at least not one that's printed. Examples of this include:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default rule := "value"
|
||||
|
||||
map["key"] := "value"
|
||||
|
||||
collection contains "value"
|
||||
```
|
||||
|
||||
OPA represents such rules internally (that is, in the AST) as having a body with a single expression containing the
|
||||
boolean value `true`. This creates a uniform way to represent rules, so a rule like:
|
||||
|
||||
```rego
|
||||
collection contains "value"
|
||||
```
|
||||
|
||||
Would in the AST be identical to:
|
||||
|
||||
```rego
|
||||
collection contains "value" if {
|
||||
true
|
||||
}
|
||||
```
|
||||
|
||||
And in the OPA JSON AST format:
|
||||
|
||||
```json
|
||||
{
|
||||
"body": [
|
||||
{
|
||||
"index": 0,
|
||||
"location": {
|
||||
"file": "p.rego",
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"text": "Y29sbGVjdGlvbg=="
|
||||
},
|
||||
"terms": {
|
||||
"location": {
|
||||
"file": "p.rego",
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"text": "Y29sbGVjdGlvbg=="
|
||||
},
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"head": {
|
||||
"name": "collection",
|
||||
"key": {
|
||||
"location": {
|
||||
"file": "p.rego",
|
||||
"row": 5,
|
||||
"col": 21,
|
||||
"text": "InZhbHVlIg=="
|
||||
},
|
||||
"type": "string",
|
||||
"value": "value"
|
||||
},
|
||||
"ref": [
|
||||
{
|
||||
"location": {
|
||||
"file": "p.rego",
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"text": "Y29sbGVjdGlvbg=="
|
||||
},
|
||||
"type": "var",
|
||||
"value": "collection"
|
||||
}
|
||||
],
|
||||
"location": {
|
||||
"file": "p.rego",
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"text": "Y29sbGVjdGlvbiBjb250YWlucyAidmFsdWUi"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"file": "p.rego",
|
||||
"row": 5,
|
||||
"col": 1,
|
||||
"text": "Y29sbGVjdGlvbg=="
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice how there's 20 lines of JSON just to represent the body, even though there isn't really one!
|
||||
|
||||
The optimized Rego AST format discards generated bodies entirely, and the same rule would be represented as:
|
||||
|
||||
```json
|
||||
{
|
||||
"head": {
|
||||
"location": "5:1:5:11",
|
||||
"ref": [
|
||||
{
|
||||
"location": "5:1:5:11",
|
||||
"type": "var",
|
||||
"value": "collection"
|
||||
}
|
||||
],
|
||||
"key": {
|
||||
"type": "string",
|
||||
"value": "value",
|
||||
"location": "5:21:5:27"
|
||||
}
|
||||
},
|
||||
"location": "5:1:5:11"
|
||||
}
|
||||
```
|
||||
|
||||
Note that this applies equally to empty `else` bodies, which are represented the same way in the original AST, and
|
||||
omitted entirely in the optimized format.
|
||||
|
||||
Similarly, Roast discards `location` attributes from attributes that don't have an actual location in the source code.
|
||||
An example of this is the `data` term of a package path, which is present only in the AST.
|
||||
|
||||
### Removed `annotations` attribute from module
|
||||
|
||||
OPA already attaches `annotations` to rules. With the Roast format attaching `package` and `subpackages` scoped
|
||||
`annotations` to the `package` as well, there is no need to store `annotations` at the module level, as that's
|
||||
effectively just duplicating data. Having this removed can save a considerable amount of space in well-documented
|
||||
policies, as they should be!
|
||||
|
||||
### Removed `index` attribute from body expressions
|
||||
|
||||
In the original AST, each expression in a body carries a numeric `index` attribute. While this doesn't take much space,
|
||||
it is largely redundant, as the same number can be inferred from the order of the expressions in the body array. It's
|
||||
therefore been removed from the Roast format.
|
||||
|
||||
### Removed `name` attribute from rule heads
|
||||
|
||||
The `name` attribute found in the OPA AST for `rules` is unreliable, as it's not always present. The `ref`
|
||||
attribute however always is. While this doesn't come with any real cost in terms of AST size or performance, consistency
|
||||
is key.
|
||||
|
||||
### Fixed inconsistencies in the original Rego AST
|
||||
|
||||
A few inconsistencies exist in the original AST JSON format:
|
||||
|
||||
- `comments` attributes having a `Text` attribute rather than the expected `text`
|
||||
- `comments` attributes having a `Location` attribute rather than the expected `location`
|
||||
|
||||
Fixing these in the original format would be a breaking change. The Roast format corrects these inconsistencies, and
|
||||
uses `text` and `location` consistently.
|
||||
|
||||
## Performance
|
||||
|
||||
While the numbers may vary some, the Roast format is currently about 40-50% smaller in size than the original AST JSON
|
||||
format, and can be processed (in Rego, using `walk` and so on) about 1.25 times faster.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
|
||||
# Debug Adapter
|
||||
|
||||
In addition to being a [language server](https://openpolicyagent.org/projects/regal/language-server),
|
||||
Regal can act as a
|
||||
[Debug Adapter](https://microsoft.github.io/debug-adapter-protocol/).
|
||||
A Debug Adapter is a program that can communicate with a debugger client,
|
||||
such as Visual Studio Code's debugger, to provide debugging capabilities
|
||||
for a language.
|
||||
|
||||
<img
|
||||
src={require('./assets/dap/animation.gif').default}
|
||||
alt="Animation showing the a debugging session in VS Code"/>
|
||||
_A debugging session in VS Code_
|
||||
|
||||
:::info
|
||||
In order to use the Debug Adapter, you must be using
|
||||
[Regal v0.27.0](https://github.com/open-policy-agent/regal/releases/v0.27.0) or greater,
|
||||
as well as a compatible client. See [Editor Support](https://openpolicyagent.org/projects/regal/editor-support) for
|
||||
more details.
|
||||
:::
|
||||
|
||||
## Getting Started
|
||||
|
||||
See the documentation in the Editor Support page for supported clients:
|
||||
|
||||
* [Visual Studio Code](https://openpolicyagent.org/projects/regal/editor-support#visual-studio-code)
|
||||
* [Neovim](https://openpolicyagent.org/projects/regal/editor-support#neovim)
|
||||
|
||||
## Features
|
||||
|
||||
The Regal Debug Adapter currently supports the following features:
|
||||
|
||||
### Breakpoints
|
||||
|
||||
Breakpoints allow you to continue execution of a policy until a given point.
|
||||
This can be helpful for:
|
||||
|
||||
* Inspection of variables at a given point in time
|
||||
* Seeing how many times a given block of Rego code is executed, if at all
|
||||
* Avoiding the need to step through code as it's run line by line
|
||||
|
||||
<img
|
||||
src={require('./assets/dap/breakpoint.png').default}
|
||||
alt="Screenshot of a breakpoint in VS Code"/>
|
||||
|
||||
### Variable Inspection
|
||||
|
||||
Either at a breakpoint or while stepping through code, you can inspect the
|
||||
local variables in scope as well as the contents of the global `input` and
|
||||
`data` documents.
|
||||
|
||||
`input` will be loaded from `input.json` in the workspace if it exists.
|
||||
|
||||
<img
|
||||
src={require('./assets/dap/variables.png').default}
|
||||
alt="Variables being inspected during execution in VS Code"/>
|
||||
|
||||
### Print Statements
|
||||
|
||||
Print statements are also supported, these are shown in the debug console:
|
||||
|
||||
<img
|
||||
src={require('./assets/dap/print.png').default}
|
||||
alt="Print statements shown in the debug output console"/>
|
||||
@@ -0,0 +1,161 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
|
||||
# Editor Support
|
||||
|
||||
## Visual Studio Code
|
||||
|
||||
[vscode-opa](https://marketplace.visualstudio.com/items?itemName=tsandall.opa) -
|
||||
the official OPA extension for Visual Studio Code - now supports the Regal language server.
|
||||
|
||||
To see Regal linting as you work, install the extension at version `0.13.3` or later
|
||||
and open a workspace with Rego files.
|
||||
|
||||
The plugin will automatically find and use [Regal config](https://openpolicyagent.org/projects/regal#configuration).
|
||||
|
||||
### Debug Adapter Protocol (DAP)
|
||||
|
||||
From
|
||||
[`v0.17.0`](https://github.com/open-policy-agent/vscode-opa/blob/main/CHANGELOG.md#0170)
|
||||
onwards, the OPA extension for Visual Studio Code supports the
|
||||
[Regal Debug Adapter](https://openpolicyagent.org/projects/regal/debug-adapter).
|
||||
|
||||
To start a new debug session use the code action `Debug` found above a Rego rule
|
||||
or package.
|
||||
|
||||
<img
|
||||
src={require('./assets/dap/codeaction.png').default}
|
||||
alt="Code Action in VS Code"/>
|
||||
|
||||
Breakpoints can be added by clicking in the gutter to the left of the editor.
|
||||
Print statements will be shown in the debug console.
|
||||
|
||||
## Zed
|
||||
|
||||
[Zed](https://zed.dev) is a modern open-source code editor with focus on performance and simplicity.
|
||||
|
||||
Zed supports Rego via Regal and the [zed-rego](https://github.com/StyraInc/zed-rego) extension developed by the Styra
|
||||
community. The extension provides syntax highlighting, linting, and most of the other language server features provided
|
||||
by Regal.
|
||||
|
||||
## Neovim
|
||||
|
||||
[Neovim](https://neovim.io/) supports both the Language Server Protocol and the Debug Adapter Protocol.
|
||||
|
||||
Generally, the Regal binary should be [installed](https://openpolicyagent.org/projects/regal#getting-started)
|
||||
first. [`mason.vim`](https://github.com/williamboman/mason.nvim) users can install the
|
||||
Regal binary with `:MasonInstall regal`
|
||||
([package definition](https://github.com/mason-org/mason-registry/blob/2024-07-23-asian-hate/packages/regal/package.yaml)).
|
||||
|
||||
### Language Server Protocol (LSP)
|
||||
|
||||
There are a number of different plugins available for Neovim which integrate
|
||||
with language servers using the Language Server Protocol.
|
||||
|
||||
Below are a number of different plugin options to configure a language server
|
||||
client for Regal in Neovim.
|
||||
|
||||
#### nvim-lspconfig
|
||||
|
||||
[nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) has native support for the
|
||||
Regal language server. Use the configuration below to configure Regal:
|
||||
|
||||
```lua
|
||||
require('lspconfig').regal.setup()
|
||||
```
|
||||
|
||||
#### none-ls
|
||||
|
||||
[none-ls](https://github.com/nvimtools/none-ls.nvim) - Use Neovim as a
|
||||
language server to inject LSP diagnostics, code actions, and more via Lua.
|
||||
|
||||
Minimal installation via [VimPlug](https://github.com/junegunn/vim-plug)
|
||||
|
||||
```vim
|
||||
Plug 'nvim-lua/plenary.nvim'
|
||||
Plug 'nvimtools/none-ls.nvim'
|
||||
|
||||
lua <<EOF
|
||||
local null_ls = require("null-ls")
|
||||
null_ls.setup {
|
||||
sources = { null_ls.builtins.diagnostics.regal }
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Using sample rego file `test.rego` with following content
|
||||
|
||||
```rego
|
||||
package test
|
||||
|
||||
default allowRbac := true
|
||||
```
|
||||
|
||||
Example of the diagnostics in as shown in the UI:
|
||||
|
||||

|
||||
|
||||
#### nvim-cmp
|
||||
|
||||
[nvim-cmp](https://github.com/hrsh7th/nvim-cmp) supports the adding of language
|
||||
servers as a source.
|
||||
|
||||
To use Regal with `nvim-cmp`, it is recommended that you use
|
||||
the [`nvim-lspconfig` source](https://github.com/hrsh7th/cmp-nvim-lsp) and
|
||||
follow the instructions above to configure `nvim-lspconfig`.
|
||||
|
||||
#### Other plugins
|
||||
|
||||
To see live linting of Rego, your plugin must support
|
||||
[`textDocument/diagnostic`](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_diagnostic)
|
||||
messages.
|
||||
|
||||
There are many language server integrations for Neovim, if you'd like to see
|
||||
another one listed, please [open an issue](https://github.com/open-policy-agent/regal/issues/new)
|
||||
or drop us a message in [Slack](https://slack.openpolicyagent.org).
|
||||
|
||||
### Debug Adapter Protocol (DAP)
|
||||
|
||||
#### nvim-dap and nvim-dap-rego
|
||||
|
||||
[nvim-dap](https://github.com/mfussenegger/nvim-dap) is a DAP client
|
||||
implementation for Neovim.
|
||||
This plugin provides basic functions to control debuggers from Neovim.
|
||||
|
||||
[nvim-dap-rego](https://github.com/rinx/nvim-dap-rego) provides basic
|
||||
configurations for Regal's DAP interface.
|
||||
|
||||
To set up Regal's debugger,
|
||||
|
||||
```lua
|
||||
require('dap-rego').setup()
|
||||
```
|
||||
|
||||
Then you can launch debug sessions by calling `:lua require('dap').continue()`.
|
||||
|
||||
## Helix
|
||||
|
||||
The [Helix](https://helix-editor.com/) editor comes with a default config that tries to
|
||||
use `regols` for Rego files.
|
||||
You can make it use Regal instead via this `languages.toml` config:
|
||||
|
||||
```toml
|
||||
[[language]]
|
||||
name = "rego"
|
||||
scope = "source.rego"
|
||||
roots = [".regal/config.yaml"]
|
||||
file-types = ["rego"]
|
||||
indent = { tab-width = 4, unit = "\t" }
|
||||
comment-token = "#"
|
||||
language-servers = [ { name = "regal" } ]
|
||||
|
||||
[language-server.regal]
|
||||
command = "regal"
|
||||
args = ["language-server"]
|
||||
config = { provideFormatter = true }
|
||||
```
|
||||
|
||||
[See the languages docs](https://docs.helix-editor.com/languages.html#language-configuration)
|
||||
for details.
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
|
||||
# Fixing Violations
|
||||
|
||||
For each violation Regal is able to detect, there is a documentation page explaining the issue in detail and how to fix
|
||||
it. For example, here's the one for the [`prefer-some-in-iteration`](https://openpolicyagent.org/projects/regal/rules/style/prefer-some-in-iteration) rule.
|
||||
|
||||
Some rules are **automatically** fixable, meaning that Regal can fix the violation for you. Note that while most fixes
|
||||
will make minor changes to the code, some fixes make more significant modifications. As an example, the
|
||||
[directory-package-mismatch](https://openpolicyagent.org/projects/regal/rules/idiomatic/directory-package-mismatch) fix will
|
||||
automatically move a file to its idiomatic location based on the package path declared in the file.
|
||||
|
||||
:::tip
|
||||
Before you make any automated fixes, make sure to commit or stash any other changes you may have done in your project!
|
||||
That way you can easily revert the changes if anything goes wrong, and this allows you to review the diff before
|
||||
committing the fixes.
|
||||
:::
|
||||
|
||||
Currently, the following rules are automatically fixable:
|
||||
|
||||
- [opa-fmt](https://openpolicyagent.org/projects/regal/rules/style/opa-fmt)
|
||||
- [non-raw-regex-pattern](https://openpolicyagent.org/projects/regal/rules/idiomatic/non-raw-regex-pattern)
|
||||
- [use-assignment-operator](https://openpolicyagent.org/projects/regal/rules/style/use-assignment-operator)
|
||||
- [no-whitespace-comment](https://openpolicyagent.org/projects/regal/rules/style/no-whitespace-comment)
|
||||
- [directory-package-mismatch](https://openpolicyagent.org/projects/regal/rules/idiomatic/directory-package-mismatch)
|
||||
- [use-rego-v1](https://openpolicyagent.org/projects/regal/rules/imports/use-rego-v1) (v0 Rego only)
|
||||
|
||||
So, how do you go on about automatically fixing reported violations?
|
||||
|
||||
## The `regal fix` Command
|
||||
|
||||
The first method is to use `regal fix` from the command line. This command can be seen as the remediating counterpart
|
||||
to `regal lint`, and most configuration options are the same. For example, a linter rule configured to be ignored by
|
||||
the linter will also be ignored by the fixer. Typing `regal fix --help` will show you the available options, as well
|
||||
as all the supported "fixers" for your version of Regal. Lets's see what automatically fixing violations looks like!
|
||||
|
||||
**Example**: fixing all files in the `bundle` directory:
|
||||
|
||||
```shell
|
||||
> regal fix bundle
|
||||
3 fixes applied:
|
||||
In project root: /Users/john/projects/authz/bundle
|
||||
|
||||
lib/roles.rego:
|
||||
- use-rego-v1
|
||||
|
||||
policy.rego -> main/policy.rego:
|
||||
- directory-package-mismatch
|
||||
- no-whitespace-comment
|
||||
```
|
||||
|
||||
In the example above, Regal made fixes corresponding to the linter rules `use-rego-v1`, `directory-package-mismatch`,
|
||||
and `no-whitespace-comment` in `lib/roles.rego` and `policy.rego`. While the number of fixes applied was reported as 3,
|
||||
the number of _violations_ fixed could of course have been higher, as e.g. the `no-whitespace-comment` rule might have
|
||||
been violated in multiple places in `policy.rego`. Note also how one of the fixes (`directory-package-mismatch`)
|
||||
involved **moving** `policy.rego` to `main/policy.rego`, as that rule requires the file to be in a directory structure
|
||||
matching its package path (`package main`).
|
||||
|
||||
### Project Roots
|
||||
|
||||
All paths are relative to its closest **project root**, as reported in the second line of the output. Most policy
|
||||
projects will likely only have one "root", which is the workspace directory itself. More complex projects may however
|
||||
host multiple roots inside the workspace, and defining these roots — either by configuration, or by `.manifest` files —
|
||||
will in some cases (like the previously mentioned `directory-package-mismatch` fix) help Regal better apply the correct
|
||||
fixes. See the documentation on [project roots](https://openpolicyagent.org/projects/regal#project-roots) for more information.
|
||||
|
||||
### Dry Run
|
||||
|
||||
Using the `--dry-run` flag is a great way to see what changes will be made without actually applying them. Following our
|
||||
example from above, adding the `--dry-run` flag to `regal fix bundle` would have told us beforehand what changes we
|
||||
should expect to see. Make it a habit to dry-run your fixes before applying them, and make sure you've commited any
|
||||
other changes before running the fixer!
|
||||
|
||||
## Fixing Violations in Editors
|
||||
|
||||
In addition to the `regal fix` command, users integratiing Regal with their editors can fix violations directly as
|
||||
they are reported in the file being edited. This is done by means of Code Actions, which commonly displays a lightbulb
|
||||
icon next to where a violation occurs. Clicking on the lightbulb will show a list of available actions, which in Regal's
|
||||
case maps directly to the available fix for the violation reported (if any).
|
||||
|
||||
Example of code action in VS Code, where available fixes can be listed either by clicking the lightbulb icon to the
|
||||
right, or by clicking "Quick Fix..." in the tooltip window:
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/code_action_show.png').default}
|
||||
alt="Screenshot of code action displayed in VS Code"/>
|
||||
|
||||
Example of suggested Code Action for the
|
||||
[use-assignment-operator](https://openpolicyagent.org/projects/regal/rules/style/use-assignment-operator) rule. Click to fix!
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/code_action_fix.png').default}
|
||||
alt="Screenshot of code action fix suggestion displayed in VS Code"/>
|
||||
|
||||
### Limitations
|
||||
|
||||
Compared to `regal fix`, automatically fixing violations in editors has some limitations:
|
||||
|
||||
- Normally works on one file at a time, not entire directories
|
||||
- No ability to dry-run a fix, but on the other hand, the editor's **Undo** feature will let you easily revert any
|
||||
changes made.
|
||||
|
||||
:::tip
|
||||
If you're curious about using Regal to fix problems directly in your editor, see the docs on editor support
|
||||
[here](https://openpolicyagent.org/projects/regal/editor-support) to learn more!
|
||||
:::
|
||||
@@ -0,0 +1,285 @@
|
||||
---
|
||||
image: /img/regal.png
|
||||
sidebar_position: 1
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
import Intro from '@site/src/components/projects/regal/Intro';
|
||||
|
||||
<!-- markdownlint-disable MD025 -->
|
||||
|
||||
# Regal
|
||||
|
||||
Regal is a linter and language server for
|
||||
[Rego](https://www.openpolicyagent.org/docs/policy-language/), making
|
||||
your Rego magnificent, and you the ruler of rules!
|
||||
|
||||
With its extensive set of linter rules, documentation and editor integrations,
|
||||
Regal is the perfect companion for policy development, whether you're an
|
||||
experienced Rego developer or just starting out.
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
|
||||
<Intro image={require('./assets/regal-banner.png').default}/>
|
||||
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
## Goals
|
||||
|
||||
- Deliver an outstanding policy development experience by providing the best possible tools for that purpose
|
||||
- Identify common mistakes, bugs and inefficiencies in Rego policies, and suggest better approaches
|
||||
- Provide advice on [best practices](https://github.com/StyraInc/rego-style-guide), coding style, and tooling
|
||||
- Allow users, teams and organizations to enforce custom rules on their policy code
|
||||
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
## What People Say About Regal
|
||||
|
||||
> I really like that at each release of Regal I learn something new!
|
||||
> Of all the linters I'm exposed to, Regal is probably the most instructive one.
|
||||
|
||||
— Leonardo Taccari, [NetBSD](https://www.netbsd.org/)
|
||||
|
||||
> Reviewing the Regal rules documentation. Pure gold.
|
||||
|
||||
— Dima Korolev, [Miro](https://miro.com/)
|
||||
|
||||
> Such an awesome project!
|
||||
|
||||
— Shawn McGuire, [Atlassian](https://www.atlassian.com/)
|
||||
|
||||
> I am really impressed with Regal. It has helped me write more expressive and deterministic Rego.
|
||||
|
||||
— Jimmy Ray, [Boeing](https://www.boeing.com/)
|
||||
|
||||
See the [adopters](https://openpolicyagent.org/projects/regal/adopters) file for more Regal users.
|
||||
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Download Regal
|
||||
|
||||
**MacOS and Linux**
|
||||
|
||||
```shell
|
||||
brew install regal
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><strong>Other Installation Options</strong></summary>
|
||||
|
||||
Please see [Packages](https://openpolicyagent.org/projects/regal/adopters#packaging)
|
||||
for a list of package repositories which distribute Regal.
|
||||
|
||||
Manual installation commands:
|
||||
|
||||
**MacOS (Apple Silicon)**
|
||||
|
||||
```shell
|
||||
curl -L -o regal "https://github.com/open-policy-agent/regal/releases/latest/download/regal_Darwin_arm64"
|
||||
```
|
||||
|
||||
**MacOS (x86_64)**
|
||||
|
||||
```shell
|
||||
curl -L -o regal "https://github.com/open-policy-agent/regal/releases/latest/download/regal_Darwin_x86_64"
|
||||
```
|
||||
|
||||
**Linux (x86_64)**
|
||||
|
||||
```shell
|
||||
curl -L -o regal "https://github.com/open-policy-agent/regal/releases/latest/download/regal_Linux_x86_64"
|
||||
chmod +x regal
|
||||
```
|
||||
|
||||
**Windows**
|
||||
|
||||
```shell
|
||||
curl.exe -L -o regal.exe "https://github.com/open-policy-agent/regal/releases/latest/download/regal_Windows_x86_64.exe"
|
||||
```
|
||||
|
||||
**Docker**
|
||||
|
||||
```shell
|
||||
docker pull ghcr.io/styrainc/regal:latest
|
||||
```
|
||||
|
||||
See all versions, and checksum files, at the Regal [releases](https://github.com/open-policy-agent/regal/releases/)
|
||||
page, and published Docker images at the [packages](https://github.com/open-policy-agent/regal/pkgs/container/regal)
|
||||
page.
|
||||
|
||||
</details>
|
||||
|
||||
### Try it out!
|
||||
|
||||
First, author some Rego!
|
||||
|
||||
**policy/authz.rego**
|
||||
|
||||
```rego
|
||||
package authz
|
||||
|
||||
default allow = false
|
||||
|
||||
allow if {
|
||||
isEmployee
|
||||
"developer" in input.user.roles
|
||||
}
|
||||
|
||||
isEmployee if regex.match("@acmecorp\\.com$", input.user.email)
|
||||
```
|
||||
|
||||
Next, run `regal lint` pointed at one or more files or directories to have them linted.
|
||||
|
||||
```shell
|
||||
regal lint policy/
|
||||
```
|
||||
|
||||
<!-- markdownlint-capture -->
|
||||
<!-- markdownlint-disable MD010 -->
|
||||
|
||||
```text
|
||||
Rule: non-raw-regex-pattern
|
||||
Description: Use raw strings for regex patterns
|
||||
Category: idiomatic
|
||||
Location: policy/authz.rego:12:27
|
||||
Text: isEmployee if regex.match("@acmecorp\\.com$", input.user.email)
|
||||
Documentation: https://openpolicyagent.org/projects/regal/rules/idiomatic/non-raw-regex-pattern
|
||||
|
||||
Rule: use-assignment-operator
|
||||
Description: Prefer := over = for assignment
|
||||
Category: style
|
||||
Location: policy/authz.rego:5:1
|
||||
Text: default allow = false
|
||||
Documentation: https://openpolicyagent.org/projects/regal/rules/style/use-assignment-operator
|
||||
|
||||
Rule: prefer-snake-case
|
||||
Description: Prefer snake_case for names
|
||||
Category: style
|
||||
Location: policy/authz.rego:12:1
|
||||
Text: isEmployee if regex.match("@acmecorp\\.com$", input.user.email)
|
||||
Documentation: https://openpolicyagent.org/projects/regal/rules/style/prefer-snake-case
|
||||
|
||||
1 file linted. 3 violations found.
|
||||
```
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<br />
|
||||
|
||||
> **Note**
|
||||
> If you're running Regal on an existing policy library, you may want to disable the `style` category initially, as it
|
||||
> will likely generate a lot of violations. You can do this by passing the `--disable-category style` flag to
|
||||
> `regal lint`.
|
||||
|
||||
### Using Regal in Your Editor
|
||||
|
||||
Linting from the command line is a great way to get started with Regal, and even for some experienced developers
|
||||
the preferred way to work with the linter. However, not only is Regal a linter, but a full-fledged development
|
||||
companion for Rego development!
|
||||
|
||||
Integrating Regal in your favorite editor means you'll get immediate feedback from the linter as you work on your
|
||||
policies. More than that, it'll unlock a whole new set of features that leverage Regal's
|
||||
[language server](https://openpolicyagent.org/projects/regal/language-server),
|
||||
like context-aware completion suggestions, informative tooltips on hover,
|
||||
or go-to-definition.
|
||||
|
||||
Elevate your policy development experience with Regal in VS Code, Neovim, Zed, Helix
|
||||
and more on our [Editor Support page](https://openpolicyagent.org/projects/regal/editor-support)!
|
||||
|
||||
To learn more about the features provided by the Regal language server, see the
|
||||
[Language Server](https://openpolicyagent.org/projects/regal/language-server) page.
|
||||
|
||||
### Using Regal in Your Build Pipeline
|
||||
|
||||
To ensure Regal's rules are enforced consistently in your project or organization,
|
||||
we've made it easy to run Regal as part of your builds.
|
||||
See the docs on [Using Regal in your build pipeline](https://openpolicyagent.org/projects/regal/cicd) to learn more
|
||||
about how to set up Regal to lint your policies on every commit or pull request.
|
||||
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now you're up and running with Regal, take a look around some of our documentation
|
||||
to get a feel for the different features and capabilities of Regal.
|
||||
|
||||
- [Rules](https://openpolicyagent.org/projects/regal/rules)
|
||||
- [Bugs](https://openpolicyagent.org/projects/regal/rules/bugs): Common mistakes, potential bugs and inefficiencies in Rego policies.
|
||||
- [Idiomatic](https://openpolicyagent.org/projects/regal/rules/idiomatic): Suggestions for more idiomatic constructs.
|
||||
- [Imports](https://openpolicyagent.org/projects/regal/rules/imports): Best practices for imports.
|
||||
- [Performance](https://openpolicyagent.org/projects/regal/rules/performance): Rules for improving performance of policies.
|
||||
- [Style](https://openpolicyagent.org/projects/regal/rules/style): Rego Style Guide rules.
|
||||
- [Testing](https://openpolicyagent.org/projects/regal/rules/testing): Rules for testing and development.
|
||||
- [Custom](https://openpolicyagent.org/projects/regal/rules/custom): Custom rules where enforcement can be adjusted to match your preferences.
|
||||
- [Configuration](https://openpolicyagent.org/projects/regal/configuration): Dig into some of the different configuration options available.
|
||||
- [Editor Support](https://openpolicyagent.org/projects/regal/editor-support): Get Regal integrated into your editor of choice.
|
||||
- [Language Server](https://openpolicyagent.org/projects/regal/language-server): Learn more
|
||||
about Regal's advanced editor capabilities.
|
||||
- [DAP](https://openpolicyagent.org/projects/regal/debug-adapter): Live debug your Rego policies with Regal's DAP support.
|
||||
- [Fixing Issues](https://openpolicyagent.org/projects/regal/fixing): See Regal can help you fix issues in your Rego policies automatically.
|
||||
- [CI/CD](https://openpolicyagent.org/projects/regal/ci-cd): Run Regal as part of your automated checks.
|
||||
- [Custom Rules](https://openpolicyagent.org/projects/regal/custom-rules): Learn how to write your own rules for Regal.
|
||||
- [Adopters](https://openpolicyagent.org/projects/regal/adopters): See who else is using Regal.
|
||||
|
||||
|
||||
<!-- If updating, please check resources.md too -->
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
## Learn More
|
||||
|
||||
[Contributing](https://github.com/open-policy-agent/regal/blob/main/docs/CONTRIBUTING.md)
|
||||
contains information about how to hack on Regal itself.
|
||||
|
||||
### Talks
|
||||
|
||||
- [OPA Maintainer Track, featuring Regal](https://www.youtube.com/watch?v=XtA-NKoJDaI), KubeCon London, 2025
|
||||
- [Regal the Rego Linter](https://www.youtube.com/watch?v=Xx8npd2TQJ0&t=2567s), CNCF London meetup, June 2023
|
||||
[](https://www.youtube.com/watch?v=Xx8npd2TQJ0&t=2567s)
|
||||
|
||||
### Blogs and Articles
|
||||
|
||||
- [Guarding the Guardrails - Introducing Regal the Rego Linter](https://www.styra.com/blog/guarding-the-guardrails-introducing-regal-the-rego-linter/)
|
||||
by Anders Eknert ([@anderseknert](https://github.com/anderseknert))
|
||||
- [Scaling Open Source Community by Getting Closer to Users](https://thenewstack.io/scaling-open-source-community-by-getting-closer-to-users/)
|
||||
by Charlie Egan ([@charlieegan3](https://github.com/charlieegan3))
|
||||
- [Renovating Rego](https://www.styra.com/blog/renovating-rego/) by Anders Eknert ([@anderseknert](https://github.com/anderseknert))
|
||||
- [Linting Rego with... Rego!](https://www.styra.com/blog/linting-rego-with-rego/) by Anders Eknert ([@anderseknert](https://github.com/anderseknert))
|
||||
- [Regal: Rego(OPA)用リンタの導入手順](https://zenn.dev/erueru_tech/articles/6cfb886d92858a) by Jun Fujita ([@erueru-tech](https://github.com/erueru-tech))
|
||||
- [Regal を使って Rego を Lint する](https://tech.dentsusoken.com/entry/2024/12/05/Regal_%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6_Rego_%E3%82%92_Lint_%E3%81%99%E3%82%8B)
|
||||
by Shibata Takao ([@shibata.takao](https://shodo.ink/@shibata.takao/))
|
||||
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
## Status
|
||||
|
||||
Regal is currently in beta. End-users should not expect any drastic changes, but any API may change without notice.
|
||||
If you want to embed Regal in another project or product, please reach out!
|
||||
|
||||
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
|
||||
## Roadmap
|
||||
|
||||
The current Roadmap items are all related to the preparation for
|
||||
[Regal 1.0](https://github.com/open-policy-agent/regal/issues/979):
|
||||
|
||||
- [Go API: Refactor the Location object in Violation (#1554)](https://github.com/open-policy-agent/regal/issues/1554)
|
||||
- [Rego API: Provide a stable and well-documented Rego API (#1555)](https://github.com/open-policy-agent/regal/issues/1555)
|
||||
- [Go API: Audit and reduce the public Go API surface (#1556)](https://github.com/open-policy-agent/regal/issues/1556)
|
||||
- [Custom Rules: Tighten up Authoring experience (#1559)](https://github.com/open-policy-agent/regal/issues/1559)
|
||||
- [docs: Improve automated documentation generation (#1557)](https://github.com/open-policy-agent/regal/issues/1557)
|
||||
- [docs: Break down README into smaller units (#1558)](https://github.com/open-policy-agent/regal/issues/1558)
|
||||
- [lsp: Support a JetBrains LSP client (#1560)](https://github.com/open-policy-agent/regal/issues/1560)
|
||||
|
||||
If there's something you'd like to have added to the roadmap, either open an issue, or reach out in the community Slack!
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
sidebar_label: Go Integration
|
||||
---
|
||||
|
||||
|
||||
# Go Integration
|
||||
|
||||
:warning: Using Regal from Go is currently experimental and subject to change. Please swing by
|
||||
[Slack](https://slack.openpolicyagent.org) if you're keen to use Regal in a production system.
|
||||
|
||||
If you'd like to integrate Regal into a Go application, this guide contains some pointers.
|
||||
|
||||
## Using Regal from Go
|
||||
|
||||
Regal can be used from a Go application by importing the `linter` package:
|
||||
|
||||
```go
|
||||
import "github.com/open-policy-agent/regal/pkg/linter"
|
||||
```
|
||||
|
||||
### Input
|
||||
|
||||
Input to `Lint` can be provided in a number of ways:
|
||||
|
||||
* Using the `InputFromPaths` helper to load Rego files from the filesystem,
|
||||
* Using the `InputFromText` helper to parse a single Rego module from a string,
|
||||
|
||||
#### Using `InputFromPaths`
|
||||
|
||||
```go
|
||||
paths := []string{"foo.rego", "bar.rego"}
|
||||
|
||||
input, err := rules.InputFromPaths(paths)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
```
|
||||
|
||||
#### Using `InputFromText`
|
||||
|
||||
```go
|
||||
|
||||
regoText := `package foo...`
|
||||
|
||||
input, err := rules.InputFromText("policy.rego", regoText)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
To get a Regal report back for the provided input, create a Regal instance and call `Lint`:
|
||||
|
||||
```go
|
||||
regalInstance := linter.NewLinter().WithInputModules(&input)
|
||||
|
||||
lintingReport, err := regalInstance.Lint(r.Context())
|
||||
if err != nil {
|
||||
response.ErrorMessage = err.Error()
|
||||
writeJSON(w, http.StatusOK, response)
|
||||
return
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,236 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
|
||||
# Language Server
|
||||
|
||||
In order to support Rego policy development in editors like
|
||||
[VS Code](https://github.com/open-policy-agent/vscode-opa) or [Zed](https://github.com/StyraInc/zed-rego),
|
||||
Regal provides an implementation of the
|
||||
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP) for Rego.
|
||||
|
||||
This implementation allows the result of linting to be presented directly in your editor as you work on your policies,
|
||||
and without having to call Regal from the command line. The language server however provides much more than just
|
||||
linting!
|
||||
|
||||
:::tip
|
||||
Check support for your editor on the [editor support](./editor-support.md) page.
|
||||
:::
|
||||
|
||||
## Features
|
||||
|
||||
The Regal language server currently supports the following LSP features:
|
||||
|
||||
### Diagnostics
|
||||
|
||||
Diagnostics are errors, warnings, and information messages that are shown in the editor as you type. Regal currently
|
||||
uses diagnostics to present users with either parsing errors in case of syntax issues, and linter violations reported
|
||||
by the Regal linter.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/diagnostics.png').default}
|
||||
alt="Screenshot of diagnostics as displayed in Zed"/>
|
||||
|
||||
Future versions of Regal may include also [compilation errors](https://github.com/open-policy-agent/regal/issues/745)
|
||||
as part of diagnostics messages.
|
||||
|
||||
### Hover
|
||||
|
||||
The hover feature means that moving the mouse over certain parts of the code will bring up a tooltip with documentation
|
||||
for the code under the cursor. This is particularly useful for built-in functions, as it allows you to quickly look up
|
||||
the meaning of the function, and the arguments it expects.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/hover.png').default}
|
||||
alt="Screenshot of hover as displayed in VS Code"/>
|
||||
|
||||
The Regal language server currently supports hover for all built-in functions OPA provides.
|
||||
|
||||
### Go to definition
|
||||
|
||||
Go to definition allows references to rules and functions to be clicked on (while holding `ctrl/cmd`), and the editor
|
||||
will navigate to the definition of the rule or function.
|
||||
|
||||
### Folding ranges
|
||||
|
||||
Regal provides folding ranges for any policy being edited. Folding ranges are areas of the code that can be collapsed
|
||||
or expanded, which may be useful for hiding content that is not relevant to the current task.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/folding.png').default}
|
||||
alt="Screenshot of folding ranges as displayed in Zed"/>
|
||||
|
||||
Regal supports folding ranges for blocks, imports and comments.
|
||||
|
||||
### Document and workspace symbols
|
||||
|
||||
Document and workspace symbols allow policy authors to quickly scan and navigate to symbols (like rules and functions)
|
||||
anywhere in the document or workspace.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/documentsymbols.png').default}
|
||||
alt="Screenshot showing search on workspace symbols in Zed"/>
|
||||
|
||||
VS Code additionally provides an "Outline" view, which is a nice visual representation of the symbols in the document.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/documentsymbols2.png').default}
|
||||
alt="Screenshot showing outline view of document symbols in VS Code"/>
|
||||
|
||||
### Inlay hints
|
||||
|
||||
Inlay hints help developers quickly understand the meaning of the arguments passed passed to functions in the code,
|
||||
by showing the name of the argument next to the value. Inlay hints can additionally be hovered for more information,
|
||||
like the expected type of the argument.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/inlay.png').default}
|
||||
alt="Screenshot showing inlay hints in VS Code"/>
|
||||
|
||||
Regal currently supports inlay hints for all built-in functions. Future versions may support inlay hints for
|
||||
user-defined functions too.
|
||||
|
||||
### Formatting
|
||||
|
||||
By default, Regal uses the `opa fmt` formatter for formatting Rego. This is made available as a command in editors,
|
||||
but also via a [code action](#code-actions) when unformatted files are encountered.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/format.png').default}
|
||||
alt="Screenshot of diagnostics as displayed in Zed"/>
|
||||
|
||||
Two other formatters are also available — `opa fmt --rego-v1` and `regal fix`. See the docs on
|
||||
[Fixing Violations](fixing.md) for more information about the `fix` command. Which formatter to use
|
||||
can be set via the `formatter` configuration option, which can be passed to Regal via the client (see
|
||||
the documentation for your client for how to do that).
|
||||
|
||||
### Code completions
|
||||
|
||||
Code completions, or suggestions, is likely one of the most useful features of the Regal language server. And best of
|
||||
all, you don't need to do anything special for it to happen! Just write your policy as you normally would, and Regal
|
||||
will provide suggestions for anything that could be relevant in the context that you're typing. This could for example
|
||||
be suggestions for:
|
||||
|
||||
- Built-in functions
|
||||
- Local variables
|
||||
- Imported packages
|
||||
- References from anywhere in the workspace
|
||||
- And much more!
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/completion.png').default}
|
||||
alt="Screenshot of completion suggestions as displayed in Zed"/>
|
||||
|
||||
New completion providers are added continuously, so if you have a suggestion for
|
||||
a new completion, please
|
||||
[open an issue](https://github.com/open-policy-agent/regal/issues)!
|
||||
|
||||
### Code actions
|
||||
|
||||
Code actions are actions that appear in the editor when certain conditions are met. One example would be "quick fixes"
|
||||
that may appear when a linter rule has been violated. Code actions can be triggered by clicking on the lightbulb icon
|
||||
that appears on the line with a diagnostic message, or by pressing `ctrl/cmd + .` when the cursor is on the line.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/codeaction.png').default}
|
||||
alt="Screenshot of code action displayed in Zed"/>
|
||||
|
||||
Regal currently provides **quick fix actions** for the following linter rules:
|
||||
|
||||
- [opa-fmt](https://openpolicyagent.org/projects/regal/rules/style/opa-fmt)
|
||||
- [use-rego-v1](https://openpolicyagent.org/projects/regal/rules/imports/use-rego-v1)
|
||||
- [use-assignment-operator](https://openpolicyagent.org/projects/regal/rules/style/use-assignment-operator)
|
||||
- [no-whitespace-comment](https://openpolicyagent.org/projects/regal/rules/style/no-whitespace-comment)
|
||||
- [directory-package-mismatch](https://openpolicyagent.org/projects/regal/rules/idiomatic/directory-package-mismatch)
|
||||
|
||||
Regal also provides **source actions** — actions that apply to a whole file and aren't triggered by linter issues:
|
||||
|
||||
- **Explore compiler stages for policy** — Opens a browser window with an embedded version of the
|
||||
[opa-explorer](https://github.com/srenatus/opa-explorer), where advanced users can explore the different stages
|
||||
of the Rego compiler's output for a given policy.
|
||||
|
||||
### Code lenses (Evaluation)
|
||||
|
||||
The code lens feature provides language servers a way to add actionable commands just next to the code that the action
|
||||
belongs to. Regal provides code lenses for doing **evaluation** of any package or rule directly in the editor. This
|
||||
allows for an extremely fast feedback loop, where you can see the result of writing of modifying rules directly as you
|
||||
work with them, and without having to launch external commands or terminals. In any editor that supports code lenses,
|
||||
simply press `Evaluate` on top of a package or rule declaration to have it evaluated. The result is displayed on the
|
||||
same line.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/evalcodelens.png').default}
|
||||
alt="Screenshot of evaluation performed via code lens"/>
|
||||
|
||||
Once evaluation has completed, the result is also pretty-printed in a tooltip when hovering the rule. This is
|
||||
particularly useful when the result contains more data than can fit on a single line!
|
||||
|
||||
Note that when evaluating incrementally defined rules, the result reflects evaluation of the whole **document**,
|
||||
not a single rule definition. To make this clear, the result will be displayed next to each definition of the
|
||||
same rule.
|
||||
|
||||
In addition to showing the result of evaluation, the "Evaluate" code lens will also display the output of any
|
||||
`print` calls made in rule bodies. This can be really helpful when trying to figure out _why_ the rule evaluated
|
||||
the way it did, or where rule evaluation failed.
|
||||
|
||||
<img
|
||||
src={require('./assets/lsp/evalcodelensprint.png').default}
|
||||
alt="Screenshot of evaluation with print call performed via code lens"/>
|
||||
|
||||
Policy evaluation often depends on **input**. This can be provided via an `input.json` or `input.yaml` file which
|
||||
Regal will search for first in the same directory as the policy file evaluated. If not found there, Regal will proceed
|
||||
to search each parent directory up until the workspace root directory. It is recommended to add `input.json/yaml` to
|
||||
your `.gitignore` file so that you can work freely with evaluation in any directory without having your input
|
||||
accidentally committed.
|
||||
|
||||
#### Editor support
|
||||
|
||||
The Evaluation code lens is supported in any language server client that
|
||||
supports the running of code lenses. The evaluation result is saved to
|
||||
`output.json` in the default case.
|
||||
|
||||
The displaying of evaluation results in the current file or buffer is currently
|
||||
only supported in the
|
||||
[OPA VS Code extension](https://github.com/open-policy-agent/vscode-opa) and
|
||||
for Neovim users in
|
||||
[nvim-dap-rego](https://github.com/rinx/nvim-dap-rego/).
|
||||
|
||||
### Code lenses (Debugging)
|
||||
|
||||
Regal also implements the
|
||||
[Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/).
|
||||
This allows users to trigger debugging sessions for their policies by invoking a
|
||||
code lens on a rule. For more information, see the [Debug Adapter](./debug-adapter.md)
|
||||
page.
|
||||
|
||||
#### Editor support
|
||||
|
||||
While the code lens feature is part of the LSP specification, the action that is triggered by a code lens
|
||||
isn't necessarily part of the standard. The language server protocol does not provide a native method for requesting
|
||||
evaluation, so Regal will handle that on its own, and differently depending on what the client supports.
|
||||
|
||||
- Currently, only the [OPA VS Code extension](https://github.com/open-policy-agent/vscode-opa) and
|
||||
[nvim-dap-rego](https://github.com/rinx/nvim-dap-rego/) is capable of handling
|
||||
the request to display evaluation results on the same line as the package or rule evaluated.
|
||||
- [Neovim](https://neovim.io/) does not support the requests natively, but
|
||||
[nvim-dap-rego](https://github.com/rinx/nvim-dap-rego/) provides handlers to support them.
|
||||
Please follow [the instructions](https://github.com/rinx/nvim-dap-rego/blob/main/README.md#lsp-handlers) in
|
||||
nvim-dap-rego README.
|
||||
- [Zed](https://github.com/StyraInc/zed-rego) does not support the code lens feature at all at this point in time. As
|
||||
soon as it does, Regal will provide them.
|
||||
- Displaying the result of evaluation requires customized code in the client. Currently only VS Code and Neovim
|
||||
has the required modifications to handle this, and is thus the only editor to currently support "inline display"
|
||||
of the result.
|
||||
For other editors that support the code lens feature, Regal will instead write the result of evaluation to an
|
||||
`output.json` file.
|
||||
|
||||
## Unsupported features
|
||||
|
||||
See the
|
||||
[open issues](https://github.com/open-policy-agent/regal/issues?q=is%3Aissue+is%3Aopen+label%3A%22language+server+protocol%22)
|
||||
with the `language server protocol` label for a list of features that are not yet supported by the Regal language
|
||||
server, but that are planned for the future. If you have suggestions for anything else, please create a new issue!
|
||||
|
||||
Also note that not all clients (i.e. editors) may support all features of a language server! See the
|
||||
[editor support](./editor-support.md) page for information about Regal support in different editors.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
sidebar_label: OPA 1.0
|
||||
sidebar_position: 14
|
||||
---
|
||||
|
||||
|
||||
# OPA 1.0 and Regal
|
||||
|
||||
While we always recommend using the latest version of OPA, we're well aware that there may be situations where — for
|
||||
one reason or another — that might not be possible. As we want everyone to benefit from Regal, we do our very best to
|
||||
ensure it works seamlessly with OPA versions both before and after 1.0, and even projects that use a mix of both! While
|
||||
this should mostly work out of the box and without additional configuration, it's good to be aware of how Regal parses
|
||||
and lints policies of different versions of Rego, and how you can tell Regal to target only a specific version.
|
||||
|
||||
**Note:** This document does not cover the specifics of OPA 1.0, but rather how Regal works with it. If you want to
|
||||
learn more about what OPA 1.0 is and how to upgrade, see the [related resources](#related-resources) at the bottom of
|
||||
this page.
|
||||
|
||||
## Telling Regal which Rego version to target
|
||||
|
||||
While Regal pretty accurately guesses the Rego version of the policies it's linting — and will adapt how it parses and
|
||||
asseses Rego files accordingly — telling Regal which version to target is always going to produce the most reliable
|
||||
results — and much faster too! Guessing which Rego version to target often involves multiple passes of parsing, and
|
||||
as some files are both valid Rego v0 and v1, there will always be some ambiguity. In order to avoid this, our
|
||||
recommendation is to always provide Regal with the Rego version(s) targeted. This can be done in a couple of ways, and
|
||||
the precedence of these methods is as listed below:
|
||||
|
||||
1. Setting the `rego-version` configuration option under `project.roots` attribute
|
||||
2. Setting the `rego-version` configuration option under `project` attribute
|
||||
3. Setting the `rego_version` in a `.manifest` file in any directory (will apply to that directory and any below it)
|
||||
|
||||
Note that it's is perfectly possible to use different `rego-version`s for different roots of a project:
|
||||
|
||||
```yaml
|
||||
project:
|
||||
rego-version: 1
|
||||
roots:
|
||||
# lib/legacy overriding project version to set versin 0
|
||||
- path: lib/legacy
|
||||
rego-version: 0
|
||||
# main directory will inherit version 1 from project
|
||||
- path: main
|
||||
```
|
||||
|
||||
See the documentation covering Regal's [configuration](https://openpolicyagent.org/projects/regal#configuration) for more information
|
||||
on [configuring Rego version](https://openpolicyagent.org/projects/regal#configuring-rego-version) for your project.
|
||||
|
||||
Finally, Regal will automatically parse and lint any file with a `_v0.rego` suffix as Rego v0. This is intended only
|
||||
for testing and development, where you sometimes may want to try something out using and older Rego version without
|
||||
configuration. Note that this has lower precedence than Rego versions set by other means, and should not be considered
|
||||
as anything but a convenience for testing.
|
||||
|
||||
## Rules disabled with OPA 1.0
|
||||
|
||||
Some linter rules don't really make sense to enforce post OPA 1.0, as they are now either enforced by OPA itself or
|
||||
otherwise no longer relevant. The following rules are now disabled by default, unless Regal is configured to target
|
||||
Rego versions before 1.0, or in the case where no configuration is provided, Regal determines that the project being
|
||||
linted is not yet using OPA 1.0:
|
||||
|
||||
- [deprecated-builtin](https://openpolicyagent.org/projects/regal/rules/bugs/deprecated-builtin)
|
||||
- [import-shadows-import](https://openpolicyagent.org/projects/regal/rules/imports/import-shadows-import)
|
||||
- [rule-named-if](https://openpolicyagent.org/projects/regal/rules/bugs/rule-named-if)
|
||||
- [use-contains](https://openpolicyagent.org/projects/regal/rules/idiomatic/use-contains)
|
||||
- [use-if](https://openpolicyagent.org/projects/regal/rules/idiomatic/use-if)
|
||||
- [use-rego-v1](https://openpolicyagent.org/projects/regal/rules/imports/use-rego-v1)
|
||||
|
||||
Except for the `deprecated-bultin` rule — which is disabled simply because there currently are no deprecated built-ins
|
||||
in OPA 1.0 — these rules are now enforced automatically by OPA, and so there's no reason for Regal to duplicate that
|
||||
effort.
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Upgrading to v1.0](https://www.openpolicyagent.org/docs/v0-upgrade/)
|
||||
- OPA Docs: [v0 Backwards Compatibility](https://www.openpolicyagent.org/docs/v0-compatibility/)
|
||||
- Styra Blog: [Renovating Rego](https://www.styra.com/blog/renovating-rego/)
|
||||
- OPA Blog: [OPA 1.0 Is Coming, Here's What You Need to Know](https://blog.openpolicyagent.org/opa-1-0-is-coming-heres-what-you-need-to-know-c8fb0d258368)
|
||||
- OPA Blog: [Announcing OPA 1.0: A New Standard for Policy as Code](https://blog.openpolicyagent.org/announcing-opa-1-0-a-new-standard-for-policy-as-code-a6d8427ee828)
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
|
||||
# Pre-Commit Hooks
|
||||
|
||||
[Pre-Commit](https://pre-commit.com) is a framework for managing and maintaining multi-language pre-commit hooks.
|
||||
This allows running Regal automatically whenever (and as the name implied, before )a Rego file is about to be committed.
|
||||
|
||||
To use Regal with pre-commit, add this to your `.pre-commit-config.yaml`
|
||||
|
||||
```yaml
|
||||
- repo: https://github.com/open-policy-agent/regal
|
||||
rev: v0.7.0 # Use the ref you want to point at
|
||||
hooks:
|
||||
- id: regal-lint
|
||||
# - id: ...
|
||||
```
|
||||
|
||||
## Hooks Available
|
||||
|
||||
### `regal-lint`
|
||||
|
||||

|
||||
|
||||
Runs Regal against all staged `.rego` files, aborting the commit if any fail.
|
||||
|
||||
- requires the `go` build chain is installed and available on `$PATH`
|
||||
- will build and install the tagged version of Regal in an isolated `GOPATH`
|
||||
- ensures compatibility between versions
|
||||
|
||||
### `regal-lint-use-path`
|
||||
|
||||

|
||||
|
||||
Runs Regal against all staged `.rego` files, aborting the commit if any fail.
|
||||
|
||||
- requires the `regal` package is already installed and available on `$PATH`.
|
||||
|
||||
### `regal-download`
|
||||
|
||||

|
||||
|
||||
Runs Regal against all staged `.rego` files, aborting the commit if any fail.
|
||||
|
||||
- Downloads the latest `regal` binary from Github.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
---
|
||||
|
||||
|
||||
# Remote Features
|
||||
|
||||
This page outlines the features of Regal that need internet access to function.
|
||||
|
||||
## Checking for Updates
|
||||
|
||||
Regal will check for updates on startup. If a new version is available,
|
||||
Regal will notify you by writing a message in stderr.
|
||||
|
||||
An example of such a message is:
|
||||
|
||||
```txt
|
||||
A new version of Regal is available (v0.23.1). You are running v0.23.0.
|
||||
See https://github.com/open-policy-agent/regal/releases/tag/v0.23.1 for the latest release.
|
||||
```
|
||||
|
||||
This message is based on the local version set in the Regal binary, and **no
|
||||
user data is sent** to GitHub where the releases are hosted.
|
||||
|
||||
This same function will also write to the file at: `$HOME/.config/regal/latest_version.json`,
|
||||
this is used as a cache of the latest version to avoid consuming excessive
|
||||
GitHub API rate limits when using Regal.
|
||||
|
||||
This functionality can be disabled in two ways:
|
||||
|
||||
* Using `.regal/config.yaml` / `.regal.yaml`: set `features.remote.check-version` to `false`.
|
||||
* Using an environment variable: set `REGAL_DISABLE_CHECK_VERSION` to `true`.
|
||||
@@ -0,0 +1 @@
|
||||
{ "collapsed": false }
|
||||
@@ -0,0 +1,49 @@
|
||||
# annotation-without-metadata
|
||||
|
||||
**Summary**: Annotation without metadata
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# description: allow allows
|
||||
allow if {
|
||||
# ... some conditions
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# METADATA
|
||||
# description: allow allows
|
||||
allow if {
|
||||
# ... some conditions
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
A comment that starts with `<annotation-attribute>:` but is not part of a metadata block is likely a mistake. Add
|
||||
`# METADATA` above the line to turn it into a
|
||||
[metadata](https://www.openpolicyagent.org/docs/policy-language/#annotations) block.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
annotation-without-metadata:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Annotations](https://www.openpolicyagent.org/docs/policy-language/#annotations)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/annotation-without-metadata/annotation_without_metadata.rego)
|
||||
@@ -0,0 +1,88 @@
|
||||
# argument-always-wildcard
|
||||
|
||||
**Summary**: Argument is always a wildcard
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# there's only one definition of the last_name function in
|
||||
# this package, and the second argument is never used
|
||||
last_name(name, _) := lname if {
|
||||
parts := split(name, " ")
|
||||
lname := parts[count(parts) - 1]
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
last_name(name) := lname if {
|
||||
parts := split(name, " ")
|
||||
lname := parts[count(parts) - 1]
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Function definitions may use wildcard variables as arguments to indicate that the value is not used in the body of
|
||||
the function. This helps make the function definition more readable, as it's immediately clear which of the arguments
|
||||
are used in that definition of the function. This is particularly useful for incrementally defined functions:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default authorized(_, _) := false
|
||||
|
||||
authorized(user, _) if {
|
||||
# some logic to determine if authorized
|
||||
}
|
||||
|
||||
# or
|
||||
|
||||
authorized(user, _) if {
|
||||
# some further logic to determine if authorized
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, the second argument is a wildcard in all definitions, and could just as well be removed for a
|
||||
cleaner definition. More likely, the argument was meant to be _used_, if only in one of the definitions:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default authorized(_, _) := false
|
||||
|
||||
authorized(user, _) if {
|
||||
# some logic to determine if authorized
|
||||
}
|
||||
|
||||
# or
|
||||
|
||||
authorized(_, request) if {
|
||||
# some further logic to determine if authorized
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
argument-always-wildcard:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
# function name patterns for which this rule should make an exception
|
||||
# default is to ignore any function name starting with "mock_" as these
|
||||
# commonly don't need named arguments
|
||||
except-function-name-pattern: "^mock_"
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/argument-always-wildcard/argument_always_wildcard.rego)
|
||||
@@ -0,0 +1,42 @@
|
||||
# constant-condition
|
||||
|
||||
**Summary**: Constant condition
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
1 == 1
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow := true
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
While most often a mistake, constant conditions are sometimes used as placeholders, or "TODO logic". While this is
|
||||
harmless, it has no place in production policy, and should be replaced or removed before deployment.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
constant-condition:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/constant-condition/constant_condition.rego)
|
||||
@@ -0,0 +1,141 @@
|
||||
# deprecated-builtin
|
||||
|
||||
**Summary**: Constant condition
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
## Notice: Rule disabled with OPA 1.0
|
||||
|
||||
Since Regal v0.30.0, this rule is only enabled for projects that have either been explicitly configured to target
|
||||
versions of OPA before 1.0, or if no configuration is provided — where Regal is able to determine that an older version
|
||||
of OPA/Rego is being targeted. Consult the documentation on Regal's
|
||||
[configuration](https://openpolicyagent.org/projects/regal#configuration) for information on how to best work with older versions of
|
||||
OPA and Rego.
|
||||
|
||||
Since OPA v1.0, this rule is automatically disabled, as there currently are no deprecated built-in functions
|
||||
in that version, and trying to use a previously deprecated function will result in a parser error. Note however that
|
||||
this may change if later OPA versions deprecate current built-in functions. If/when that happens, this rule will be
|
||||
re-enabled.
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
import future.keywords.if
|
||||
|
||||
# call to deprecated `any` built-in function
|
||||
allow if any([input.user.is_admin, input.user.is_root])
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
import future.keywords.if
|
||||
|
||||
allow if input.user.is_admin
|
||||
allow if input.user.is_root
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Calling deprecated built-in functions should always be avoided, and replacing them is usually trivial.
|
||||
|
||||
## Replacing Deprecated Built-in Functions
|
||||
|
||||
### `any`
|
||||
|
||||
Use the `in` keyword (OPA v0.34.0+) to replace the `any` function:
|
||||
|
||||
**Instead of**
|
||||
```rego
|
||||
a := any([input.foo, input.bar])
|
||||
```
|
||||
|
||||
**Do this**
|
||||
```rego
|
||||
import future.keywords.in # or `import rego.v1` (OPA v0.59.0+)
|
||||
|
||||
a := true in [input.foo, input.bar]
|
||||
```
|
||||
|
||||
Using `in` additionally has the benefit that it can be used to check for any type of value, and not just boolean
|
||||
`true`!
|
||||
|
||||
### `all`
|
||||
|
||||
Use the `every` keyword (OPA v0.34.0+) to replace the `all` function:
|
||||
|
||||
**Instead of**
|
||||
```rego
|
||||
a {
|
||||
all([input.foo, input.bar])
|
||||
}
|
||||
```
|
||||
|
||||
**Do this**
|
||||
```rego
|
||||
import future.keywords.every # or `import rego.v1` (OPA v0.59.0+)
|
||||
|
||||
a {
|
||||
every x in [input.foo, input.bar] {
|
||||
x == true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Just like `in` may be used for much more than `any`, `every` can be used to evaluate complex expressions!
|
||||
|
||||
### `set_diff`
|
||||
|
||||
Use the minus (`-`) operator instead, of `set_diff`:
|
||||
|
||||
**Instead of**
|
||||
```rego
|
||||
a := set_diff(s1, s2)
|
||||
```
|
||||
|
||||
**Do this**
|
||||
```rego
|
||||
a := s1 - s2
|
||||
```
|
||||
|
||||
### `re_match` and `net.cidr_overlap`
|
||||
|
||||
These built-in function were renamed `regex.match` and `net.cidr_intersects` respectively, so simply use the new names
|
||||
instead.
|
||||
|
||||
### `cast_array`, `cast_set`, `cast_string`, `cast_boolean`, `cast_null`, `cast_object`
|
||||
|
||||
Use the `is_X` equivalent built-in function in their place:
|
||||
|
||||
**Instead of**
|
||||
```rego
|
||||
a {
|
||||
cast_string(input.name)
|
||||
}
|
||||
```
|
||||
|
||||
**Do this**
|
||||
```rego
|
||||
a {
|
||||
is_string(input.name)
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
deprecated-builtin:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Strict Mode](https://www.openpolicyagent.org/docs/policy-language/#strict-mode)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/deprecated-builtin/deprecated_builtin.rego)
|
||||
@@ -0,0 +1,55 @@
|
||||
# duplicate-rule
|
||||
|
||||
**Summary**: Duplicate rule
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if user.is_admin
|
||||
|
||||
allow if user.is_developer
|
||||
|
||||
# we already covered this!
|
||||
allow if user.is_admin
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if user.is_admin
|
||||
|
||||
allow if user.is_developer
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Duplicated rules are likely a mistake, perhaps from pasting contents from another file.
|
||||
|
||||
This rule identifies rules that are _identical_ in terms of their name, assigned value, and body — excluding
|
||||
whitespace. In technical terms, if two or more rules share the same abstract syntax tree, they are considered
|
||||
to be duplicates.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Note that this rule currently works at the scope of a single file. If you're using the same package across multiple
|
||||
files, there could still be duplicates across those files. This will be addressed in a future version of this rule.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
duplicate-rule:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/duplicate-rule/duplicate_rule.rego)
|
||||
@@ -0,0 +1,40 @@
|
||||
# if-empty-object
|
||||
|
||||
**This rule has been deprecated and replaced by the
|
||||
[if-object-literal](https://openpolicyagent.org/projects/regal/rules/bugs/if-object-literal) rule. Documentation kept here only for
|
||||
the sake of posterity.**
|
||||
|
||||
**Summary**: Empty object following `if`
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
An empty rule body would previously be considered an error by OPA. With the introduction, and use of the `if` keyword,
|
||||
that is no longer the case. In fact, empty `{}` is not considered a rule body _at all_, but rather an empty object,
|
||||
meaning that `if {}` will always evaluate. This is likely a mistake, and while hopefully caught by tests, should be
|
||||
avoided.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
if-empty-object:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Regal Docs: [constant-condition](https://openpolicyagent.org/projects/regal/rules/bugs/constant-condition)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/if-empty-object/if_empty_object.rego)
|
||||
@@ -0,0 +1,41 @@
|
||||
# if-object-literal
|
||||
|
||||
**Summary**: Object literal following `if`
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# {} interpreted as object, not a rule body
|
||||
allow if {}
|
||||
|
||||
allow if {
|
||||
# perhaps meant to be comparison?
|
||||
# but this too is an object
|
||||
input.x: 10
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
An object literal immediately following an `if` is almost certainly a mistake, and the intention was likely to express
|
||||
a rule body in its place. This isn't too common, but can happen when either an empty object `{}` is all that follows the
|
||||
`if`, or an expression in the "body" mistakenly is written as a `key: value` pair.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
if-object-literal:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/if-object-literal/if_object_literal.rego)
|
||||
@@ -0,0 +1,58 @@
|
||||
# import-shadows-rule
|
||||
|
||||
**Summary**: Import shadows rule
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
import data.resources
|
||||
|
||||
# 'resources' shadowed by import
|
||||
resources contains resource if {
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
import data.resources
|
||||
|
||||
# using a different name for the rule
|
||||
report contains resource if {
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# using an alias to avoid shadowing 'resources' rule
|
||||
import data.resources as inventory
|
||||
|
||||
resources contains resource if {
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Imported identifers like `bar` in `import data.foo.bar` has higher precedence than a rule named `bar` in the same
|
||||
package. This means that any rule that is shadowed by an import is effectively unreachable inside of the module.
|
||||
Avoid shadowing either by renaming your rule or by using an alias for the imported identifier.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
import-shadows-rule:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
# impossible-not
|
||||
|
||||
**Summary**: Impossible `not` condition
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Type**: Aggregate - runs both on single files as well as when more than one file is provided for linting
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
report contains violation if {
|
||||
# ... some conditions
|
||||
}
|
||||
```
|
||||
|
||||
```rego
|
||||
package policy_test
|
||||
|
||||
import data.policy
|
||||
|
||||
test_report_is_empty {
|
||||
# evaluation will stop here, as even an empty set is "true"
|
||||
not policy.report
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
report contains violation if {
|
||||
# ... some conditions
|
||||
}
|
||||
```
|
||||
|
||||
```rego
|
||||
package policy_test
|
||||
|
||||
import data.policy
|
||||
|
||||
test_report_is_empty {
|
||||
count(policy.report) == 0
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
The `not` keyword negates the expression that follows it. A common mistake, especially in tests, is to use `not`
|
||||
to test the result of evaluating a partial (i.e. multi-value) rule. However, as even an empty set is considered
|
||||
"truthy", the `not` will in that case always evaluate to `false`. There are more cases where `not` is impossible,
|
||||
or a [constant condition](https://openpolicyagent.org/projects/regal/rules/bugs/constant-condition), but references to partial
|
||||
rules are by far the most common. For tests where you want to assert the set is empty or has a specific number of
|
||||
items, use the built-in `count` function instead.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
impossible-not:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Regal Docs: [constant-condition](https://openpolicyagent.org/projects/regal/rules/bugs/constant-condition)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/impossible-not/impossible_not.rego)
|
||||
@@ -0,0 +1,72 @@
|
||||
# inconsistent-args
|
||||
|
||||
**Summary**: Inconsistently named function arguments
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
find_vars(rule, node) if node in rule
|
||||
|
||||
# Order of arguments changed, or at least it looks like it
|
||||
find_vars(node, rule) if {
|
||||
walk(rule, [path, value])
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
find_vars(rule, node) if node in rule
|
||||
|
||||
find_vars(rule, node) if {
|
||||
walk(rule, [path, value])
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Whenever a custom function declaration is repeated, the argument names should remain consistent in each declaration.
|
||||
|
||||
Inconsistently named function arguments is a likely source of bugs, and should be avoided.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Using wildcards (`_`) in place of unused arguments is always allowed, and in fact enforced by the compiler:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
find_vars(rule, node) if node in rule
|
||||
|
||||
# We don't use `node` here
|
||||
find_vars(rule, _) if {
|
||||
walk(rule, [path, value])
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
Using [pattern matching for equality](https://openpolicyagent.org/projects/regal/rules/idiomatic/equals-pattern-matching) checks is
|
||||
also allowed.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
inconsistent-args:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Regal Docs: [equals-pattern-matching](https://openpolicyagent.org/projects/regal/rules/idiomatic/equals-pattern-matching)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/inconsistent-args/inconsistent_args.rego)
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Bugs
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
|
||||
# Bugs
|
||||
|
||||
Rules that detect bugs in your code.
|
||||
|
||||
import RulesTable from '@site/src/components/projects/regal/RulesTable';
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<RulesTable category="bugs"/>
|
||||
@@ -0,0 +1,2 @@
|
||||
title: Bugs
|
||||
sidebar_position: 1
|
||||
@@ -0,0 +1,53 @@
|
||||
# internal-entrypoint
|
||||
|
||||
**Summary**: Entrypoint can't be marked internal
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# METADATA
|
||||
# entrypoint: true
|
||||
_authorized if {
|
||||
# some conditions
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# METADATA
|
||||
# entrypoint: true
|
||||
allow if _authorized
|
||||
|
||||
_authorized if {
|
||||
# some conditions
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Rules marked as internal using the [underscore prefix convention](https://github.com/StyraInc/rego-style-guide#optionally-use-leading-underscore-for-rules-intended-for-internal-use)
|
||||
cannot be used as entrypoints, as entrypoints by definition are public. Either rename the rule to mark it as public,
|
||||
or use another public rule as an entrypoint, which may reference the internal rule.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
internal-entrypoint:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Rego Style Guide: [Optionally, use leading underscore for rules intended for internal use](https://github.com/StyraInc/rego-style-guide#optionally-use-leading-underscore-for-rules-intended-for-internal-use)
|
||||
- Regal Docs: [no-defined-entrypoint](https://openpolicyagent.org/projects/regal/rules/idiomatic/no-defined-entrypoint)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/internal-entrypoint/internal_entrypoint.rego)
|
||||
@@ -0,0 +1,53 @@
|
||||
# invalid-metadata-attribute
|
||||
|
||||
**Summary**: Invalid attribute in metadata annotation
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
# METADATA
|
||||
# title: Main policy routing requests to other policies based on input
|
||||
# category: Routing
|
||||
package router
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
# METADATA
|
||||
# title: Main policy routing requests to other policies based on input
|
||||
# custom:
|
||||
# category: Routing
|
||||
package router
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Metadata comments should follow the schema expected by
|
||||
[annotations](https://www.openpolicyagent.org/docs/policy-language/#annotations). Custom attributes, like
|
||||
`category` above, should be placed under the `custom` key, which is a map of arbitrary key-value pairs.
|
||||
|
||||
While arbitrary attributes are accepted, they will not be treated as metadata annotations but regular comments, and as
|
||||
such won't be available to other tools that
|
||||
[process annotations](https://www.openpolicyagent.org/docs/policy-language/#accessing-annotations).
|
||||
These tools include built-in functions like
|
||||
[rego.metadata.rule](https://www.openpolicyagent.org/docs/policy-reference/#builtin-rego-regometadatarule) and
|
||||
[rego.metadata.chain](https://www.openpolicyagent.org/docs/policy-reference/#builtin-rego-regometadatachain).
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
invalid-metadata-attribute:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Annotations](https://www.openpolicyagent.org/docs/policy-language/#annotations)
|
||||
- OPA Docs: [Accessing Annotations](https://www.openpolicyagent.org/docs/policy-language/#accessing-annotations)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/invalid-metadata-attribute/invalid_metadata_attribute.rego)
|
||||
@@ -0,0 +1,72 @@
|
||||
# leaked-internal-reference
|
||||
|
||||
**Summary**: Outside reference to internal rule or function
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# Import of rule or functions marked as internal
|
||||
import data.users._all_users
|
||||
|
||||
allow if {
|
||||
# reference to rule or function marked as internal
|
||||
some role in data.permissions._roles
|
||||
# ...some conditions
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
OPA doesn't have a concept of "internal", or private rules and functions — and all rules can be queried or referenced
|
||||
from the outside. Despite this fact, it has become a common convention to use an underscore prefix in the name of
|
||||
rules and functions to indicate that they should be considered internal to the package that they're in:
|
||||
|
||||
```rego
|
||||
# `allow` may be referenced from outside the package
|
||||
allow if _user_is_developer
|
||||
|
||||
# `_user_is_developer` should not be referenced from outside the package
|
||||
_user_is_developer if "developer" in input.users.roles
|
||||
```
|
||||
|
||||
While this might seem like a pointless convention if it isn't enforced by OPA, it comes with a number of benefits:
|
||||
|
||||
- While OPA doesn't enforce it, other tools like linters can help with that. Like this rule does!
|
||||
- It clearly communicates intent to other policy authors, and as a simple form of documentation
|
||||
- Completion suggestions in editors can be filtered to exclude internal rules and functions
|
||||
- Tools that render documentation from Rego policies and metadata annotations can exclude internal rules and functions
|
||||
- Checking for unused rules and functions can be done much faster if they're known not to be referenced from outside
|
||||
|
||||
Do note that if you disagree with this rule, you don't need to disable it unless you use underscore prefixes to mean
|
||||
something else. If you don't use underscore prefixes, nothing will be reported by this rule anyway. It does however
|
||||
mean that the benefits listed above won't apply to your project.
|
||||
|
||||
## Exceptions
|
||||
|
||||
This rule is not enabled by default for test files. In tests, it can be useful
|
||||
to reference internal rules and functions to achieve good test coverage, which
|
||||
would be a violation of this rule. If you want to run this rule for tests
|
||||
too, you can set `include-test-files: true` in the configuration for this rule
|
||||
in your Regal config file.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
leaked-internal-reference:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
include-test-files: false # default is false
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Rego Style Guide: [Optionally, use leading underscore for rules intended for internal use](https://github.com/StyraInc/rego-style-guide#optionally-use-leading-underscore-for-rules-intended-for-internal-use)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/leaked-internal-reference/leaked_internal_reference.rego)
|
||||
@@ -0,0 +1,66 @@
|
||||
# not-equals-in-loop
|
||||
|
||||
**Summary**: Use of != in loop
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
deny if {
|
||||
"admin" != input.user.roles[_]
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
deny if {
|
||||
not "admin" in input.user.roles
|
||||
}
|
||||
|
||||
# Or as a one-liner
|
||||
deny if not "admin" in input.user.roles
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Likely one of the most common mistakes in Rego is to use `!=` in a loop thinking it means "not in". It took some years
|
||||
for the `in` keyword to be added to Rego, so perhaps it's not surprising that this mistake is a common one even to this
|
||||
day. If it doesn't mean "not in", what does it mean?
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
deny if {
|
||||
"admin" != input.user.roles[_]
|
||||
}
|
||||
```
|
||||
|
||||
The body of the `deny` rule above roughly translates to "for any item in `input.user.roles`, return true if the item is
|
||||
not `admin`". This is almost never what the policy author intended. What the policy author likely intended was
|
||||
"deny if `admin` is not in `input.user.roles`". The above policy would thus **not** deny a user with the roles
|
||||
`["user", "admin"]` since the first item in the array is not "admin". This is almost never what the policy author
|
||||
intended.
|
||||
|
||||
**Note**: This linter rule currently only checks for `!=` in a non-nested comparison where iteration happens on either
|
||||
side of the comparison in the same expression. This will be improved in time. Another limitation is that this rule
|
||||
currently only checks for wildcard iteration (`[_]`).
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
not-equals-in-loop:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/not-equals-in-loop/not_equals_in_loop.rego)
|
||||
@@ -0,0 +1,87 @@
|
||||
# redundant-existence-check
|
||||
|
||||
**Summary**: Redundant existence check
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
employee if {
|
||||
input.user.email
|
||||
endswith(input.user.email, "@acmecorp.com")
|
||||
}
|
||||
|
||||
is_admin(user) if {
|
||||
user
|
||||
"admin" in user.roles
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
employee if {
|
||||
endswith(input.user.email, "@acmecorp.com")
|
||||
}
|
||||
|
||||
# alternatively
|
||||
|
||||
employee if endswith(input.user.email, "@acmecorp.com")
|
||||
|
||||
is_admin(user) if {
|
||||
"admin" in user.roles
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Checking that a reference (like `input.user.email`) is defined before immediately using it is redundant. If the
|
||||
reference is undefined, the next expression will fail anyway, as the value will be checked before the rest of the
|
||||
expression is evaluated. While an extra check doesn't "hurt", it also serves no purpose, similarly to an unused
|
||||
variable.
|
||||
|
||||
**Note**: This rule only applies to references that are immediately used in the next expression. If the reference is
|
||||
used later in the rule, it won't be flagged. While the existence check _could_ be redundant even in that case, it could
|
||||
also be used to avoid making some expensive computation, an `http.send` call, or whatnot.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Function arguments where a boolean value is expected will be flagged as redundant existence checks, even though the
|
||||
intent was to check the boolean condition.
|
||||
|
||||
```rego
|
||||
report(user, is_admin) if {
|
||||
is_admin
|
||||
|
||||
# more conditions
|
||||
}
|
||||
```
|
||||
|
||||
For these cases, prefer to be explicit about what the assertion is checking:
|
||||
|
||||
```rego
|
||||
report(user, is_admin) if {
|
||||
is_admin == false # or true, != false, etc.
|
||||
|
||||
# more conditions
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
redundant-existence-check:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/redundant-existence-check/redundant_existence_check.rego)
|
||||
@@ -0,0 +1,82 @@
|
||||
# redundant-loop-count
|
||||
|
||||
**Summary**: Redundant count before loop
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
# redundant count and > comparison
|
||||
count(input.user.roles) > 0
|
||||
some role in input.user.roles
|
||||
# .. do more with role ..
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
some role in input.user.roles
|
||||
# .. do more with role ..
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
A loop that iterates over an empty collection evaluates to nothing, and counting the collection before the loop to
|
||||
ensure it's not empty is therefore redundant.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Note that this check is currently only performed on `some` loops, and not "ref-style" loops:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
# this won't be flagged
|
||||
count(input.user.roles) > 0
|
||||
role := input.user.roles[_]
|
||||
# .. do more with role ..
|
||||
}
|
||||
```
|
||||
|
||||
Another good reason to
|
||||
[prefer some .. in for iteration](https://openpolicyagent.org/projects/regal/rules/style/prefer-some-in-iteration)!
|
||||
|
||||
### `every` iteration
|
||||
|
||||
Counting to ensure a non-empty collection is used before `every` loops may **not** be redundant, as `every` evaluates
|
||||
to `true` when an empty collection is passed.
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
# every would otherwise be `true` on empty input.user.roles
|
||||
# so this may be valid, depending on the outcome you expect
|
||||
count(input.user.roles) > 0
|
||||
every role in input.user.roles {
|
||||
# .. do more with each role ..
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to have empty collections fail on `every` conditions, do make sure to use `count`!
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
redundant-loop-count:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
@@ -0,0 +1,51 @@
|
||||
# rule-assigns-default
|
||||
|
||||
**Summary**: Rule assigned its default value
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default allow := false
|
||||
|
||||
# this rule assigns the same value as the default
|
||||
# and the policy would work the same without it
|
||||
allow := false if {
|
||||
not "admin" in input.user.roles
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default allow := false
|
||||
|
||||
# or just `allow if {` as `true` is implicit
|
||||
allow := true if {
|
||||
"admin" in input.user.roles
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
When a default value is used for a rule, assigning the same value anywhere else to that rule is pointless, as the rule
|
||||
would evaluate to the same value with or without the assignment.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
rule-assigns-default:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/rule-assigns-default/rule_assigns_default.rego)
|
||||
@@ -0,0 +1,71 @@
|
||||
# rule-named-if
|
||||
|
||||
**Summary**: Rule named `if`
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
## Notice: Rule made obsolete by OPA 1.0
|
||||
|
||||
Since Regal v0.30.0, this rule is only enabled for projects that have either been explicitly configured to target
|
||||
versions of OPA before 1.0, or if no configuration is provided — where Regal is able to determine that an older version
|
||||
of OPA/Rego is being targeted. Consult the documentation on Regal's
|
||||
[configuration](https://openpolicyagent.org/projects/regal#configuration) for information on how to best work with older versions of
|
||||
OPA and Rego.
|
||||
|
||||
Since OPA v1.0, this rule is automatically disabled, as the parser itself will throw an error if a rule is named `if`,
|
||||
as that is made a keyword in Rego v1.0.
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow := true if {
|
||||
authorized
|
||||
}
|
||||
```
|
||||
|
||||
Which actually means:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow := true
|
||||
|
||||
if {
|
||||
authorized
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
import rego.v1
|
||||
|
||||
allow := true if {
|
||||
authorized
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Forgetting to import the `if` keyword (using `import future.keywords.if`, or from OPA v0.59.0+ `import rego.v1`) is a
|
||||
common mistake. While this often results in a parse error, there are some situations where the parser can't tell if the
|
||||
`if` is intended to be used as the imported keyword, or a new rule named `if`. This is almost always a mistake, and if
|
||||
it isn't — consider using a better name for your rule!
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
rule-named-if:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/rule-named-if/rule_named_if.rego)
|
||||
@@ -0,0 +1,40 @@
|
||||
# rule-shadows-builtin
|
||||
|
||||
**Summary**: Rule name shadows built-in
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# `or` is an operator
|
||||
or := 1 + 1
|
||||
|
||||
# `startswith` is a built-in function
|
||||
startswith := indexof("rego", "r")
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Using the name of built-in functions or operators as rule and variable names can lead to confusion and unexpected
|
||||
behavior.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
rule-shadows-builtin:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Built-in Functions](https://www.openpolicyagent.org/docs/policy-reference/#built-in-functions)
|
||||
- OPA Repo: [builtin_metadata.json](https://github.com/open-policy-agent/opa/blob/main/builtin_metadata.json)
|
||||
- Regal Docs: [var-shadows-builtin](https://openpolicyagent.org/projects/regal/rules/bugs/var-shadows-builtin)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/rule-shadows-builtin/rule_shadows_builtin.rego)
|
||||
@@ -0,0 +1,64 @@
|
||||
# sprintf-arguments-mismatch
|
||||
|
||||
**Summary**: Mismatch in `sprintf` arguments count
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
max_issues := 1
|
||||
|
||||
report contains warning if {
|
||||
count(issues) > max_issues
|
||||
|
||||
# two placeholders found in the string, but only one value in the array
|
||||
warning := sprintf("number of issues (%d) must not be higher than %d", [count(issues)])
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
max_issues := 1
|
||||
|
||||
report contains warning if {
|
||||
count(issues) > max_issues
|
||||
|
||||
# two placeholders found in the string, and two values in the array
|
||||
warning := sprintf("number of issues (%d) must not be higher than %d", [count(issues), max_issues])
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
While the built-in `sprintf` function itself reports argument mismatches, it'll do so by returning a string containing
|
||||
the error message rather than actually failing.
|
||||
|
||||
```shell
|
||||
> opa eval -f pretty 'sprintf("%v %d", [1])'
|
||||
"1 %!d(MISSING)"
|
||||
```
|
||||
|
||||
While this is normally caught in development and testing, having this issue reported at "compile time", which ideally
|
||||
is [directly in your editor](https://openpolicyagent.org/projects/regal/language-server) as you work on your policy. This means less
|
||||
time spent chasing down issues later, and a happier development experience.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
sprintf-arguments-mismatch:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Built-in Functions: `sprintf`](https://www.openpolicyagent.org/docs/policy-reference/#builtin-strings-sprintf)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/sprintf-arguments-mismatch/sprintf_arguments_mismatch.rego)
|
||||
@@ -0,0 +1,64 @@
|
||||
# time-now-ns-twice
|
||||
|
||||
**Summary**: Repeated calls to `time.now_ns`
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
timed if {
|
||||
now := time.now_ns()
|
||||
|
||||
# do some work here
|
||||
|
||||
# this doesn't work! result is always 0
|
||||
print("work done in:", time.now_ns() - now, "ns)
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
|
||||
To use the tools OPA provides for measuring performance.
|
||||
|
||||
## Rationale
|
||||
|
||||
An important property of Rego is that it makes policy evaluation _predictable_. Using the same input to query OPA for a
|
||||
decision multiple times should result in the same decision being made each time! A few built-in functions, like
|
||||
`http.send`, or [time.now_ns](https://www.openpolicyagent.org/docs/policy-reference/#builtin-time-timenow_ns) are
|
||||
however not **deterministic**. This means that repeated queries to policies where such functions are used may result in
|
||||
different decisions being made. For example, a policy that validates JSON Web Tokens would normally check if the current
|
||||
time is past the expiry value of the token, and deny any request where a token is found to be expired.
|
||||
|
||||
But while the use of non-deterministic built-in functions may result in different outcomes across different
|
||||
queries, all built-in functions are deterministic **within the scope of a single evaluation**. This means that calling
|
||||
e.g. `http.send` twice in a policy using the exact same arguments never results in different values being returned.
|
||||
This is equally true for `time.now_ns`. In order to ensure predictable evaluation, the time returned by `time.now_ns` is
|
||||
set once at the start of the evaluation, and never changes for the course of the request. Calling `time.now_ns` several
|
||||
times within a rule is thus pointless, as the same value will be returned each time.
|
||||
|
||||
This mistake is most commonly observed when developers try to measure elapsed time in some parts of their policy, the
|
||||
same way they'd normally do it using a traditional programming language (that is not deterministic). While this won't
|
||||
work, OPA provides several tools to help measure performance, and learning how to use them well is the best way to
|
||||
understand the performance characteristics of policy evaluation.
|
||||
|
||||
See the [performance](https://www.openpolicyagent.org/docs/policy-performance/) section of the OPA docs for an
|
||||
introduction to these tools, as well as advice on how to write performant policies.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
time-now-ns-twice:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [time.now_ns](https://www.openpolicyagent.org/docs/policy-reference/#builtin-time-timenow_ns)
|
||||
- OPA Docs: [Policy Performance](https://www.openpolicyagent.org/docs/policy-performance/)
|
||||
@@ -0,0 +1,42 @@
|
||||
# top-level-iteration
|
||||
|
||||
**Summary**: Iteration in top-level assignment
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
user := input.users[_]
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
While OPA allows this construct — it probably shouldn't. Performing iteration outside of a rule or function body
|
||||
doesn't make any sense, and traversing **any** collection containing more than one item in this context will result
|
||||
in an error:
|
||||
|
||||
```shell
|
||||
eval_conflict_error: complete rules must not produce multiple outputs
|
||||
```
|
||||
|
||||
If the collection only contains a single item, the assignment will succeed, and the result will be the single element
|
||||
assigned to the variable. As such, it is possible that a policy passing all tests still will fail when provided real
|
||||
data.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
top-level-iteration:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/top-level-iteration/top_level_iteration.rego)
|
||||
@@ -0,0 +1,50 @@
|
||||
# unassigned-return-value
|
||||
|
||||
**Summary**: Non-boolean return value unassigned
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
# return value not assigned
|
||||
lower(input.user.name)
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
# return value assigned
|
||||
name_lower := lower(input.user.name)
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Calling a built-in function that returns a non-boolean value without actually assigning the returned value is almost
|
||||
always a mistake. Only return of `false` or undefined will cause evaluation to halt, so a function that e.g. always
|
||||
returns a string will always be evaluated as "truthy". But more importantly — not handling the return value in that case
|
||||
is almost certainly a mistake.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
unassigned-return-value:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/unassigned-return-value/unassigned_return_value.rego)
|
||||
@@ -0,0 +1,98 @@
|
||||
# unused-output-variable
|
||||
|
||||
**Summary**: Unused output variable
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
some x
|
||||
role := input.user.roles[x]
|
||||
|
||||
# do something with "role", but not "x"
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
# don't declare `x` output var as it is redundant
|
||||
role := input.user.roles[_]
|
||||
|
||||
# do something with "role"
|
||||
}
|
||||
|
||||
# or better (see prefer-some-in-iteration rule)
|
||||
|
||||
allow if {
|
||||
some role in input.user.roles
|
||||
|
||||
# do something with "role"
|
||||
}
|
||||
|
||||
# or actually _use_ value bound to `x` somewhere, like in another
|
||||
# reference, function call, etc
|
||||
|
||||
allow if {
|
||||
some x
|
||||
input.user.roles[x] == data.required_roles[x]
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Output variables are variables "automatically" bound to values during evaluation, most commonly in iteration. This is
|
||||
a powerful feature of Rego that when used correctly can create concise but readable policies. However, output variables
|
||||
that are declared but not later referenced are _effectively_ unused and should be replaced by wildcard variables (`_`),
|
||||
or the use of `some .. in` iteration.
|
||||
|
||||
OPA itself has two methods for detecting and reporting unused variables as errors — one when using `some`:
|
||||
|
||||
```rego
|
||||
allow if {
|
||||
# `x` is never used in the body — this is a compiler error
|
||||
some x
|
||||
input.user.roles[role]
|
||||
|
||||
role == "admin"
|
||||
}
|
||||
```
|
||||
|
||||
And a [strict mode](https://www.openpolicyagent.org/docs/policy-language/#strict-mode) check for unused
|
||||
variables defined in assignment (`:=`), or as a function arguments:
|
||||
|
||||
```rego
|
||||
allow(role, required) {
|
||||
required_roles := data.required_roles
|
||||
|
||||
role == "admin"
|
||||
|
||||
# `required` never used in body, and neither is `required_roles`
|
||||
# both would be errors when strict mode is enabled
|
||||
}
|
||||
```
|
||||
|
||||
Neither of these methods however considers an unused output variable as "unused".
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
unused-output-variable:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Regal Docs: [prefer-some-in-iteration](https://openpolicyagent.org/projects/regal/rules/style/prefer-some-in-iteration)
|
||||
- OPA Docs: [Strict Mode](https://www.openpolicyagent.org/docs/policy-language/#strict-mode)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/unused-output-variable/unused_output_variable.rego)
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
draft: true
|
||||
---
|
||||
|
||||
# unused-return-value
|
||||
|
||||
## Please Note
|
||||
|
||||
This rule has been renamed to *unassigned-return-value* and can be found
|
||||
[here](https://openpolicyagent.org/projects/regal/rules/bugs/unassigned-return-value).
|
||||
@@ -0,0 +1,52 @@
|
||||
# var-shadows-builtin
|
||||
|
||||
**Summary**: Variable shadows built-in
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# variable `http` shadows `http.send` built-in function
|
||||
allow if {
|
||||
http := startswith(input.url, "http://")
|
||||
# do something with http
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# variable `is_http` doesn't shadow any built-in function
|
||||
allow if {
|
||||
is_http := startswith(input.url, "http://")
|
||||
# do something with is_http
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Using the name of built-in functions or operators as variable names can lead to confusion and unexpected behavior.
|
||||
A variable that shadows a built-in function (or the namespace of a function, like `http` in `http.send`) prevents any
|
||||
function in that namespace to be used later in the rule. Avoid this!
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
var-shadows-builtin:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Built-in Functions](https://www.openpolicyagent.org/docs/policy-reference/#built-in-functions)
|
||||
- OPA Repo: [builtin_metadata.json](https://github.com/open-policy-agent/opa/blob/main/builtin_metadata.json)
|
||||
- Regal Docs: [rule-shadows-builtin](https://openpolicyagent.org/projects/regal/rules/bugs/rule-shadows-builtin)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/var-shadows-builtin/var_shadows_builtin.rego)
|
||||
@@ -0,0 +1,54 @@
|
||||
# zero-arity-function
|
||||
|
||||
**Summary**: Avoid functions without args
|
||||
|
||||
**Category**: Bugs
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
first_user() := input.users[0]
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
first_user := input.users[0]
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Zero-arity functions, or functions without arguments, aren't treated as functions by Rego, but as regular rules. For
|
||||
that reason, they should also be expressed as such. One potential benefit of using functions over rules is that
|
||||
functions don't contribute to the
|
||||
[document](https://www.openpolicyagent.org/docs/philosophy/#the-opa-document-model) when a package is evaluated,
|
||||
and as such sometimes used to "hide" information from the result of evaluation. Whether this is a good practice or not,
|
||||
it importantly *doesn't work* with zero-arity functions, as they are treated as rules and *do* contribute to the
|
||||
document.
|
||||
|
||||
There is an [open issue](https://github.com/open-policy-agent/opa/issues/6315) in the OPA project to try and address
|
||||
this in the future, and allow zero-arity functions to be treated as other functions. Until then, the recommendation
|
||||
is to avoid them and just use rules in their place.
|
||||
|
||||
Note that if you're using the [opa fmt](https://openpolicyagent.org/projects/regal/rules/style/opa-fmt) command to format your code,
|
||||
it will remove the parentheses from a zero-arity function definition for you.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
bugs:
|
||||
zero-arity-function:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [The OPA Document Model](https://www.openpolicyagent.org/docs/philosophy/#the-opa-document-model)
|
||||
- OPA Issues: [Allow user-defined zero-argument functions in Rego](https://github.com/open-policy-agent/opa/issues/6315)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/bugs/zero-arity-function/zero_arity_function.rego)
|
||||
@@ -0,0 +1,50 @@
|
||||
# forbidden-function-call
|
||||
|
||||
**Summary**: Forbidden function call
|
||||
|
||||
**Category**: Custom
|
||||
|
||||
## Description
|
||||
|
||||
This custom rule allows providing Regal a list of
|
||||
[built-in functions](https://www.openpolicyagent.org/docs/policy-reference/#built-in-functions) that should be
|
||||
considered forbidden. Any call to a function in the list will be reported as a violation.
|
||||
|
||||
Another, more advanced, option to achieve the same result is the
|
||||
[capabilities](https://www.openpolicyagent.org/docs/deployments/#capabilities) feature in OPA. While a more
|
||||
capable option, allowing things like:
|
||||
|
||||
- Adding new custom built-in functions that OPA should be aware of
|
||||
- Disabling certain features not necessarily being built-in functions, like "future" keywords
|
||||
- List allowed hosts in network calls
|
||||
|
||||
...it is also more demanding to configure and maintain. If you're already using the capabilities feature
|
||||
to forbid certain functions as part of your policy development process, there's no need to enable this rule.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
custom:
|
||||
forbidden-function-call:
|
||||
# note that all rules in the "custom" category are disabled by default
|
||||
# (i.e. level "ignore") as some configuration needs to be provided by
|
||||
# the user (i.e. you!) in order for them to be useful.
|
||||
#
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
# Just an example — no functions forbidden by default
|
||||
forbidden-functions:
|
||||
# Prefer to use asymmetric algorithms
|
||||
- io.jwt.verify_hs256
|
||||
- io.jwt.verify_hs384
|
||||
- io.jwt.verify_hs512
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Capabilities](https://www.openpolicyagent.org/docs/deployments/#capabilities)
|
||||
- OPA Docs: [Built-in Functions](https://www.openpolicyagent.org/docs/policy-reference/#built-in-functions)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/custom/forbidden-function-call/forbidden_function_call.rego)
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Custom
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
|
||||
# Custom
|
||||
|
||||
The `custom` category is a special one, as the rules in this category allow you
|
||||
to enforce rules that are specific to your project, team or organization. This
|
||||
typically includes things like naming conventions, where you might want to
|
||||
ensure that, for example, all package names adhere to an organizational
|
||||
standard, like having a prefix matching the organization name.
|
||||
|
||||
:::warning
|
||||
Since these rules require configuration provided by the user, or are more
|
||||
opinionated than other rules, they are disabled by default. In order to enable
|
||||
them, see the configuration options available for each rule for how to configure
|
||||
them according to your requirements.
|
||||
:::
|
||||
|
||||
For more advanced requirements, see the guide on writing [custom rules](https://openpolicyagent.org/projects/regal/custom-rules) in Rego.
|
||||
|
||||
import RulesTable from '@site/src/components/projects/regal/RulesTable';
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<RulesTable category="custom"/>
|
||||
@@ -0,0 +1,2 @@
|
||||
title: Custom
|
||||
sidebar_position: 7
|
||||
@@ -0,0 +1,77 @@
|
||||
# missing-metadata
|
||||
|
||||
**Summary**: Package or rule missing metadata
|
||||
|
||||
**Category**: Custom
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package acmecorp.authz
|
||||
|
||||
authorized_users contains user if {
|
||||
# logic to determine authorized users
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
# METADATA
|
||||
# description: The `acmecorp.authz` module provides authorization logic for the AcmeCorp application.
|
||||
package acmecorp.authz
|
||||
|
||||
# METADATA
|
||||
# description: Provides a set of all authorized users given the conditions in `input`.
|
||||
# scope: document
|
||||
authorized_users contains user if {
|
||||
# logic to determine authorized users
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Using metadata annotations is a great way to document your policies, for both yourself and others. While using metadata
|
||||
annotations _everywhere_ might be overkill for many projects, it should absolutely be considered for libraries, or
|
||||
policies that target a larger audience.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Rules and functions with an underscore prefix in their name are commonly used to denote that they are intended
|
||||
to be used internally (i.e. within the same file) only, and while metadata occasionally help document these,
|
||||
they are not part of the "public API". The `missing-metadata` thus excludes these from the metadata requirement.
|
||||
|
||||
It is also possible to configure your own exceptions for both package and rule paths. See the configuration options
|
||||
below.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
custom:
|
||||
missing-metadata:
|
||||
# note that all rules in the "custom" category are disabled by default
|
||||
# (i.e. level "ignore"), so make sure to set the level to "error" if you
|
||||
# want this enabled!
|
||||
#
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
# package path pattern(s) to exclude from the requirement
|
||||
# defaults to no exclusions
|
||||
except-package-path-pattern: ^internal\.*
|
||||
# rule path pattern(s) to exclude from the requirement
|
||||
# defaults to no exclusions
|
||||
except-rule-path-pattern: \.report$
|
||||
# you might also want to exclude files based on their name,
|
||||
# like e.g. tests:
|
||||
ignore:
|
||||
files:
|
||||
- "*_test.rego"
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Metadata](https://www.openpolicyagent.org/docs/policy-language/#metadata)
|
||||
- OPA Docs: [Annotations](https://www.openpolicyagent.org/docs/policy-language/#annotations)
|
||||
- Rego Style Guide: [Use Metadata Annotations](https://github.com/StyraInc/rego-style-guide)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/custom/missing-metadata/missing_metadata.rego)
|
||||
@@ -0,0 +1,55 @@
|
||||
# naming-convention
|
||||
|
||||
**Summary**: Naming convention violation
|
||||
|
||||
**Category**: Custom
|
||||
|
||||
## Description
|
||||
|
||||
This custom rule allows teams and organizations to define their own naming conventions for their Rego projects, without
|
||||
having to write custom linter policies. Naming conventions are simply defined in the Regal configuration file using
|
||||
regex patterns.
|
||||
|
||||
Regal can enforce naming conventions for:
|
||||
|
||||
- Package names
|
||||
- Rule names
|
||||
- Function names
|
||||
- Variable names
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
custom:
|
||||
naming-convention:
|
||||
# note that all rules in the "custom" category are disabled by default
|
||||
# (i.e. level "ignore") as some configuration needs to be provided by
|
||||
# the user (i.e. you!) in order for them to be useful.
|
||||
#
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
conventions:
|
||||
# allow only "private" rules and functions, i.e. those starting with
|
||||
# underscore, or rules named "deny" or "allow"
|
||||
- pattern: '^_[a-z]+$|^deny$|^allow$'
|
||||
# one of "package", "rule", "function", "variable"
|
||||
targets:
|
||||
- rule
|
||||
- function
|
||||
# any number of naming rules may be added
|
||||
# package names must start with "acmecorp" or "system"
|
||||
- pattern: '^acmecorp|^system'
|
||||
targets:
|
||||
- package
|
||||
```
|
||||
|
||||
**Note:** In order to avoid characters accidentally getting escaped, always use single quotes to encode your regex
|
||||
patterns. Additionally, you'll most often want to include anchors for the start and end of the string (`^` and `$`) in
|
||||
your patterns, or else your pattern might accidentally match only parts of the name rather than the whole name.
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/custom/naming-convention/naming_convention.rego)
|
||||
@@ -0,0 +1,181 @@
|
||||
# narrow-argument
|
||||
|
||||
**Summary**: Function argument can be narrowed
|
||||
|
||||
**Category**: Custom
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
valid_user(user) if endswith(user.email, "acmecorp.com")
|
||||
valid_user(user) if endswith(user.email, "acmecorp.org")
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
valid_email(email) if endswith(email, "acmecorp.com")
|
||||
valid_email(email) if endswith(email, "acmecorp.org")
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
**Note!** This is a highly opinionated rule with some caveats that you should be aware of before you use it.
|
||||
|
||||
Accepting the most minimal types/values as functions arguments avoids unnecessary "dependencies", makes them more
|
||||
likely to be reusable, and tends to make code more readable as it's often easier to predict from a call site what
|
||||
a specifically named function does compared to a more generic version. It's fairly common to realize a function with
|
||||
narrowed arguments would benefit from being renamed. That is however left as an exercise to you!
|
||||
|
||||
This rule scans all use of arguments inside function heads and bodies, and will suggest narrowing down any argument
|
||||
passed to the minimal value which the function depends on. Incrementally defined functions are scanned in their entirety
|
||||
to make sure the narrowing is valid for all definitions. Example:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
country_code(user) := 61 if user.country == "Australia"
|
||||
country_code(user) := 81 if user.country == "Japan"
|
||||
```
|
||||
|
||||
In the above example, the functions only depends on `user.country`, and this rule (when enabled) will thus recommend
|
||||
narrowing the argument passed:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
country_code(country) := 61 if country == "Australia"
|
||||
country_code(country) := 81 if country == "Japan"
|
||||
```
|
||||
|
||||
Instead of passing around potentially large `user` objects, our function now only needs to consider a `country` string,
|
||||
which perhaps may prove useful for more than just users. Another benefit of this approach is that it's often possible
|
||||
to simplify functions even further by moving the equality comparison directly into the function's arguments — a simple
|
||||
form of [pattern matching](https://openpolicyagent.org/projects/regal/rules/idiomatic/equals-pattern-matching):
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
country_code("Australia") := 61
|
||||
country_code("Japan") := 81
|
||||
```
|
||||
|
||||
### Reference prefix narrowing
|
||||
|
||||
So far we have looked only at functions using an identical reference to one of its arguments. That's not always the
|
||||
case, but it doesn't mean the value passed can't be narrowed! Consider the following example:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
internal_user(context) if endswith(context.user.email, "@acmecorp.com")
|
||||
internal_user(context) if "staff" in context.user.roles
|
||||
```
|
||||
|
||||
In the example above, the `narrow-argument` rule would point out that while two different references to the `context`
|
||||
argument are used, they both have the `context.user` prefix in common, and the value passed could thus be narrowed to
|
||||
that:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
internal_user(user) if endswith(user.email, "@acmecorp.com")
|
||||
internal_user(user) if "staff" in user.roles
|
||||
```
|
||||
|
||||
## Caveats
|
||||
|
||||
Narrowing the types passed as function arguments may come with unintended and/or undesired side-effects. More
|
||||
specifically, the way OPA evaluates functions means that arguments are evaluated before the function is called. "Big"
|
||||
objects, like a `user` tend to be less likely to be undefined than e.g. a `user.fax` attribute. Aborting evaluation only
|
||||
because a user is without a fax machine is probably not what we want! But could be an unfortunate consequence of our
|
||||
change unless we are careful (or better, have extensive test coverage). Consider the following example:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
is_unreachable(user) if {
|
||||
not has_phone(user)
|
||||
not has_fax(user)
|
||||
}
|
||||
|
||||
has_phone(user) if
|
||||
is_string(user.phone)
|
||||
user.phone != ""
|
||||
}
|
||||
|
||||
has_fax(user) if {
|
||||
is_string(user.fax)
|
||||
user.fax != ""
|
||||
}
|
||||
```
|
||||
|
||||
While it's tempting to try and narrow the arguments passed to `has_phone` and `has_fax` only to what they need:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
is_unreachable(user) if {
|
||||
not has_phone(user.phone)
|
||||
not has_fax(user.fax)
|
||||
}
|
||||
|
||||
has_phone(phone) if
|
||||
is_string(phone)
|
||||
phone != ""
|
||||
}
|
||||
|
||||
has_fax(fax) if {
|
||||
is_string(fax)
|
||||
fax != ""
|
||||
}
|
||||
```
|
||||
|
||||
We have now changed the behavior of `is_unreachable`, and a user without phone or fax will no longer be considered
|
||||
unreachable. Why? Again, because OPA evaluates the function arguments before they are passed to the function, **and**
|
||||
before the result is negated by `not`, an expression like:
|
||||
|
||||
```rego
|
||||
not has_phone(user.phone)
|
||||
```
|
||||
|
||||
Will be rewritten by OPA to something like this:
|
||||
|
||||
```rego
|
||||
arg1 := user.phone
|
||||
not has_phone(arg1)
|
||||
```
|
||||
|
||||
If the `user.phone` attribute doesn't exist, evaluation will never reach the next line where the function is called!
|
||||
|
||||
Before narrowing arguments, always consider the impact of undefined values, negation and how functions are evaluated.
|
||||
And make sure to not rewrite any function that isn't extensively covered by unit tests! With that said, there are often
|
||||
ways to deal with undefined attributes even when passing narrower argument types. In the example above, we could for
|
||||
example rewrite `is_unreachable` to `is_reachable`, and then use `not` to negate *that* to answer if the user is
|
||||
impossible to reach.
|
||||
|
||||
Find what works best for you, and use the `exclude-args` configuration option (see below) to exclude arg names that you
|
||||
commonly don't want to narrow, or [ignore directives](https://openpolicyagent.org/projects/regal#inline-ignore-directives) for single
|
||||
locations.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
custom:
|
||||
narrow-argument:
|
||||
# note that all rules in the "custom" category are disabled by default
|
||||
# (i.e. level "ignore")
|
||||
#
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
# exclude args by name
|
||||
# example below excludes any argument named 'config' or 'user'
|
||||
exclude-args:
|
||||
- config
|
||||
- user
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
# one-liner-rule
|
||||
|
||||
**Summary**: Rule body could be made a one-liner
|
||||
|
||||
**Category**: Custom
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
is_admin
|
||||
}
|
||||
|
||||
is_admin if {
|
||||
"admin" in input.user.roles
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if is_admin
|
||||
|
||||
is_admin if "admin" in input.user.roles
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Rules with only a single expression in the body may omit the curly braces around the body, and be written as a
|
||||
one-liner. This makes simple rules read more like English, and will have more rules fit on the screen.
|
||||
|
||||
As with other rules in the `custom` category, this is not necessarily a general recommendation, but a style preference
|
||||
teams or organizations might want to standardize on. As such, it must be enabled via configuration.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
custom:
|
||||
one-liner-rule:
|
||||
# note that all rules in the "custom" category are disabled by default
|
||||
# (i.e. level "ignore")
|
||||
#
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/custom/one-line-rule/one_line_rule.rego)
|
||||
@@ -0,0 +1,85 @@
|
||||
# prefer-value-in-head
|
||||
|
||||
**Summary**: Prefer value in rule head
|
||||
|
||||
**Category**: Custom
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
pin_as_number := val if {
|
||||
is_number(input.pin_code)
|
||||
val := to_number(input.pin_code)
|
||||
}
|
||||
|
||||
deny contains message if {
|
||||
not input.user
|
||||
message := "user attribute missing from input"
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
pin_as_number := to_number(input.pin_code) if is_number(input.pin_code)
|
||||
|
||||
deny contains "user attribute missing from input" if not input.user
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Rules that return the value assigned in the last expression of the rule body may have the value, or the function
|
||||
returning the value, moved directly to the rule head. This creates more succinct rules, and often allows for rules to be
|
||||
expressed as "one-liners". This is not a general recommendation, but a style preference that a team or organization
|
||||
might want to standardize on. As such, it is placed in the custom category, and must be explicitly enabled in
|
||||
configuration.
|
||||
|
||||
The `only-scalars` configuration option may be used to only suggest moving scalar values (strings, numbers, booleans,
|
||||
null) to the head, and not expressions or functions returning a value. With this option set to `true`, the following
|
||||
example would be flagged:
|
||||
|
||||
```rego
|
||||
deny contains message if {
|
||||
not input.user
|
||||
# value is a scalar
|
||||
message := "user attribute missing from input"
|
||||
}
|
||||
```
|
||||
|
||||
But not:
|
||||
|
||||
```rego
|
||||
deny contains message if {
|
||||
not input.user
|
||||
# value returned from a function call, not suggested if `only-scalars` is set to `true`
|
||||
message := sprintf("user attribute missing from input: %v", [input])
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
custom:
|
||||
prefer-value-in-head:
|
||||
# note that all rules in the "custom" category are disabled by default
|
||||
# (i.e. level "ignore")
|
||||
#
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
# whether to only suggest moving scalar values (strings, numbers, booleans, null)
|
||||
# to the head, and not expressions or functions
|
||||
only-scalars: false
|
||||
# variable names to exempt from the rule (by default, none)
|
||||
except-var-names:
|
||||
- report
|
||||
- violation
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head.rego)
|
||||
@@ -0,0 +1,86 @@
|
||||
# ambiguous-scope
|
||||
|
||||
**Summary**: Ambiguous metadata scope
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# METADATA
|
||||
# description: allow is true if the user is admin, or the requested resource is public
|
||||
allow if user_is_admin
|
||||
|
||||
allow if public_resource
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# METADATA
|
||||
# description: allow is true if the user is admin, or the requested resource is public
|
||||
# scope: document
|
||||
allow if user_is_admin
|
||||
|
||||
allow if public_resource
|
||||
```
|
||||
|
||||
**Or (scope `rule` implied, but _all_ incremental definitions are annotated)**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# METADATA
|
||||
# description: allow is true if the user is admin
|
||||
allow if user_is_admin
|
||||
|
||||
# METADATA
|
||||
# description: allow is true if the requested resource is public
|
||||
allow if public_resource
|
||||
```
|
||||
|
||||
**Or (scope `rule` explicit)**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# METADATA
|
||||
# description: allow is true if the user is admin
|
||||
# scope: rule
|
||||
allow if user_is_admin
|
||||
|
||||
allow if public_resource
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
The default scope for metadata annotating a rule is the `rule` scope, which
|
||||
"[applies to the individual rule statement](https://www.openpolicyagent.org/docs/policy-language/#scope)" only.
|
||||
This default is sensible for a rule defined only once, but is somewhat ambiguous for a rule defined incrementally, like
|
||||
the `allow` rule in the examples above. Was the intention really to annotate that single definition, or the rule as
|
||||
whole? Most likely the latter, and that's what the `document` scope is for.
|
||||
|
||||
If only a single rule in a group of incremental rule definitions is annotated, it should have it's `scope` set explicitly
|
||||
to either `document` or `rule`. If all incremental definitions are annotated, explicit `scope: rule` is not required.
|
||||
|
||||
## Exceptions
|
||||
|
||||
If a single incremental rule definition is annotated as `entrypoint: true`, this rule will allow that.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
ambiguous-scope:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Annotations](https://www.openpolicyagent.org/docs/policy-language/#annotations)
|
||||
- Regal Docs: [no-defined-entrypoint](https://openpolicyagent.org/projects/regal/rules/idiomatic/no-defined-entrypoint)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/ambiguous-scope/ambiguous_scope.rego)
|
||||
@@ -0,0 +1,55 @@
|
||||
# boolean-assignment
|
||||
|
||||
**Summary**: Prefer `if` over boolean assignment
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Avoid**
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
more_than_one_member := count(input.members) > 1
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
more_than_one_member if count(input.members) > 1
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Assigning the result of a boolean function is almost always redundant, as the boolean value returned by the expression
|
||||
rarely is used for anything but to determine whether to continue evaluation. Moving the condition to the body following
|
||||
an `if` will have the rule either evaluate to `true` or be undefined. For the few cases where `false` should be
|
||||
returned, using a `default` rule assignment is preferable, as it is guaranteed to be assigned a value even on undefined
|
||||
input:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default more_than_one_member := false
|
||||
|
||||
# will be assigned `false` even if input.members is undefined
|
||||
more_than_one_member if count(input.members) > 1
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
boolean-assignment:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Styra Blog: [How to express OR in Rego](https://www.styra.com/blog/how-to-express-or-in-rego/)
|
||||
- Regal Docs: [default-over-else](https://openpolicyagent.org/projects/regal/rules/style/default-over-else)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/boolean-assignment/boolean_assignment.rego)
|
||||
@@ -0,0 +1,46 @@
|
||||
# custom-has-key-construct
|
||||
|
||||
**Summary**: Custom function may be replaced by `in` and `object.keys`
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
mfa if has_key(input.claims, "mfa")
|
||||
|
||||
has_key(map, key) if {
|
||||
_ = map[key]
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
mfa if "mfa" in object.keys(input.claims)
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Checking if a key exists in an object (regardless of the attribute's value) used to be done using custom functions. With
|
||||
the introduction of the [object.keys](https://www.openpolicyagent.org/docs/policy-reference/#builtin-object-objectkeys)
|
||||
(OPA [v0.47.0](https://github.com/open-policy-agent/opa/releases/tag/v0.47.0)) function, this is no longer necessary,
|
||||
and using the built-in function together with `in` should be preferred.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
custom-has-key-construct:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/custom-has-key-construct/custom_has_key_construct.rego)
|
||||
@@ -0,0 +1,49 @@
|
||||
# custom-in-construct
|
||||
|
||||
**Summary**: Custom function may be replaced by `in` keyword
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if has_value(input.user.roles, "admin")
|
||||
|
||||
# This custom function was commonly seen before the introduction
|
||||
# of the `in` keyword. Avoid it now.
|
||||
has_value(arr, item) if {
|
||||
item == arr[_]
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if "admin" in input.user.roles
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
The `in` keyword was introduced in OPA [v0.34.0](https://github.com/open-policy-agent/opa/releases/tag/v0.34.0).
|
||||
Prior to that, it was a common practice to create a custom helper function that would iterate over values of an array in
|
||||
order to check if it contained a provided value. Since the introduction of the `in` keyword, this is no longer
|
||||
necessary. The `in` keyword additionally supports sets and maps as the collection type, so using it consistently is
|
||||
recommended.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
custom-in-construct:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/custom-in-construct/custom_in_construct.rego)
|
||||
@@ -0,0 +1,137 @@
|
||||
# directory-package-mismatch
|
||||
|
||||
**Summary**: Directory structure should mirror package
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Automatically fixable**: Yes
|
||||
|
||||
## Rationale
|
||||
|
||||
Quickly finding the package you're looking for in a policy repository is made much easier when package paths are
|
||||
mirrored in the directory structure of your project. Meaning that if the name of your package is
|
||||
`permissions.users.claims`, it should reside in a file under the `permissions/users/claims` directory. Note however
|
||||
that any number of files can contribute to the same package! The `permissions/users/claims` directory may thus
|
||||
contain several policy files that all declare `package permissions.users.claims`.
|
||||
|
||||
### Example
|
||||
|
||||
An example of directory structure for a project following this convention might look like this:
|
||||
|
||||
```shell
|
||||
# Directory structure # Package path
|
||||
.
|
||||
├── README.md
|
||||
└── bundle
|
||||
└── authorization
|
||||
├── main.rego # authorization
|
||||
└── rbac
|
||||
├── data.json # authorization.rbac
|
||||
├── roles
|
||||
│ └── roles.rego # authorization.rbac.roles
|
||||
│ └── roles_test.rego # authorization.rbac.roles_test
|
||||
└── users
|
||||
├── customers.rego # authorization.rbac.users
|
||||
├── customers_test.rego # authorization.rbac.users_test
|
||||
├── internal.rego # authorization.rbac.users
|
||||
└── internal_test.rego # authorization.rbac.users_test
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
Astute observers may notice that the test files in the example above are placed in the same directory as the
|
||||
policies they test. This may seem to contradict the
|
||||
[test-outside-test-package](https://openpolicyagent.org/projects/regal/rules/testing/test-outside-test-package) rule, which
|
||||
says that any test package should have a `_test` suffix in its package path. However, putting tests next to
|
||||
the file they target arguably makes it _easier_ to find, and is a common practice in the OPA community. This
|
||||
rule therefore by default ignores the `_test` suffix when determining whether the package path matches the
|
||||
directory structure.
|
||||
|
||||
This behavior can be changed by setting the `exclude-test-suffix` configuration option to `false`, in which
|
||||
case package paths with a `_test` suffix also will be required to reside in a directory with a `_test` suffix.
|
||||
|
||||
Setting the `exclude-test-suffix` option to `false` means the example from above would now look like this:
|
||||
|
||||
```shell
|
||||
# Directory structure # Package path
|
||||
.
|
||||
├── README.md
|
||||
└── bundle
|
||||
└── authorization
|
||||
├── main.rego # authorization
|
||||
└── rbac
|
||||
├── data.json # authorization.rbac
|
||||
├── roles
|
||||
│ └── roles.rego # authorization.rbac.roles
|
||||
├── roles_test
|
||||
│ └── roles_test.rego # authorization.rbac.roles_test
|
||||
├── users
|
||||
│ ├── customers.rego # authorization.rbac.users
|
||||
│ └── internal.rego # authorization.rbac.users
|
||||
└── users_test
|
||||
├── customers_test.rego # authorization.rbac.users_test
|
||||
└── internal_test.rego # authorization.rbac.users_test
|
||||
```
|
||||
|
||||
Whichever way you choose is up to you. Consistency is key!
|
||||
|
||||
### Bundles
|
||||
|
||||
While directory structure doesn't matter to OPA when parsing _policies_, directories parsed as
|
||||
[bundles](https://www.openpolicyagent.org/docs/management-bundles/) will read _data_ (`data.json` or
|
||||
`data.yaml`) files and insert the data in the `data` document tree based on the directory structure relative
|
||||
to the bundle root. Having policies structured in the same manner provides a uniform experience, and makes it
|
||||
easier to understand where both policies and data come from.
|
||||
|
||||
### `regal fix` & Editor Support
|
||||
|
||||
Regal's [`fix` command](https://openpolicyagent.org/projects/regal/fixing) can automatically
|
||||
rename files in a project to ensure compliance with this rule. This is
|
||||
particularly useful when refactoring a project with many files.
|
||||
|
||||
:::info
|
||||
Note that files will be renamed relative to their nearest root, see the
|
||||
[documentation on roots](https://openpolicyagent.org/projects/regal#project-roots) when using
|
||||
this rule with policy roots different from the project root.
|
||||
:::
|
||||
|
||||
Editors integrating Regal's [language server](https://openpolicyagent.org/projects/regal/language-server) will automatically display
|
||||
suggestions for idiomatic package paths based on the directory structure in which a policy resides. The image below
|
||||
demonstrates a new policy being created inside an `authorization/rbac/roles` directory, and the editor
|
||||
([via Regal](https://openpolicyagent.org/projects/regal/language-server#code-completions)) suggesting the package path
|
||||
`authorization.rbac.roles`.
|
||||
|
||||
<img
|
||||
src={require('../../assets/rules/pkg_name_completion.png').default}
|
||||
alt="Package path auto-completion in VS Code"/>
|
||||
|
||||
In addition, empty files will be be 'formatted' to have the correct package
|
||||
based on the directory structure. Newly created Rego files are treated in much
|
||||
the same way. When a new file is created, the server will send a series of edits
|
||||
back to set the content. If `exclude-test-suffix` is set to `false`, the file
|
||||
will also be moved if required to the `_test` directory for that package.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
directory-package-mismatch:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
# exclude _test suffixes from package paths before comparing
|
||||
# them to directory structure paths. when set to false, a
|
||||
# package like authz.policy_test would need to be placed in
|
||||
# an authz/policy_test directory, and if set to true (default)
|
||||
# would be expected to be in authz/policy
|
||||
exclude-test-suffix: true
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Rego Style Guide: [Package name should match file location](https://github.com/StyraInc/rego-style-guide#package-name-should-match-file-location)
|
||||
- Regal Docs: [test-outside-test-package](https://openpolicyagent.org/projects/regal/rules/testing/test-outside-test-package)
|
||||
- OPA Docs: [Bundles](https://www.openpolicyagent.org/docs/management-bundles/)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/directory-package-mismatch/directory_package_mismatch.rego)
|
||||
@@ -0,0 +1,96 @@
|
||||
# equals-pattern-matching
|
||||
|
||||
**Summary**: Prefer pattern matching in function arguments
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
readable_number(x) := "one" if x == 1
|
||||
readable_number(x) := "two" if x == 2
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
readable_number(1) := "one"
|
||||
readable_number(2) := "two"
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Pattern matching on equality in function arguments is one of Rego's most well-kept secrets. As secret as it might be,
|
||||
it's a great way to simplify custom functions performing equality checks on their arguments in the rule body, by
|
||||
moving the equality check to match on the function call itself. This means that a function like the one below:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
normalize_role(role) := "admin" if {
|
||||
role == "administrator"
|
||||
}
|
||||
|
||||
normalize_role(role) := "admin" if {
|
||||
role == "root"
|
||||
}
|
||||
```
|
||||
|
||||
May have the equality check moved to the function argument, and the function only evaluated in case the argument matches
|
||||
the equality "pattern":
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
normalize_role("administrator") := "admin"
|
||||
|
||||
normalize_role("root") := "admin"
|
||||
```
|
||||
|
||||
Rules that evaluate to `true` may even have the assignment removed altogether, i.e.:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
is_admin(role) if role == "admin"
|
||||
|
||||
is_admin(role) if role == "administrator"
|
||||
|
||||
is_admin(role) if role == "root"
|
||||
```
|
||||
|
||||
Can be simplified to just:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
is_admin("admin")
|
||||
|
||||
is_admin("administrator")
|
||||
|
||||
is_admin("root")
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
This rule is currently limited to simple rules where the equality check is the **only** condition in the rule body. This
|
||||
will be improved in future releases.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
equals-pattern-matching:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Styra Blog: [How to express OR in Rego](https://www.styra.com/blog/how-to-express-or-in-rego/)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/equals-pattern-matching/equals_pattern_matching.rego)
|
||||
@@ -0,0 +1,65 @@
|
||||
# in-wildcard-key
|
||||
|
||||
**Summary**: Unnecessary wildcard key
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
# since only the value is used, we don't need to iterate the keys
|
||||
some _, user in input.users
|
||||
|
||||
# do something with each user
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
allow if {
|
||||
some user in input.users
|
||||
|
||||
# do something with each user
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
The `some .. in` iteration form can either iterate only values:
|
||||
|
||||
```rego
|
||||
some value in object
|
||||
```
|
||||
|
||||
Or keys and values:
|
||||
|
||||
```rego
|
||||
some key, value in object
|
||||
```
|
||||
|
||||
Using a wildcard variable for the key in the key-value form is thus unnecessary, and:
|
||||
|
||||
```rego
|
||||
some _, value in object
|
||||
```
|
||||
|
||||
Can simply be replaced by:
|
||||
|
||||
````rego
|
||||
some value in object
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
in-wildcard-key:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
````
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Idiomatic
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
|
||||
# Idiomatic
|
||||
|
||||
Rules that enforce idiomatic code.
|
||||
|
||||
import RulesTable from '@site/src/components/projects/regal/RulesTable';
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<RulesTable category="idiomatic"/>
|
||||
@@ -0,0 +1,2 @@
|
||||
title: Idiomatic
|
||||
sidebar_position: 2
|
||||
@@ -0,0 +1,88 @@
|
||||
# no-defined-entrypoint
|
||||
|
||||
**Summary**: Missing entrypoint annotation
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Type**: Aggregate - only runs when more than one file is provided for linting
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default allow := false
|
||||
|
||||
# Nothing wrong with this rule, but an
|
||||
# entrypoint should be documented as such
|
||||
allow if user_is_admin
|
||||
allow if public_resource_read
|
||||
|
||||
user_is_admin if {
|
||||
some role in input.user.roles
|
||||
role in data.permissions.admin_roles
|
||||
}
|
||||
|
||||
public_resource_read if {
|
||||
input.request.method == "GET"
|
||||
input.request.path[0] == "public"
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
package policy
|
||||
|
||||
default allow := false
|
||||
|
||||
# METADATA
|
||||
# description: Allow only admins, or reading public resources
|
||||
# entrypoint: true
|
||||
allow if user_is_admin
|
||||
allow if public_resource_read
|
||||
|
||||
user_is_admin if {
|
||||
some role in input.user.roles
|
||||
role in data.permissions.admin_roles
|
||||
}
|
||||
|
||||
public_resource_read if {
|
||||
input.request.method == "GET"
|
||||
input.request.path[0] == "public"
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Defining one or more entrypoints for your policies is a good practice to follow. An entrypoint is simply a package or
|
||||
rule that is meant to be queried for decisions from the outside. While it might seem obvious to the policy author which
|
||||
rules are meant to be queried, adding an extra line of two of metadata will help make it obvious to others.
|
||||
|
||||
Marking a package or rule via an
|
||||
[entrypoint annotation attribute](https://www.openpolicyagent.org/docs/policy-language/#entrypoint) not only
|
||||
provides good documentation for others, but also unlocks programmatic possibilities, like:
|
||||
|
||||
1. Your policy library may be compiled to WebAssembly without extra entrypoint arguments
|
||||
1. Your policy library may be compiled to an
|
||||
[intermediate representation](https://blog.openpolicyagent.org/i-have-a-plan-exploring-the-opa-intermediate-representation-ir-format-7319cd94b37d)
|
||||
(IR) format without extra entrypoint arguments
|
||||
1. External applications may present your entrypoints as part of rendered documentation
|
||||
1. External applications may use your entrypoints to know what to evaluate
|
||||
1. External applications — like Regal — may use this information to determine what other rules are used or not
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
no-defined-entrypoint:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Metadata](https://www.openpolicyagent.org/docs/policy-language/#metadata)
|
||||
- OPA Docs: [Entrypoint](https://www.openpolicyagent.org/docs/policy-language/#entrypoint)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/no-defined-entrypoint/no_defined_entrypoint.rego)
|
||||
@@ -0,0 +1,61 @@
|
||||
# non-raw-regex-pattern
|
||||
|
||||
**Summary**: Use raw strings for regex patterns
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Automatically fixable**: [Yes](https://openpolicyagent.org/projects/regal/fixing)
|
||||
|
||||
**Avoid**
|
||||
```rego
|
||||
all_digits if {
|
||||
regex.match("[\\d]+", "12345")
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
```rego
|
||||
all_digits if {
|
||||
regex.match(`[\d]+`, "12345")
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
[Raw strings](https://www.openpolicyagent.org/docs/edge/policy-language/#strings) are interpreted literally, allowing
|
||||
you to avoid having to escape special characters like `\` in your regex patterns. Using raw strings for regex patterns
|
||||
additionally makes them easier to identify as such.
|
||||
|
||||
## Limitations
|
||||
|
||||
This rule currently only scans regex string literals in the place of the `pattern` argument of the various
|
||||
[regex built-in functions](https://www.openpolicyagent.org/docs/policy-reference/#regex). It will not **not**
|
||||
try to "resolve" patterns assigned to variables. The following example would as such not render a warning:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# Pattern assigned to variable
|
||||
pattern := "[\\d]+"
|
||||
|
||||
# This won't trigger a violation
|
||||
allow if regex.match(pattern, "12345")
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
non-raw-regex-pattern:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- Rego Style Guide: [Use raw strings for regex patterns](https://github.com/StyraInc/rego-style-guide#use-raw-strings-for-regex-patterns)
|
||||
- OPA Docs: [Regex Functions Reference](https://www.openpolicyagent.org/docs/policy-reference/#regex)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/non-raw-regex-pattern/non_raw_regex_pattern.rego)
|
||||
@@ -0,0 +1,149 @@
|
||||
# prefer-set-or-object-rule
|
||||
|
||||
**Summary**: Prefer set or object rule over comprehension
|
||||
|
||||
**Category**: Idiomatic
|
||||
|
||||
**Avoid**
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# top level set comprehension
|
||||
developers := {developer |
|
||||
some user in input.users
|
||||
"developer" in user.roles
|
||||
developer := user.name
|
||||
}
|
||||
|
||||
# top level object comprehension
|
||||
user_roles_mapping := {user: roles |
|
||||
some user in input.users
|
||||
roles := user.roles
|
||||
}
|
||||
```
|
||||
|
||||
**Prefer**
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# set generating rule
|
||||
developers contains developer if {
|
||||
some user in input.users
|
||||
"developer" in user.roles
|
||||
developer := user.name
|
||||
}
|
||||
|
||||
# object generating rule
|
||||
user_roles_mapping[user] := roles if {
|
||||
some user in input.users
|
||||
roles := user.roles
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
Comprehensions are [awesome](https://www.styra.com/blog/five-things-you-didnt-know-about-opa/), and should be part of
|
||||
any policy author's toolbox. Using comprehensions inside of rule bodies allow for a wide variety of elegant solutions to
|
||||
otherwise hard problems. However, when used as the value directly (and unconditionally) assigned to a rule, it is almost
|
||||
always better to use a rule that generates a set or object in the rule body rather than having a comprehension do so in
|
||||
the rule head. Why is that?
|
||||
|
||||
### Readability
|
||||
|
||||
Rules that generate objects, and sets even more so, read more natural than comprehensions, and are generally more
|
||||
descriptive. While both constructs are easy to spot for a seasoned Rego author, anything that helps improve readability
|
||||
is a win.
|
||||
|
||||
### Extensibility
|
||||
|
||||
While readability is important, the real benefit of using a rule to generate a set or object is that it allows the rule
|
||||
to be _extended_. This is particularly true for set generating rules, and it's not by accident they often are referred
|
||||
to as **multi-value rules**. A rule assigned the value of a set comprehension can't have its value changed later, or
|
||||
more items added to the set. A set generating rule however, can easily be extended to contain more items, conditionally
|
||||
or unconditionally.
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# Getting developers from input
|
||||
developers contains developer if {
|
||||
some user in input.users
|
||||
"developer" in user.roles
|
||||
developer := user.name
|
||||
}
|
||||
|
||||
# *Also* getting developers from data
|
||||
developers contains developer if {
|
||||
some user in data.users
|
||||
"developer" in user.roles
|
||||
developer := user.name
|
||||
}
|
||||
|
||||
# Unconditionally adding a developer to the set
|
||||
developers contains "Hackerman"
|
||||
```
|
||||
|
||||
In the example above, all three rules contribute to the `developers` set. If we wanted to, we could even create another
|
||||
policy file using the same package, and have more rules added there that would contribute to the set. This creates some
|
||||
great opportunities for extensibility, and collaboration across developers and teams working on policy together.
|
||||
|
||||
Objects differ somewhat from sets in that while several rules can be used to generate an object, there cannot be more
|
||||
than one rule contributing to a single key-value pair.
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
novels[title] := content if {
|
||||
some document in input.documents
|
||||
document.type == "novel"
|
||||
title := document.title
|
||||
content := document.content
|
||||
}
|
||||
|
||||
# This works as long as "The Hobbit" is not already in the novels object
|
||||
novels["The Hobbit"] := "In a hole in the ground there lived a hobbit."
|
||||
|
||||
# Map and set generating objects can also be combined, in which case the
|
||||
# value is extensible even for the same key! In the example above, more
|
||||
# rules could help contribute titles to an author, perhaps using different
|
||||
# data sources.
|
||||
titles_by_author[document.author] contains document.title if {
|
||||
some document in input.documents
|
||||
}
|
||||
```
|
||||
|
||||
## Exceptions
|
||||
|
||||
Note that this rule does **not** apply to array comprehensions, as there is no equivalent tp use a rule to generate an
|
||||
array.
|
||||
|
||||
This rule will also ignore simple comprehensions used solely for the purpose of converting an array to a set, i.e:
|
||||
|
||||
```rego
|
||||
package policy
|
||||
|
||||
# Convert set to array. This is fine.
|
||||
my_set := {item | some item in arr}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
This linter rule provides the following configuration options:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
idiomatic:
|
||||
prefer-set-or-object-rule:
|
||||
# one of "error", "warning", "ignore"
|
||||
level: error
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- OPA Docs: [Generating Sets](https://www.openpolicyagent.org/docs/policy-language/#generating-sets)
|
||||
- OPA Docs: [Generating Objects](https://www.openpolicyagent.org/docs/policy-language/#generating-objects)
|
||||
- OPA Docs: [Comprehensions](https://www.openpolicyagent.org/docs/policy-language/#comprehensions)
|
||||
- Styra Blog: [Five Things You Didn't Know About OPA](https://www.styra.com/blog/five-things-you-didnt-know-about-opa/)
|
||||
- GitHub: [Source Code](https://github.com/open-policy-agent/regal/blob/main/bundle/regal/rules/idiomatic/prefer-set-or-object-rule/prefer_set_or_object_rule.rego)
|
||||