mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-13 03:42:35 -06:00
docs/regal: Update docs following 0.36.0 (#7891)
* docs/regal: Update docs following 0.36.0 Mostly these had already been imported during the initial migration to the OPA site, but there were some minor changes and this process is still manual (scripted). Signed-off-by: Charlie Egan <charlie_egan@apple.com> * website/regal: Update rules table to show content Signed-off-by: Charlie Egan <charlie_egan@apple.com> --------- Signed-off-by: Charlie Egan <charlie_egan@apple.com>
This commit is contained in:
@@ -20,8 +20,8 @@ When running Regal against a directory, like `regal lint my-policies/`, Regal do
|
||||
- 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.
|
||||
[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
|
||||
|
||||
@@ -9,11 +9,14 @@ 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 —
|
||||
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
|
||||
[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
|
||||
|
||||
@@ -6,7 +6,8 @@ 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.
|
||||
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
|
||||
|
||||
@@ -1,69 +1,151 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import Link from "@docusaurus/Link";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
import rules from "@generated/regal/default/rules.json";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
export default function RegalRulesTable({ category = "" }) {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [selectedCategory, setSelectedCategory] = useState("");
|
||||
|
||||
export default function RulesTable({ category }) {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const preselectedCategory = category || null;
|
||||
|
||||
let predicates = [];
|
||||
const effectiveCategory = preselectedCategory || selectedCategory;
|
||||
|
||||
let basePath = "./rules/";
|
||||
if (category !== undefined && category !== "") {
|
||||
predicates.push((rule) => rule.id.startsWith(category + "/"));
|
||||
basePath = "./";
|
||||
}
|
||||
const allRows = useMemo(() => {
|
||||
return Object.values(rules)
|
||||
.map((rule) => {
|
||||
const filePathMatch = rule.filePath.match(/\/regal\/rules\/([^/]+)\//);
|
||||
const pathCategory = filePathMatch ? filePathMatch[1] : "unknown";
|
||||
|
||||
if (searchQuery !== "") {
|
||||
predicates.push((rule) => rule.id.includes(searchQuery.toLowerCase()));
|
||||
}
|
||||
const content = rule.content || "";
|
||||
|
||||
const filteredRules = rules.filter(rule => {
|
||||
if (predicates.length === 0) return true;
|
||||
const summaryMatch = content.match(/\*\*Summary\*\*:\s*(.*?)\n/);
|
||||
const typeMatch = content.match(/\*\*Type\*\*:\s*(.*?)\n/);
|
||||
const fixableMatch = content.match(/\*\*Automatically fixable\*\*:\s*\[(.*?)\]\((.*?)\)/);
|
||||
|
||||
return predicates.map((predicate) => predicate(rule))
|
||||
.every((e) => e == true);
|
||||
});
|
||||
const summaryParts = [];
|
||||
|
||||
const summary = summaryMatch?.[1]?.trim();
|
||||
if (summary) summaryParts.push(summary);
|
||||
|
||||
const type = typeMatch?.[1]?.trim();
|
||||
if (type) summaryParts.push(`**Type**: ${type}`);
|
||||
|
||||
if (fixableMatch) {
|
||||
const label = fixableMatch[1];
|
||||
const link = fixableMatch[2];
|
||||
summaryParts.push(`**Automatically fixable**: [${label}](${link})`);
|
||||
}
|
||||
|
||||
if (summaryParts.length === 0) return null;
|
||||
|
||||
const contentCategoryMatch = content.match(/\*\*Category\*\*:\s*(.*?)\n/);
|
||||
|
||||
return {
|
||||
pathCategory,
|
||||
displayCategory: contentCategoryMatch?.[1] || pathCategory,
|
||||
id: rule.id,
|
||||
content,
|
||||
summary: summaryParts.join("\n\n"),
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
}, []);
|
||||
|
||||
const categories = useMemo(() => {
|
||||
const set = new Set(allRows.map((r) => r.pathCategory));
|
||||
return Array.from(set).sort();
|
||||
}, [allRows]);
|
||||
|
||||
const filteredRows = useMemo(() => {
|
||||
const term = searchTerm.toLowerCase();
|
||||
return allRows
|
||||
.map((row) => {
|
||||
if (!term) return { ...row, contentOnlyMatch: false };
|
||||
|
||||
const idMatch = row.id.toLowerCase().includes(term);
|
||||
const categoryMatch = row.displayCategory.toLowerCase().includes(term);
|
||||
const contentMatch = row.content.toLowerCase().includes(term);
|
||||
|
||||
const contentOnlyMatch = contentMatch && !idMatch && !categoryMatch;
|
||||
const matches = idMatch || categoryMatch || contentMatch;
|
||||
|
||||
return matches ? { ...row, contentOnlyMatch } : null;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.filter((row) => effectiveCategory ? row.pathCategory === effectiveCategory : true)
|
||||
.sort((a, b) => a.displayCategory.localeCompare(b.displayCategory));
|
||||
}, [allRows, searchTerm, effectiveCategory]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.searchContainer}>
|
||||
<>
|
||||
<div className={styles.controls}>
|
||||
{!preselectedCategory && (
|
||||
<select
|
||||
className={styles.select}
|
||||
value={selectedCategory}
|
||||
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||
>
|
||||
<option value="">All Categories</option>
|
||||
{categories.map((cat) => (
|
||||
<option key={cat} value={cat}>
|
||||
{cat.charAt(0).toUpperCase() + cat.slice(1)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<input
|
||||
className={styles.input}
|
||||
type="text"
|
||||
placeholder="Search entries..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className={styles.searchInput}
|
||||
placeholder="Search rules..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{filteredRules.length === 0
|
||||
? <p>No matching rules</p>
|
||||
: (
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rule</th>
|
||||
<th>Summary</th>
|
||||
{filteredRows.length === 0 && (
|
||||
<div>
|
||||
<p>No rules found.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{filteredRows.length > 0 && (
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
{!preselectedCategory && <th className={styles.th}>Category</th>}
|
||||
<th className={styles.th}>ID</th>
|
||||
<th className={styles.th}>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredRows.map((row) => (
|
||||
<tr key={row.id}>
|
||||
{!preselectedCategory && (
|
||||
<td className={styles.categoryCell}>
|
||||
{row.displayCategory}
|
||||
</td>
|
||||
)}
|
||||
<td className={styles.idCell}>
|
||||
<Link to={`/projects/regal/rules/${row.id}`}>
|
||||
{row.id}
|
||||
</Link>
|
||||
{row.contentOnlyMatch && (
|
||||
<span className={styles.contentMatchIndicator}>
|
||||
Content Matches Search
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className={styles.summaryCell}>
|
||||
<ReactMarkdown>{row.summary}</ReactMarkdown>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredRules.map((rule) => {
|
||||
return (
|
||||
<tr key={rule.id}>
|
||||
<td>
|
||||
<Link to={basePath + rule.id}>{rule.id}</Link>
|
||||
</td>
|
||||
<td>{rule.summary}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,64 @@
|
||||
.searchContainer {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
max-width: 40rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
.table tbody td:first-child {
|
||||
width: 1;
|
||||
.th {
|
||||
text-align: left;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 0.125rem solid #ccc;
|
||||
}
|
||||
|
||||
.table tbody td:last-child {
|
||||
.td {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 0.0625rem solid #eee;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.categoryCell {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 0.0625rem solid #eee;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.idCell {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 0.0625rem solid #eee;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.summaryCell {
|
||||
padding: 0.5rem;
|
||||
border-bottom: 0.0625rem solid #eee;
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input {
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.select {
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.contentMatchIndicator {
|
||||
margin-left: 0.5rem;
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ export async function loadRules() {
|
||||
const id = filePath.replace(rootPath + "/", "").replace(".md", "");
|
||||
|
||||
acc.push({
|
||||
summary,
|
||||
filePath,
|
||||
content,
|
||||
id,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user