Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions web/src/constants/project.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { POLICIES_STRING_REGEX } from "./project";

describe("POLICIES_STRING_REGEX", () => {
it.each([
"resources=*;actions=*",
"resources=application;actions=get",
"resources=application,piped;actions=get,list",
"resources=application;actions=get,list,create,update,delete",
])("matches policy without labels: %s", (policy) => {
expect(POLICIES_STRING_REGEX.test(policy)).toBe(true);
});

// Regression test for https://github.com/pipe-cd/pipecd/issues/7172
// Label scoped resources (resources=NAME{key:value}) were rejected by the
// form validation even though parseRBACPolicies/formalizePoliciesList
// already supported them.
it.each([
"resources=application{env:prod};actions=get",
"resources=application{env:prod,team:foo};actions=get",
"resources=application{env:prod},piped;actions=get",
"resources=application{env:prod},piped{env:prod,team:foo};actions=get,list",
])("matches policy with label scoped resources: %s", (policy) => {
expect(POLICIES_STRING_REGEX.test(policy)).toBe(true);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you add resources=application{a=b;c:d};actions=get and resources=application{};actions=get to the invalid cases once the pattern is tightened? these are the cases where form validation and parseRBACPolicies currently disagree

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added both cases to the invalid-policy test list — resources=application{a=b;c:d};actions=get and resources=application{};actions=get — and confirmed the full suite still passes (50 suites / 185 tests) with all existing valid/invalid cases intact.

it.each([
"resources=application{env:prod;actions=get", // unclosed brace
"resources=bogus;actions=get", // unknown resource type
"resources=application;actions=bogus", // unknown action
"resources=application", // missing actions
"resources=application{a=b;c:d};actions=get", // semicolon escapes the label block
"resources=application{};actions=get", // empty label block
"resources=application{env:pr\n\nod};actions=get", // newline injection in label value
])("does not match invalid policy: %s", (policy) => {
expect(POLICIES_STRING_REGEX.test(policy)).toBe(false);
});
});
14 changes: 12 additions & 2 deletions web/src/constants/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,21 @@ export const TEXT_TO_RBAC_ACTION_TYPE: Record<
delete: ProjectRBACPolicy.Action.DELETE,
};

// example: resources=(\*|application|deployment|event|piped|deploymentChain|project|apiKey|insight|,)+;\s*actions=(\*|get|list|create|update|delete|,)+
// A single label pair, e.g. "env:prod". Key/value exclude the characters
// that are structurally significant elsewhere in the grammar (`;{}:,`), as
// well as whitespace, so a label block can't accidentally swallow the
// resources/actions separator or use a newline (policies are split on
// blank lines) to escape the label block.
const LABEL_PAIR = "[^;{}:,\\s]+:[^;{}:,\\s]+";
// A label block requires at least one valid key:value pair; empty ("{}")
// or malformed ("{garbage}") label content is rejected.
const LABEL_BLOCK = `(\\{${LABEL_PAIR}(,${LABEL_PAIR})*\\})?`;

// example: resources=(\*|application(\{key:value(,key:value)*\})?|deployment(\{key:value(,key:value)*\})?|...|,)+;\s*actions=(\*|get|list|create|update|delete|,)+
export const POLICIES_STRING_REGEX = new RegExp(
"resources=(" +
rbacResourceTypes()
.map((v) => v.replace(/\*/, "\\*"))
.map((v) => v.replace(/\*/, "\\*") + LABEL_BLOCK)
.join("|") +
"|,)+;\\s*actions=(" +
rbacActionTypes()
Expand Down
Loading