installOne handles a single plugin: build a staging Registrar, call Install, run validateSelf, and on success commit to the live Registry / PluginRules. Any error means staged data is discarded.
(name string, p platform.Plugin, result *InstallResult)
| 111 | // Install, run validateSelf, and on success commit to the live |
| 112 | // Registry / PluginRules. Any error means staged data is discarded. |
| 113 | func installOne(name string, p platform.Plugin, result *InstallResult) error { |
| 114 | caps, capsErr := safeCallCapabilities(p) |
| 115 | if capsErr != nil { |
| 116 | return capsErr |
| 117 | } |
| 118 | |
| 119 | // FailurePolicy is a closed enum. An out-of-range value almost |
| 120 | // always means the plugin author shipped FailurePolicy(2)/etc. by |
| 121 | // mistake, and the host's switch on caps.FailurePolicy below would |
| 122 | // silently treat the unknown value as FailOpen — defeating the |
| 123 | // security boundary the policy was meant to express. Reject up |
| 124 | // front with ReasonInvalidCapability (classified as |
| 125 | // untrusted-config, so the abort is unconditional). |
| 126 | if caps.FailurePolicy != platform.FailOpen && caps.FailurePolicy != platform.FailClosed { |
| 127 | return &PluginInstallError{ |
| 128 | PluginName: name, |
| 129 | ReasonCode: ReasonInvalidCapability, |
| 130 | Reason: fmt.Sprintf("FailurePolicy=%d is not a recognised value (expected FailOpen or FailClosed)", |
| 131 | caps.FailurePolicy), |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | // Strict consistency check: Restricts=true must pair with |
| 136 | // FailClosed (design hard-constraint #6). |
| 137 | if caps.Restricts && caps.FailurePolicy != platform.FailClosed { |
| 138 | return &PluginInstallError{ |
| 139 | PluginName: name, |
| 140 | ReasonCode: ReasonRestrictsMismatch, |
| 141 | Reason: "Restricts=true requires FailurePolicy=FailClosed", |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | // Version compatibility check. Two distinct failure modes: |
| 146 | // |
| 147 | // 1. Parse error (constraint is malformed, e.g. ">=abc") |
| 148 | // -> ReasonInvalidCapability, classified as untrusted-config |
| 149 | // so the host aborts unconditionally. This is a plugin |
| 150 | // authoring bug; FailurePolicy must NOT mask it. |
| 151 | // |
| 152 | // 2. Legitimate version mismatch (constraint parses fine but |
| 153 | // current CLI does not satisfy it) |
| 154 | // -> ReasonCapabilityUnmet, honours FailurePolicy. A FailOpen |
| 155 | // plugin announcing ">=2.0" against a 1.x CLI is skipped |
| 156 | // with a warning; a FailClosed plugin aborts. |
| 157 | if ok, err := satisfiesRequiredCLIVersion(currentCLIVersion(), caps.RequiredCLIVersion); err != nil { |
| 158 | return &PluginInstallError{ |
| 159 | PluginName: name, |
| 160 | ReasonCode: ReasonInvalidCapability, |
| 161 | Reason: err.Error(), |
| 162 | } |
| 163 | } else if !ok { |
| 164 | return &PluginInstallError{ |
| 165 | PluginName: name, |
| 166 | ReasonCode: ReasonCapabilityUnmet, |
| 167 | Reason: fmt.Sprintf("CLI version %q does not satisfy plugin requirement %q", |
| 168 | currentCLIVersion(), caps.RequiredCLIVersion), |
| 169 | } |
| 170 | } |
no test coverage detected