ããã«ã¡ã¯ãXïŒã¯ãã¹ïŒ ã€ãããŒã·ã§ã³ æ¬éš ã¯ã©ãŠã ã€ãããŒã·ã§ã³ ã»ã³ã¿ãŒã®æŽç°ã§ãããã®èšäºã§ã¯ Rego ã® Linter ã§ãã Regal ã玹ä»ããŸãã 1 ãã®èšäºã¯ é»éç·ç Advent Calendar 2024 ã® 5 æ¥ç®ã®æçš¿ã§ãã忥ã®èšäºã¯äºæããã®ãå
¥ç€Ÿ 3 幎ç®ã®æ¥åå
容玹ä»ãã§ããã Open Policy Agent / Rego ãšã¯ïŒ Regal ãšã¯ïŒ Regal ãå®è¡ãã Lint ãå®è¡ãã ããŒã«ã«ã§å®è¡ãã ã€ã³ã¹ããŒã«ãã å®è¡ãã CI ã§å®è¡ãã ãšãã£ã¿ãšçµã¿åãããŠå®è¡ãã ã³ãŒããèªåä¿®æ£ãã ã«ãŒã« çµã¿èŸŒã¿ã«ãŒã« ã«ã¹ã¿ã ã«ãŒã« Regal ãèšå®ãã èšå®ãã¡ã€ã« æ ŒçŽå Žæ ãµã³ãã« èšå®ãã¡ã€ã«ã®æžãæ¹ rules ignores capabilities project ããã©ã«ãã®èšå®å€ ã«ãŒã«ãæå¶ãã inline directive CLI ãã©ã° èšå®ãã¡ã€ã« ãããžã§ã¯ãã«ãŒã ã«ã¹ã¿ã ã«ãŒã«ãå®è£
ãã ã«ã¹ã¿ã ã«ãŒã«ã®çš®é¡ é圢ãäœæãã ãµã³ãã«ã³ãŒã ãã¡ã€ã«åäœã§æ€æ»ãã è€æ°ã®ãã¡ã€ã«ããŸãšããŠæ€æ»ãã ãã¹ãã³ãŒã ã«ã¹ã¿ã ã«ãŒã«ã®æžãæ¹ æ ŒçŽå Žæ ããã±ãŒãž ã¡ã¿ããŒã¿ description ïŒå¿
é ïŒ related_resources ïŒä»»æïŒ schema ïŒä»»æïŒ ã«ãŒã« report aggregate aggregate_report Regal ãæäŸããäž»ãªè£å©é¢æ° result.fail(metadata, details) result.location(x) result.aggregate(chain, aggregate_data) regal.parse_module(filename, policy) ãã¹ããå®è¡ãã ãããã« åèè³æ Open Policy Agent / Rego ãšã¯ïŒ Open Policy Agent (OPA) ãš Rego ã®æŠèŠã«ã€ããŠã¯ä»¥äžã®èšäºããåç
§ãã ããã 2 Policy as Code ãå®çŸãã Open Policy Agent / Rego ã®çŽ¹ä» - é»éç·ç ããã¯ããã° Regal ãšã¯ïŒ Regal 㯠Styra 瀟ãéçºããŠãã Rego ã® Linter ã§ãã 3 Regal ã䜿çšã㊠ãã¹ãã ã©ã¯ ãã£ã¹ã«åããã³ãŒã ããããééãããã°ãéå¹ççãªåŠçãå«ãã³ãŒã ãããžã§ã¯ãç¬èªã®ã³ãŒãã£ã³ã°èŠçŽã«åããã³ãŒã ãªã©ã æ©æ¢°ç ã«çºèŠããããšã§ Rego ã³ãŒãã®å質ãå¯èªæ§ãåäžã§ããŸãã Regal ãå®è¡ãã Lint ãå®è¡ãã ããŒã«ã«ã§å®è¡ãã ã€ã³ã¹ããŒã«ãã 以äžã® 3 ã€ã®æ¹æ³ããããŸãã ããã±ãŒãžãããŒãžã£ãŒã䜿ã£ãŠã€ã³ã¹ããŒã«ããã 4 GitHub ã® release asset ãããŠã³ããŒãïŒã€ã³ã¹ããŒã«ããã Docker ã䜿ãã å
·äœçãªã€ã³ã¹ããŒã«æé ã¯ããã§ã¯çç¥ããŸãã詳ããã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Download Regal | Styra Documentation Packaging | Styra Documentation å®è¡ãã 以äžã®ãã㪠Rego ã® ãœãŒã¹ã³ãŒã ã policy/authz.rego ã«ãããšããŸãã package authz import rego.v1 default allow = false allow if { isEmployee "developer" in input.user.roles } isEmployee if regex.match("@acmecorp\\.com$", input.user.email) 以äžã®ã³ãã³ãã§ Lint ãå®è¡ããŸãã 5 regal lint policy/ ãããšã«ãŒã«ã«éåããŠããç®æã衚瀺ãããŸãã Rule: opa-fmt Description: File should be formatted with `opa fmt` Category: style Location: policy/authz.rego:1:1 Text: package authz Documentation: https://docs.styra.com/regal/rules/style/opa-fmt Rule: directory-package-mismatch Description: Directory structure should mirror package Category: idiomatic Location: policy/authz.rego:1:9 Text: package authz Documentation: https://docs.styra.com/regal/rules/idiomatic/directory-package-mismatch 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://docs.styra.com/regal/rules/idiomatic/non-raw-regex-pattern Rule: use-assignment-operator Description: Prefer := over = for assignment Category: style Location: policy/authz.rego:5:15 Text: default allow = false Documentation: https://docs.styra.com/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://docs.styra.com/regal/rules/style/prefer-snake-case 1 file linted. 5 violations found. Hint: 2/5 violations can be automatically fixed (directory-package-mismatch, use-assignment-operator) Run regal fix --help for more details. CI ã§å®è¡ãã CI ã§ Regal ãå®è¡ããããšãã§ããŸããäŸãã° GitHub Actions ã§ããã° StyraInc/setup-regal action ã䜿ã£ãŠä»¥äžã®ãããªã¯ãŒã¯ãããŒãèšå®ã§ããŸãã name : Regal Lint on : pull_request : jobs : lint-rego : runs-on : ubuntu-latest steps : - uses : actions/checkout@v4 - uses : StyraInc/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 詳ããã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Using Regal in your build pipeline | Styra Documentation ãšãã£ã¿ãšçµã¿åãããŠå®è¡ãã Regal 㯠Language Server ããã³ Debug Adapter ã®æ©èœãæäŸããŸããæ®æ®µäœ¿ã£ãŠãããšãã£ã¿ãš Regal ã飿ºãããããšã§ Linter ããã®çŽ æ©ããã£ãŒããã㯠ã³ãŒãè£å® ããŠã¹ãªãŒããŒæã® ããŒã«ããã ã®è¡šç€º å®çŸ©ãžã®ç§»å ãªã©ã®æ©æµãåããããšãå¯èœã§ãã æ¬èšäºã§ã¯ãããã®è©³çŽ°ã¯æ±ããŸããã詳ããã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Editor support | Styra Documentation Language Server | Styra Documentation Debug Adapter | Styra Documentation ã³ãŒããèªåä¿®æ£ãã Regal ã¯ä»¥äžã®ã«ãŒã«ã«é¢ããŠã³ãŒããèªåçã«ä¿®æ£ã§ããŸãã opa-fmt use-rego-v1 use-assignment-operator no-whitespace-comment directory-package-mismatch å
ã»ã©ã®ãµã³ãã«ã³ãŒã policy/authz.rego ãä¿®æ£ããŠã¿ãŸãããã以äžã®ã³ãã³ãã§ã³ãŒããèªåçã«ä¿®æ£ããŸãã 6 ãªã --dry-run ãã©ã°ãèšå®ã㊠dry run ããããšãã§ããŸãã regal fix policy/ 2 fixes applied: In project root: /home/ubuntu/projects/regal/policy authz.rego -> authz/authz.rego: - directory-package-mismatch - use-rego-v1 ãµã³ãã«ã³ãŒããã«ãŒã«ã«åŸã£ãŠä»¥äžã®ããã«ä¿®æ£ãããŸããã 7 directory-package-mismatch ã«åŸã£ãŠãã¡ã€ã«ã policy/authz.rego ãã policy/authz/authz.rego ãžç§»åãããŸããã opa-fmt ãš use-assignment-operator ã«åŸã£ãŠãã¡ã€ã«ã®äžèº«ã以äžã®ããã«ä¿®æ£ãããŸããã package authz import rego.v1 -default allow = false +default allow := false allow if { - isEmployee - "developer" in input.user.roles + isEmployee + "developer" in input.user.roles } isEmployee if regex.match("@acmecorp\\.com$", input.user.email) 詳ããã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Fixing Violations | Styra Documentation ã«ãŒã« çµã¿èŸŒã¿ã«ãŒã« Regal ã«ã¯ããã©ã«ãã§è€æ°ã®ã«ãŒã«ãçµã¿èŸŒãŸããŠããŸãã çµã¿èŸŒã¿ã«ãŒã«ã¯ä»¥äžã® 7 ã€ã®ã«ããŽãªã«åé¡ãããŠããŸããcustom ã«ããŽãªä»¥å€ã®ã«ãŒã«ã¯ããã©ã«ãã§æå¹ã§ãã ^6 bus ïŒ äžè¬çãªééãã æœåšç ãªãã°ãéå¹ççãªæžãæ¹ã«é¢ããã«ãŒã«ã idiomatic ïŒ æ
£çšçãªæžãæ¹ã«é¢ãã imports ïŒ ã€ã³ããŒãã«é¢ããã«ãŒã«ã performance ïŒ ããã©ãŒãã³ã¹ã«é¢ããã«ãŒã«ã style ïŒ ã¹ã¿ã€ã«ã«é¢ããã«ãŒã«ïŒ Rego Style Guide ïŒã testing ïŒ ãã¹ãã«é¢ããã«ãŒã«ã custom ïŒ ããŒãºã«åãããŠã«ã¹ã¿ãã€ãºãã¹ãã«ãŒã«ã å
·äœçãªã«ãŒã«ã®å
容ã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Rules | Styra Documentation ã«ã¹ã¿ã ã«ãŒã« ç¬èªã®ã«ãŒã«ãå®çŸ©ããããšãã§ããŸãã詳ããã¯åŸã»ã©èª¬æããŸãã Regal ãèšå®ãã èšå®ãã¡ã€ã« æ ŒçŽå Žæ Regal ã¯èšå®ãã¡ã€ã«ã以äžã®ããã«æ¢çŽ¢ããŸãã ã«ã¬ã³ã ãã£ã¬ã¯ã ãªãããããžã§ã¯ãã«ãŒããžãã©ããŒã¹ãã€ã€ãã¡ã€ã« .regal/config.yaml ãæ¢ããŸããæåã«èŠã€ãã£ããã¡ã€ã«ãèšå®ãã¡ã€ã«ãšããŠåç
§ããŸãã --config-file ãã©ã°ã«ãã¡ã€ã«ãã¹ãæå®ãããŠããå Žåã¯ãããåç
§ããŸãã ãµã³ãã« 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 èšå®ãã¡ã€ã«ã®æžãæ¹ rules rules ã«ã¯åã«ãŒã«ã«é¢ããèšå®ãèšè¿°ããŸããäž»ãªèšå®é
ç®ã以äžã«ç€ºããŸãã ã㌠説æ rules.default.level ãã¹ãŠã®ã«ãŒã«ã®ããã©ã«ãã® level ãæå®ããŸãã rules.<category>.default.level ããã«ããŽãªã®ããã©ã«ãã® level ãæå®ããŸãã rules.<category>.<rule>.level ããã«ãŒã«ã® level ãæå®ããŸãã rules.<category>.<rule>.ignore.files ããã«ãŒã«ã®é©çšå¯Ÿè±¡é€å€ãšãããã¡ã€ã«ãæå®ããŸãã level ã«ã¯ä»¥äžã®å€ãæå®ã§ããŸãã error ïŒ éåã衚瀺ã㊠lint ã³ãã³ãããŒã以å€ã®çµäºã³ãŒãã§çµäºããŸãã warning ïŒ éåã衚瀺ã㊠lint ã³ãã³ãããŒãã®çµäºã³ãŒãã§çµäºããŸãã ignore ïŒ ã«ãŒã«ãç¡å¹åããŸãã ã«ãŒã«ã«ãã£ãŠã¯äžèšã«å ããŠåºæã®èšå®é
ç®ããããŸãã 詳ããã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãã«èšèŒãããåã«ãŒã«ã®èª¬ææããåç
§ãã ããã Rules | Styra Documentation ignores ignores ã«ã¯ãã¹ãŠã®ã«ãŒã«ã®é©çšå¯Ÿè±¡å€ãšãããã¡ã€ã«ãæå®ããŸãã capabilities capabilities ã«ã¯ OPA ã®ã©ã³ã¿ã€ã ããŒãžã§ã³ãªã©ãæå®ããŸãã æå®ããã©ã³ã¿ã€ã ããŒãžã§ã³ã§ã¯å©çšã§ããªãæ©èœã颿°ã«äŸåããã«ãŒã«ã¯ç¡å¹åãããŸãã 8 äž»ãªèšå®é
ç®ã以äžã«ç€ºããŸãã ã㌠説æ capabilities.from OPA ã®ã©ã³ã¿ã€ã ããŒãžã§ã³ãæå®ããŸãããã®ããŒãžã§ã³ã§ã¯å©çšã§ããªãæ©èœã颿°ã«äŸåããã«ãŒã«ã¯ç¡å¹åãããŸãã[^8]æå®ããªãå Žå㯠Regal ããªãªãŒã¹ãããæã®ææ°ã® OPA ã®ã©ã³ã¿ã€ã ããŒãžã§ã³ã䜿çšãããŸãã capabilities.plus å©çšã§ããæ©èœã颿°ã远å ã§æå®ããŸãã capabilities.minus å©çšã§ããªãæ©èœã颿°ã远å ã§æå®ããŸãã 詳ããã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Capabilities | Styra Documentation project ãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªãæå®ããŸãã詳ããã¯åŸã»ã©èª¬æããŸãã ããã©ã«ãã®èšå®å€ ãŠãŒã¶ãèšå®ãã¡ã€ã«ã§æç€ºçã«å€ãèšå®ããŠããªãèšå®é
ç®ã«ã€ããŠã¯ä»¥äžã®å€ã䜿çšãããŸãã data.yaml ã«ãŒã«ãæå¶ãã Regal ã®ã«ãŒã«ããããžã§ã¯ãã«é©ããªãå Žåã¯ãã®ã«ãŒã«ãç¡å¹åã§ããŸããã«ãŒã«ãç¡å¹åããæ¹æ³ã¯ä»¥äžã® 3 çš®é¡ããããŸãã inline directive ãœãŒã¹ã³ãŒã ã«ä»¥äžã®ãããªã³ã¡ã³ããèšè¿°ããããšã§ã ãœãŒã¹ã³ãŒã ã®ç¹å®ã®ç®æã«å¯Ÿããç¹å®ã®ã«ãŒã«ã®é©çšãç¡å¹åã§ããŸãã # regal ignore:<rule> å©çšäŸã¯ä»¥äžã®ãšããã§ãã package policy import rego.v1 # 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 # ... } CLI ãã©ã° 以äžã®ãã©ã°ã§æå®ããã«ãŒã«ãæå¹åãŸãã¯ç¡å¹åã§ããŸãã --enable , --disable ïŒ æå®ããã«ãŒã«ãæå¹åãŸãã¯ç¡å¹åããŸãã --enable-category , --disable-category ïŒ æå®ããã«ããŽãªã®ã«ãŒã«ãæå¹åãŸãã¯ç¡å¹åããŸãã --enable-all , --disable-all ïŒ ãã¹ãŠã®ã«ãŒã«ãæå¹åãŸãã¯ç¡å¹åããŸãã ãŸãæå®ãããã¡ã€ã«ãç¡èŠããããšãã§ããŸãã --ignore-files ïŒ æå®ãããã¡ã€ã«ãç¡èŠããŸãã èšå®ãã¡ã€ã« æå®ããã«ãŒã«ãç¡å¹åããŸãã rules : style : prefer-snake-case : level : ignore æå®ããã«ããŽãªã®ã«ãŒã«ãç¡å¹åããŸãã rules : style : default : level : ignore ãã¹ãŠã®ã«ãŒã«ãç¡å¹åããŸãã rules : default : level : ignore æå®ãããã¡ã€ã«ã«å¯ŸããŠæå®ããã«ãŒã«ãç¡å¹åããŸãã rules : style : line-length : level : error ignore : files : - "*_test.rego" - "scratch.rego" æå®ãããã¡ã€ã«ã«å¯ŸããŠãã¹ãŠã®ã«ãŒã«ãç¡å¹åããŸãã ignore : files : - file1.rego - "*_tmp.rego" ãããžã§ã¯ãã«ãŒã Regal ã¯ä»¥äžã® ãã£ã¬ã¯ã ãªããããžã§ã¯ãã«ãŒããšããŠèªèããŸãã èšå®ãã¡ã€ã«ã® .project.roots ã§æå®ããã ãã£ã¬ã¯ã 㪠.manifest ãã¡ã€ã«ãååšãã ãã£ã¬ã¯ã 㪠.regal ãã£ã¬ã¯ã ãªãååšãã ãã£ã¬ã¯ã 㪠ã«ã¬ã³ã ãã£ã¬ã¯ã 㪠äžéšã®ã«ãŒã«ïŒäŸãã° directory-package-mismatch ïŒãæ£ããè©äŸ¡ããããã«ã¯ãããžã§ã¯ãã«ãŒããæ£ããèšå®ããå¿
èŠããããŸãã ãããããžã§ã¯ãã«ãŒããéåžžãšç°ãªãå Žåã¯ãããžã§ã¯ãã«ãŒããæç€ºçã«èšå®ããŠãããŸãããã 詳ããã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Project Roots | Styra Documentation ã«ã¹ã¿ã ã«ãŒã«ãå®è£
ãã ã«ã¹ã¿ã ã«ãŒã«ã®çš®é¡ ã«ã¹ã¿ã ã«ãŒã«ã«ã¯ä»¥äžã® 2 çš®é¡ã®å®è£
æ¹æ³ããããŸãã ãã¡ã€ã«åäœã§æ€æ»ããæ¹æ³ è€æ°ã®ãã¡ã€ã«ããŸãšããŠæ€æ»ããæ¹æ³ ãã¡ã€ã«åäœã§ã¯ã«ãŒã«éåã®æç¡ã倿ã§ããªãå Žåã¯åŸè
ã®å®è£
æ¹æ³ãæ¡çšããŸãã é圢ãäœæãã regal new rule ã³ãã³ããå®è¡ããŸãã regal new rule --category <category> --name <rule> ãããš Regal ã¯ã«ã¹ã¿ã ã«ãŒã«ã® ãœãŒã¹ã³ãŒã ã®é圢ã以äžã«äœæããŸãã .regal/rules/custom/regal/rules/<category>/<rule>/<rule>.rego .regal/rules/custom/regal/rules/<category>/<rule>/<rule>_test.rego ãµã³ãã«ã³ãŒã ãã¡ã€ã«åäœã§æ€æ»ãã äŸãã°åãã¡ã€ã«ã® package ã acme.corp system.log ã®ããããã§å§ãŸã£ãŠããããšãæ€æ»ããã«ã¹ã¿ã ã«ãŒã«ã¯ä»¥äžã®ããã«ãªããŸãã # 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 rego.v1 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" } è€æ°ã®ãã¡ã€ã«ããŸãšããŠæ€æ»ãã äŸãã° default allow := false ã®ããã« ååã allow ããã©ã«ãå€ã false ãªã«ãŒã«ããããžã§ã¯ãå
šäœã§ 1 ã€ä»¥äžååšããããšãæ€æ»ããã«ã¹ã¿ã ã«ãŒã«ã¯ä»¥äžã®ããã«ãªããŸãã # 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 rego.v1 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(), {"package": input["package"]}) # optional metadata here } # 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`"}) } ãã¹ãã³ãŒã äŸãã°å
ã»ã©ã® custom.regal.rules.naming["acme-corp-package"] ããã±ãŒãžããã¹ããããã¹ãã³ãŒãã¯ä»¥äžã®ããã«ãªããŸãã package custom.regal.rules.naming["acme-corp-package_test"] import rego.v1 import data.custom.regal.rules.naming["acme-corp-package"] as rule test_acme_corp_package_allowed if { module := regal.parse_module("example.rego", "package acme.corp.foo") r := rule.report with input as module count(r) == 0 } test_system_log_package_allowed if { module := regal.parse_module("example.rego", "package system.log.foo") r := rule.report with input as module count(r) == 0 } test_foo_bar_baz_package_not_allowed if { module := regal.parse_module("example.rego", "package foo.bar.baz") r := rule.report with input as module r == {{ "category": "naming", "description": "All packages must use \"acme.corp\" base name", "level": "error", "location": { "col": 9, "end": { "col": 12, "row": 1, }, "file": "example.rego", "row": 1, "text": "package foo.bar.baz", }, "related_resources": [{ "description": "documentation", "ref": "https://www.acmecorp.example.org/docs/regal/package", }], "title": "acme-corp-package", }} } ã«ã¹ã¿ã ã«ãŒã«ã®æžãæ¹ æ ŒçŽå Žæ ã«ã¹ã¿ã ã«ãŒã«ã® ãœãŒã¹ã³ãŒã 㯠.regal/rules é
äžã«æ ŒçŽããŸããç¹å¥ãªèŠä»¶ããªããã° regal new rule ã³ãã³ãã«å£ã£ãŠä»¥äžã®ãã¹ã«æ ŒçŽããã®ãããã§ãããã .regal/rules/custom/regal/rules/<category>/<rule>/<rule>.rego ãã regal/rules 以å€ã®å Žæã«æ ŒçŽããå Žå㯠regal ã®å®è¡æã« --rules ãã©ã°ã§ãã¹ãæå®ããŸãã ããã±ãŒãž ã«ã¹ã¿ã ã«ãŒã«ã®ããã±ãŒãžã¯ä»¥äžã® åœåèŠå ã«åŸãå¿
èŠããããŸãã custom.regal.rules.<category>.<rule> ã¡ã¿ããŒã¿ ã¡ã¿ããŒã¿ ã«ã€ããŠã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Open Policy Agent | Policy Language | Metadata description ïŒå¿
é ïŒ ã«ã¹ã¿ã ã«ãŒã«ã®æŠèŠãèšè¿°ããŸãã description : All packages must use "acme.corp" base name related_resources ïŒä»»æïŒ ã«ã¹ã¿ã ã«ãŒã«ã®ããã¥ã¡ã³ãã® URL ãèšèŒããŸãã description ã documentation ã«ããªããš regal lint ã®å®è¡çµæã«ã¯è¡šç€ºãããŸããã related_resources : - description : documentation ref : https://www.acmecorp.example.org/docs/regal/package schema ïŒä»»æïŒ ã¹ããŒã ã«ã€ããŠã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ãããåç
§ãã ããã Open Policy Agent | Policy Language | Schema Regal ã§ã¯ä»¥äžã® ã¹ããŒã ãæäŸãããŠããŸãã schema.regal.ast ïŒ report , aggregate ã«ãŒã«ã® input ã® ã¹ããŒã schema.regal.aggregate ïŒ aggregate_report ã«ãŒã«ã® input ã® ã¹ããŒã 詳ããã¯åŸã»ã©èª¬æããŸãã schemas : - input : schema.regal.ast ã«ãŒã« å®è£
æ¹æ³ããšã«å¿
èŠãªã«ãŒã«ãå®è£
ããŸãã ãã¡ã€ã«åäœã§æ€æ»ããå Žåã¯ä»¥äžã®ã«ãŒã«ãå¿
èŠã§ãã report è€æ°ã®ãã¡ã€ã«ããŸãšããŠæ€æ»ããå Žåã¯ä»¥äžã®ã«ãŒã«ãå¿
èŠã§ãã aggregate aggregate_report report ãã¡ã€ã«åäœã§ã«ã¹ã¿ã ã«ãŒã«ã®æ€æ»ãè¡ããŸãã report contains violation if { # 1. ãã¡ã€ã«åäœã§ã«ã¹ã¿ã ã«ãŒã«ã®æ€æ»ãè¡ãã # ... # 2. éåãçºèŠãããå Žå㯠report ã®èŠçŽ ã« result.fail ãçæãããªããžã§ã¯ããå ããã violation := result.fail(rego.metadata.chain(), result.location(obj)) } input ã«ã¯åãã¡ã€ã«ã® Rego ã³ãŒãã®æœè±¡ æ§ææš ã Regal çšã«æé©åãããã®ãæ ŒçŽãããŸãã ã¹ããŒã 㯠schema.regal.ast ã§ããå
·äœçãªå
容ã¯ä»¥äžã®ã³ãã³ãã§ç¢ºèªã§ããŸãã regal parse <æ€æ»å¯Ÿè±¡ãã¡ã€ã«> ã«ãŒã«éåãçºèŠãããå Žå㯠report ã«ãŒã«ã®èŠçŽ ã« result.fail 颿°ãçæãããªããžã§ã¯ããå ããŸãã aggregate åãã¡ã€ã«ããããŒã¿ãåéããŸãã aggregate contains entry if { # 1. åãã¡ã€ã«ããããŒã¿ãåéããã # ... # 2. aggregate ã®èŠçŽ ã« result.aggregate ãçæãããªããžã§ã¯ããå ããã entry := result.aggregate(rego.metadata.chain(), {...}) } input 㯠report ã«ãŒã«ãšåãã§ãã å¿
èŠã«å¿ã㊠aggregate ã«ãŒã«ã®èŠçŽ ã« result.aggregate 颿°ãçæãããªããžã§ã¯ããå ããŸãããã®ããŒã¿ã¯åŸè¿°ãã aggregate_report ã«ãŒã«ã«æž¡ãããŸãã aggregate_report aggregate ã«ãŒã«ã®çµæãããšã«ã«ã¹ã¿ã ã«ãŒã«ã®æ€æ»ãè¡ããŸãã aggregate_report contains violation if { # 1. aggregate ã®çµæã«å¯ŸããŠã«ã¹ã¿ã ã«ãŒã«ã®æ€æ»ãè¡ãã # ... # 2. éåãçºèŠãããå Žå㯠aggregate_report ã®èŠçŽ ã« result.fail ãçæãããªããžã§ã¯ããå ããã violation := result.fail(rego.metadata.chain(), {...}) } input ã® ã¹ããŒã 㯠schema.regal.aggregate ã§ãã input.aggregate ã«ã¯ aggregate ã«ãŒã«ã®çµæãé
åãšããŠæ ŒçŽãããŸãã ã«ãŒã«éåãçºèŠãããå Žå㯠aggregate_report ã«ãŒã«ã®èŠçŽ ã« result.fail 颿°ãçæãããªããžã§ã¯ããå ããŸãã Regal ãæäŸããäž»ãªè£å©é¢æ° Regal ãæäŸããè£å©é¢æ°ã®ãã¡äž»ã«å©çšãããã®ã以äžã«ç޹ä»ããŸãã result.fail(metadata, details) # METADATA # description: | # helper function to call when building the "return value" for the `report` in any linter rule â # recommendation being that both built-in rules and custom rules use this in favor of building the # result by hand # scope: document fail(metadata, details) := ... report , aggregate_report ã«ãŒã«ã®èŠçŽ ãšãªããªããžã§ã¯ããçæããŸãã第äžåŒæ° metadata ã«ã¯ rego.metadata.chain() ãã第äºåŒæ° details ã«ã¯ result.location ãçæãããªããžã§ã¯ããæå®ããŸãã result.location(x) # METADATA # description: | # returns a "normalized" location object from the location value found in the AST. # new code should most often use one of the ranged_ location functions instea, as # that will also include an `"end"` location attribute # scope: document location(x) := ... fail ã®ç¬¬äºåŒæ°ã«æž¡ããªããžã§ã¯ããçæããŸãã第äžåŒæ° x ã«ã¯ input ã®ãã¡ã«ãŒã«éåãçºèŠãããèŠçŽ ãæå®ããŸãã result.aggregate(chain, aggregate_data) # METADATA # description: | # The result.aggregate function works similarly to `result.fail`, but instead of producing # a violation returns an entry to be aggregated for later evaluation. This is useful in # aggregate rules (and only in aggregate rules) as it provides a uniform format for # aggregate data entries. Example return value: # # { # "rule": { # "category": "testing", # "title": "aggregation", # }, # "aggregate_source": { # "file": "policy.rego", # "package_path": ["a", "b", "c"], # }, # "aggregate_data": { # "foo": "bar", # "baz": [1, 2, 3], # }, # } # aggregate(chain, aggregate_data) := ... aggregate ã«ãŒã«ã®èŠçŽ ãšãªããªããžã§ã¯ããçæããŸãã第äžåŒæ° chain ã«ã¯ rego.metadata.chain ãã第äºåŒæ° aggregate_data ã«ã¯ä»»æã®ãªããžã§ã¯ããæå®ããŸãã regal.parse_module(filename, policy) Rego ã³ãŒãã®æœè±¡ æ§ææš ã Regal çšã«æé©åãããã®ãè¿ããŸãã第äžåŒæ° filename ã«ã¯ãã¡ã€ã«åã第äºåŒæ° policy ã«ã¯ Rego ã³ãŒããæå®ããŸãã ãã®è£å©é¢æ°ã¯äž»ã«ãã¹ãã§äœ¿çšããŸãããã®è£å©é¢æ°ã®æ»ãå€ã«å¯ŸããŠã«ã¹ã¿ã ã«ãŒã«ãæåŸ
ããçµæãçæããããã¹ãããŸãã rule.report == want with input as regal.parse_module(filename, policy) ãã¹ããå®è¡ãã ã«ã¹ã¿ã ã«ãŒã«ã®ãã¹ãã³ãŒããè©äŸ¡ããã«ã¯ä»¥äžã®ã³ãã³ããå®è¡ããŸãã regal test <path> ã«ã¹ã¿ã ã«ãŒã«ããã³ãã®ãã¹ãã³ãŒã㯠Regal ãæäŸããã©ã€ãã©ãª data.regal.* ã ã¹ããŒã schema.regal.ast ãå©çšããŠããŸãããã®ãã opa test ã³ãã³ãã§ã¯ãã¹ããå®è¡ã§ããŸããã ãããã« ãã®èšäºã§ã¯ Rego ã® Linter ã§ãã Regal ã玹ä»ããŸããã å人çã«ã¯ãããžã§ã¯ãã§ Rego ãæ¡çšããé㯠Regal ãäžç·ã«å°å
¥ããããšãåŒ·ãæšå¥šããŸããRego ã¯äºäŸãå°ãªããå®è£
çµéšãè±å¯ãªãšã³ãžãã¢ã¯ããŸãå€ããããŸãããRegal ãå°å
¥ããããšã§ Rego ã®çµéšã®æµ
ããšã³ãžãã¢ã§ããã¹ãã ã©ã¯ ãã£ã¹ã«åã£ãã³ãŒããæžãããšãã§ããŸãã ç§èªèº«ã OPA / Rego ã䜿ã£ãŠ Kubernetes ã® ãããã§ã¹ã ãã¡ã€ã«ã Terraform ã®æ§æãã¡ã€ã«ã«å¯Ÿããããªã·ãŒãæžãããšãããã®ã§ãããä»ãŸã§ã¯çµéšãæµ
ãææµã§ Rego ã³ãŒããæžããŠãããã以äžã®ãããªèª²é¡ããããŸããã ããããééãããã°ãéå¹ççãªåŠçãå«ãã³ãŒãã誀ã£ãŠæžããŠããŸãã ä»ã®ã¡ã³ããŒã«ãšã£ãŠå¯èªæ§ã®äœãã³ãŒããæžããŠããŸãã çŸåšã¯ãããžã§ã¯ãã« Regal ãå°å
¥ããŠãã¹ãã ã©ã¯ ãã£ã¹ãã³ãŒãã£ã³ã°èŠçŽãžã®æºæ ã匷å¶ããããšã§ Rego ã³ãŒãã®å質ãå¯èªæ§ã«é¢ãããããã®åé¡ãæ¹åã§ããŠããŸãã æåŸãŸã§ãèªã¿ããã ããããããšãããããŸããã åèè³æ Regal | Styra Documentation ç§ãã¡ã¯äžç·ã«åããŠããã仲éãåéããŠããŸãïŒ ã¯ã©ãŠãã¢ãŒããã¯ã å·çïŒ @shibata.takao ãã¬ãã¥ãŒïŒ Ishizawa Kento (@kent) ïŒ Shodo ã§å·çãããŸãã ïŒ ãã®èšäºã§ã¯ Regal v0.28.0 ã䜿çšããŸãã ↩ 3 幎åã«æžãããèšäºã®ããèšèŒå
容ã®äžéšã¯å€ããªã£ãŠããŸãïŒç¹ã«ææ³ãªã©ïŒãææ°ã®æ
å ±ã¯ å
¬åŒããã¥ã¡ã³ã ããåç
§ãã ããã ↩ Regal 㯠β çã®ãã仿§ãäºåãªã倿Žãããå¯èœæ§ããããŸãã ↩ å
¬åŒããã¥ã¡ã³ãã§ã¯ Homebrew , asdf , pkgsrc , Nix , mason.vim ã玹ä»ãããŠããŸãã ↩ Regal 㯠Rego ã³ãŒããæ§æçã«æ£ãã ã³ã³ãã€ã« å¯èœã§ããããšãåæãšããŠããŸãããã®ãã regal lint ã³ãã³ãã®å®è¡åã« opa check --strict ã³ãã³ãã§ Rego ã³ãŒããæ§æçã«æ£ãã ã³ã³ãã€ã« å¯èœãæ€èšŒããããšãæšå¥šãããŠããŸãã詳ãã㯠OPA Check and Strict Mode | Styra Documentation ããåç
§ãã ããã ↩ ã³ãŒãã®èªåä¿®æ£ãå®è¡ããéã¯ãäœãåé¡ãçºçããå Žåã«ç°¡åã«å€æŽãå
ã«æ»ãããããGit ãªã©ã® VCS ã䜿ã£ãŠå€æŽãã³ããããŸãã¯ã¹ã¿ãã·ã¥ããŠããããšãæšå¥šããŸãã ↩ regal fix ã³ãã³ãã®å®è¡çµæãèŠããš directory-package-mismatch , use-rego-v1 ã® 2 ã€ã«é¢ããä¿®æ£ãè¡ããããšåºåãããŠããŸãããå®éã«ã¯ directory-package-mismatch , opa-fmt , use-assignment-operator ã® 3 ã€ã«é¢ããä¿®æ£ãè¡ãããŠããŸãããªããããªã£ãŠãããã¯äžæã§ãã ↩ ã«ãŒã«ã capabilities ã«å¿ããŠæå¹åãŸãã¯ç¡å¹åãããããã«å®è£
ãããŠããå¿
èŠããããŸãã ↩