repofence 0.1.6 → 0.1.8

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/README.md CHANGED
@@ -151,6 +151,7 @@ Step-by-step: **[docs/RELEASE.md](docs/RELEASE.md)** (`npm version`, `npm publis
151
151
  | `REPOFENCE_API_BASE_URL` | API host for the CLI (`auth`, `init`). Default normalizes to `…/api`. |
152
152
  | `REPOFENCE_MOCK` | Set to `1` for mock API responses (no network). |
153
153
  | `REPOFENCE_PACK_ID` | Pack id for `repofence init` (default: `core`). |
154
+ | `REPOFENCE_PUBLIC_KEY` | Optional. PEM or base64 public key to verify signed packs. If unset, the CLI uses its built-in public key. |
154
155
 
155
156
  ## Requirements
156
157
 
@@ -9,6 +9,7 @@ const os_1 = __importDefault(require("os"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const crypto_1 = __importDefault(require("crypto"));
11
11
  const tar_1 = __importDefault(require("tar"));
12
+ const pack_public_key_1 = require("./pack-public-key");
12
13
  const defaultCommandsDir = () => process.env.REPOFENCE_COMMANDS_DIR || path_1.default.join(os_1.default.homedir(), '.cursor', 'commands');
13
14
  const commandsDir = (baseDir) => baseDir || defaultCommandsDir();
14
15
  const manifestPath = (baseDir) => path_1.default.join(commandsDir(baseDir), '.repofence-manifest.json');
@@ -141,6 +142,19 @@ const installPack = async (pack, options = {}, baseDir) => {
141
142
  exports.installPack = installPack;
142
143
  const manifestFilePath = (baseDir) => manifestPath(baseDir);
143
144
  exports.manifestFilePath = manifestFilePath;
145
+ /** Standard base64 or base64url (some APIs / env pastes). */
146
+ const decodeBase64Flexible = (input) => {
147
+ const trimmed = input.trim().replace(/\s/g, '');
148
+ if (!trimmed) {
149
+ throw new Error('Cadena vacía.');
150
+ }
151
+ const b64 = trimmed.includes('-') || trimmed.includes('_')
152
+ ? trimmed.replace(/-/g, '+').replace(/_/g, '/')
153
+ : trimmed;
154
+ const pad = b64.length % 4;
155
+ const padded = pad ? b64 + '='.repeat(4 - pad) : b64;
156
+ return Buffer.from(padded, 'base64');
157
+ };
144
158
  const validateSignature = async (_pack) => {
145
159
  const pack = _pack;
146
160
  // Si no hay firma ni hashes, no validamos (modo demo/mock).
@@ -151,16 +165,30 @@ const validateSignature = async (_pack) => {
151
165
  const payloadToVerify = pack.archiveHash
152
166
  ? `${pack.manifestHash}:${pack.archiveHash}`
153
167
  : pack.manifestHash;
154
- const publicKeyEnv = process.env.REPOFENCE_PUBLIC_KEY;
155
- if (!publicKeyEnv) {
156
- throw new Error('Falta REPOFENCE_PUBLIC_KEY para validar la firma del pack.');
157
- }
158
- const publicKeyPem = publicKeyEnv.includes('BEGIN')
159
- ? publicKeyEnv
160
- : Buffer.from(publicKeyEnv, 'base64').toString('utf8');
168
+ const rawKey = process.env.REPOFENCE_PUBLIC_KEY?.trim() || pack_public_key_1.EMBEDDED_PACK_PUBLIC_KEY_PEM;
169
+ const publicKeyPem = rawKey.includes('BEGIN')
170
+ ? rawKey
171
+ : Buffer.from(rawKey, 'base64').toString('utf8');
161
172
  try {
162
173
  const keyObject = crypto_1.default.createPublicKey(publicKeyPem);
163
- const isValid = crypto_1.default.verify(null, Buffer.from(payloadToVerify), keyObject, Buffer.from(pack.signature, 'base64'));
174
+ const sigBuf = decodeBase64Flexible(pack.signature);
175
+ const dataBuf = Buffer.from(payloadToVerify, 'utf8');
176
+ const keyType = keyObject.asymmetricKeyType;
177
+ let isValid;
178
+ if (keyType === 'ed25519') {
179
+ if (sigBuf.length !== 64) {
180
+ throw new Error(`Firma Ed25519: tras decodificar se esperaban 64 bytes, hay ${sigBuf.length}. ` +
181
+ 'Suele indicar que el API firmó con otro algoritmo (p. ej. RSA) o la firma está corrupta. ' +
182
+ 'El servidor debe usar el par Ed25519 que corresponde a la clave pública del CLI.');
183
+ }
184
+ isValid = crypto_1.default.verify(null, dataBuf, keyObject, sigBuf);
185
+ }
186
+ else if (keyType === 'rsa') {
187
+ isValid = crypto_1.default.verify('sha256', dataBuf, keyObject, sigBuf);
188
+ }
189
+ else {
190
+ throw new Error(`Tipo de clave pública no soportado: ${String(keyType)}`);
191
+ }
164
192
  if (!isValid) {
165
193
  throw new Error('Firma de pack inválida.');
166
194
  }
@@ -168,6 +196,10 @@ const validateSignature = async (_pack) => {
168
196
  }
169
197
  catch (error) {
170
198
  const msg = error instanceof Error ? error.message : 'error_validando_firma';
199
+ if (msg.includes('asn1') || msg.includes('DECODER') || msg.includes('wrong tag')) {
200
+ throw new Error(`${msg}. Comprueba que REPOFENCE_SIGNING_KEY en el API sea el par Ed25519 de public_key.pem ` +
201
+ '(o que REPOFENCE_PUBLIC_KEY en el cliente sea la clave pública que corresponde a la privada del servidor).');
202
+ }
171
203
  throw new Error(msg);
172
204
  }
173
205
  };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EMBEDDED_PACK_PUBLIC_KEY_PEM = void 0;
4
+ /**
5
+ * Default Ed25519 public key for verifying signed packs from the Repofence API.
6
+ * Must match `public_key.pem` in the repo (copy PEM lines verbatim from that file).
7
+ * Override with env REPOFENCE_PUBLIC_KEY for testing or key rotation.
8
+ */
9
+ exports.EMBEDDED_PACK_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
10
+ MCowBQYDK2VwAyEAJTfxc7baW85w7r3uxbYNuaZRMoe1e2lcva2l7MO0iak=
11
+ -----END PUBLIC KEY-----
12
+ `;
13
+ //# sourceMappingURL=pack-public-key.js.map
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "repofence",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Repofence CLI (packs + backend auth)",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
7
- "repofence": "dist/cli.js"
7
+ "repofence": "./dist/cli.js"
8
8
  },
9
9
  "files": [
10
10
  "dist/**/*.js",
@@ -20,7 +20,9 @@
20
20
  "start:cli": "node dist/cli.js",
21
21
  "dev": "ts-node src/cli.ts",
22
22
  "backend": "ts-node backend/server.ts",
23
- "backend:start": "node backend-dist/server.js"
23
+ "backend:start": "node backend-dist/server.js",
24
+ "inspect:signing": "ts-node scripts/inspect-signing-key.ts",
25
+ "verify:pack-sig": "ts-node scripts/verify-pack-signature-files.ts"
24
26
  },
25
27
  "keywords": [
26
28
  "sdd",