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 +1 -0
- package/dist/core/pack-manager.js +40 -8
- package/dist/core/pack-public-key.js +13 -0
- package/package.json +5 -3
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
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
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.
|
|
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",
|