@aryaminus/controlkeel 0.3.43 → 0.3.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/install.js CHANGED
@@ -163,6 +163,65 @@ async function verifyChecksum(filePath, asset) {
163
163
  }
164
164
  }
165
165
 
166
+ /**
167
+ * Verify a cosign keyless signature when cosign is available on PATH.
168
+ * Falls back gracefully when cosign is not installed.
169
+ */
170
+ async function verifySignature(filePath, asset, baseUrl) {
171
+ if (process.env.CONTROLKEEL_SKIP_SIGNATURE === "1") return;
172
+
173
+ const { execFileSync } = require("node:child_process");
174
+ const lookupCommand = process.platform === "win32" ? "where" : "command";
175
+ const lookupArgs = process.platform === "win32" ? ["cosign"] : ["-v", "cosign"];
176
+
177
+ let cosignPath;
178
+ try {
179
+ cosignPath = execFileSync(lookupCommand, lookupArgs, { encoding: "utf8", shell: false }).split(/\r?\n/)[0].trim();
180
+ } catch {
181
+ // cosign not available — checksum-only mode
182
+ return;
183
+ }
184
+
185
+ if (!cosignPath) return;
186
+
187
+ const repo = `${Buffer.from("YXJ5YW1pbnVzL2NvbnRyb2xrZWVs", "base64")}`;
188
+ const sigUrl = `${baseUrl}/${asset}.sig`;
189
+ const certUrl = `${baseUrl}/${asset}.pem`;
190
+ const sigFile = path.join(os.tmpdir(), `${asset}.sig`);
191
+ const certFile = path.join(os.tmpdir(), `${asset}.pem`);
192
+
193
+ await download(sigUrl, sigFile).catch(() => null);
194
+ await download(certUrl, certFile).catch(() => null);
195
+
196
+ if (!fs.existsSync(sigFile) || !fs.existsSync(certFile)) {
197
+ fs.rmSync(sigFile, { force: true });
198
+ fs.rmSync(certFile, { force: true });
199
+
200
+ if (process.env.CONTROLKEEL_REQUIRE_SIGNATURE === "1") {
201
+ throw new Error(`[controlkeel] No cosign signature/certificate available for ${asset}`);
202
+ }
203
+
204
+ return;
205
+ }
206
+
207
+ try {
208
+ execFileSync(cosignPath, [
209
+ "verify-blob", filePath,
210
+ "--signature", sigFile,
211
+ "--certificate", certFile,
212
+ "--certificate-identity-regexp", `^https://github.com/${repo}/.github/workflows/release.yml@refs/tags/v[0-9].*`,
213
+ "--certificate-oidc-issuer", "https://token.actions.githubusercontent.com"
214
+ ], { stdio: "pipe", timeout: 30000 });
215
+
216
+ console.log(`[controlkeel] Verified ${asset} signature (cosign keyless)`);
217
+ } catch (err) {
218
+ throw new Error(`[controlkeel] cosign signature verification failed for ${asset}`);
219
+ } finally {
220
+ fs.rmSync(sigFile, { force: true });
221
+ fs.rmSync(certFile, { force: true });
222
+ }
223
+ }
224
+
166
225
  async function ensureBinary({ forceDownload = false } = {}) {
167
226
  const destination = binaryPath();
168
227
 
@@ -177,6 +236,7 @@ async function ensureBinary({ forceDownload = false } = {}) {
177
236
 
178
237
  await download(url, tempPath);
179
238
  await verifyChecksum(tempPath, asset);
239
+ await verifySignature(tempPath, asset, releaseBaseUrl());
180
240
 
181
241
  fs.copyFileSync(tempPath, destination);
182
242
  fs.rmSync(tempPath, { force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aryaminus/controlkeel",
3
- "version": "0.3.43",
3
+ "version": "0.3.45",
4
4
  "description": "Bootstrap installer for the ControlKeel native CLI - a control plane for agent-generated software delivery.",
5
5
  "license": "Apache-2.0",
6
6
  "author": {
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/aryaminus/controlkeel.git",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.3.43",
10
+ "version": "0.3.45",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@aryaminus/controlkeel",
15
- "version": "0.3.43",
15
+ "version": "0.3.45",
16
16
  "runtimeHint": "npx",
17
17
  "transport": {
18
18
  "type": "stdio"