applyUserPolicyPruning resolves the user-layer Rule from plugin contributions and/or ~/.lark-cli/policy.yml and installs denyStubs for commands it rejects. Missing yaml is not an error -- the CLI runs with no user-layer restriction. A malformed Rule (bad MaxRisk enum, malformed glob, etc.) surfaces
(rootCmd *cobra.Command, pluginRules []cmdpolicy.PluginRule)
| 36 | // pluginRules carries Plugin.Restrict() contributions collected from |
| 37 | // the InstallAll phase; nil/empty is fine. |
| 38 | func applyUserPolicyPruning(rootCmd *cobra.Command, pluginRules []cmdpolicy.PluginRule) error { |
| 39 | // Plugin rules shadow the yaml source entirely (Resolve: plugin > |
| 40 | // yaml). When a plugin contributed rules we therefore do NOT even |
| 41 | // read ~/.lark-cli/policy.yml: build.go fail-CLOSES on any policy |
| 42 | // error once a plugin is present, so reading a malformed yaml here |
| 43 | // would let an unrelated broken file on the user's machine abort a |
| 44 | // plugin-governed binary -- exactly the file the plugin is supposed |
| 45 | // to shadow. Skipping the read keeps the shadow contract honest. |
| 46 | var ( |
| 47 | yamlRules []*platform.Rule |
| 48 | yamlPath string |
| 49 | ) |
| 50 | if len(pluginRules) == 0 { |
| 51 | p, perr := userPolicyPath() |
| 52 | if perr != nil { |
| 53 | // No user home dir means we cannot locate the policy. Treat |
| 54 | // the same as "file missing": no pruning, no error. This keeps |
| 55 | // non-interactive CI environments (no HOME set) running. |
| 56 | p = "" |
| 57 | } |
| 58 | yamlPath = p |
| 59 | loaded, lerr := cmdpolicy.LoadYAMLPolicy(yamlPath) |
| 60 | if lerr != nil { |
| 61 | // Yaml-only failures are fail-OPEN at the caller (warn and |
| 62 | // continue), but the active-policy snapshot is process-global |
| 63 | // and may still carry data from a previous build in long-lived |
| 64 | // embedders / tests. Clear it explicitly so `config policy |
| 65 | // show` reports "no policy" instead of a stale rule that |
| 66 | // doesn't reflect the current command tree. |
| 67 | cmdpolicy.SetActive(nil) |
| 68 | return lerr |
| 69 | } |
| 70 | yamlRules = loaded |
| 71 | } |
| 72 | |
| 73 | rules, source, err := cmdpolicy.Resolve(cmdpolicy.Sources{ |
| 74 | PluginRules: pluginRules, |
| 75 | YAMLRules: yamlRules, |
| 76 | YAMLPath: yamlPath, |
| 77 | }) |
| 78 | if err != nil { |
| 79 | cmdpolicy.SetActive(nil) |
| 80 | return err |
| 81 | } |
| 82 | if len(rules) == 0 { |
| 83 | cmdpolicy.SetActive(&cmdpolicy.ActivePolicy{Source: source}) |
| 84 | return nil |
| 85 | } |
| 86 | |
| 87 | // RuleName attributes a denial to a specific rule in the envelope. |
| 88 | // With a single rule that is unambiguous and preserves the legacy |
| 89 | // envelope verbatim; with several rules a denial means "no rule |
| 90 | // granted it", which has no single owner, so the field is left empty |
| 91 | // and reason_code=no_matching_rule carries the meaning instead. |
| 92 | ruleName := "" |
| 93 | if len(rules) == 1 { |
| 94 | ruleName = rules[0].Name |
| 95 | } |