@sahu-01/openpaw 1.0.0
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/dist/chunk-MO4EEYFW.js +38 -0
- package/dist/dist-Z5EH6AMS-AR4I3RYI.js +233 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +231321 -0
- package/package.json +38 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
__require,
|
|
36
|
+
__commonJS,
|
|
37
|
+
__toESM
|
|
38
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./chunk-MO4EEYFW.js";
|
|
3
|
+
|
|
4
|
+
// ../migrate/dist/dist-Z5EH6AMS.js
|
|
5
|
+
import { randomBytes, createCipheriv, createDecipheriv, createHash } from "crypto";
|
|
6
|
+
import { readFile, writeFile, unlink, stat, mkdir } from "fs/promises";
|
|
7
|
+
import { join, dirname } from "path";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
var ALGORITHM = "aes-256-gcm";
|
|
11
|
+
var IV_LENGTH = 12;
|
|
12
|
+
var AUTH_TAG_LENGTH = 16;
|
|
13
|
+
var KEY_LENGTH = 32;
|
|
14
|
+
var CredentialTypeSchema = z.enum(["api_key", "oauth_token", "password", "certificate"]);
|
|
15
|
+
var CredentialSchema = z.object({
|
|
16
|
+
id: z.string(),
|
|
17
|
+
service: z.string(),
|
|
18
|
+
type: CredentialTypeSchema,
|
|
19
|
+
encryptedValue: z.string(),
|
|
20
|
+
createdAt: z.string(),
|
|
21
|
+
// ISO date string for JSON serialization
|
|
22
|
+
updatedAt: z.string()
|
|
23
|
+
});
|
|
24
|
+
var VaultStoreSchema = z.object({
|
|
25
|
+
version: z.literal(1),
|
|
26
|
+
credentials: z.array(CredentialSchema)
|
|
27
|
+
});
|
|
28
|
+
function generateCredentialId(service, type) {
|
|
29
|
+
const random = randomBytes(8).toString("hex");
|
|
30
|
+
const hash = createHash("sha256").update(`${service}:${type}:${Date.now()}:${random}`).digest("hex").slice(0, 8);
|
|
31
|
+
return `cred_${service}_${type}_${hash}`;
|
|
32
|
+
}
|
|
33
|
+
function parseCredentialId(id) {
|
|
34
|
+
const match = id.match(/^cred_([a-zA-Z0-9]+)_(.+)_([a-f0-9]{8})$/);
|
|
35
|
+
if (!match || !match[1] || !match[2] || !match[3]) return null;
|
|
36
|
+
return {
|
|
37
|
+
service: match[1],
|
|
38
|
+
type: match[2],
|
|
39
|
+
hash: match[3]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function generateMasterKey() {
|
|
43
|
+
return randomBytes(KEY_LENGTH);
|
|
44
|
+
}
|
|
45
|
+
async function deriveKeyFromPassword(password, salt) {
|
|
46
|
+
const { pbkdf2 } = await import("crypto");
|
|
47
|
+
const { promisify } = await import("util");
|
|
48
|
+
const pbkdf2Async = promisify(pbkdf2);
|
|
49
|
+
const actualSalt = salt ?? randomBytes(16);
|
|
50
|
+
const key = await pbkdf2Async(password, actualSalt, 1e5, KEY_LENGTH, "sha256");
|
|
51
|
+
return { key, salt: actualSalt };
|
|
52
|
+
}
|
|
53
|
+
function encrypt(plaintext, key) {
|
|
54
|
+
if (key.length !== KEY_LENGTH) {
|
|
55
|
+
throw new Error(`Key must be ${KEY_LENGTH} bytes for AES-256-GCM`);
|
|
56
|
+
}
|
|
57
|
+
const iv = randomBytes(IV_LENGTH);
|
|
58
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
59
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
60
|
+
const authTag = cipher.getAuthTag();
|
|
61
|
+
return Buffer.concat([iv, authTag, encrypted]).toString("base64");
|
|
62
|
+
}
|
|
63
|
+
function encryptDetailed(plaintext, key) {
|
|
64
|
+
if (key.length !== KEY_LENGTH) {
|
|
65
|
+
throw new Error(`Key must be ${KEY_LENGTH} bytes for AES-256-GCM`);
|
|
66
|
+
}
|
|
67
|
+
const iv = randomBytes(IV_LENGTH);
|
|
68
|
+
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
69
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
70
|
+
const authTag = cipher.getAuthTag();
|
|
71
|
+
return {
|
|
72
|
+
iv: iv.toString("base64"),
|
|
73
|
+
ciphertext: encrypted.toString("base64"),
|
|
74
|
+
tag: authTag.toString("base64")
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function decrypt(ciphertext, key) {
|
|
78
|
+
if (key.length !== KEY_LENGTH) {
|
|
79
|
+
throw new Error(`Key must be ${KEY_LENGTH} bytes for AES-256-GCM`);
|
|
80
|
+
}
|
|
81
|
+
const data = Buffer.from(ciphertext, "base64");
|
|
82
|
+
if (data.length < IV_LENGTH + AUTH_TAG_LENGTH) {
|
|
83
|
+
throw new Error("Invalid ciphertext: too short");
|
|
84
|
+
}
|
|
85
|
+
const iv = data.subarray(0, IV_LENGTH);
|
|
86
|
+
const authTag = data.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
|
|
87
|
+
const encrypted = data.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
|
|
88
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
89
|
+
decipher.setAuthTag(authTag);
|
|
90
|
+
return decipher.update(encrypted) + decipher.final("utf8");
|
|
91
|
+
}
|
|
92
|
+
function decryptDetailed(encResult, key) {
|
|
93
|
+
if (key.length !== KEY_LENGTH) {
|
|
94
|
+
throw new Error(`Key must be ${KEY_LENGTH} bytes for AES-256-GCM`);
|
|
95
|
+
}
|
|
96
|
+
const iv = Buffer.from(encResult.iv, "base64");
|
|
97
|
+
const authTag = Buffer.from(encResult.tag, "base64");
|
|
98
|
+
const encrypted = Buffer.from(encResult.ciphertext, "base64");
|
|
99
|
+
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
100
|
+
decipher.setAuthTag(authTag);
|
|
101
|
+
return decipher.update(encrypted) + decipher.final("utf8");
|
|
102
|
+
}
|
|
103
|
+
async function secureWipe(filePath) {
|
|
104
|
+
const fileStats = await stat(filePath);
|
|
105
|
+
for (let i = 0; i < 3; i++) {
|
|
106
|
+
const randomData = randomBytes(fileStats.size);
|
|
107
|
+
await writeFile(filePath, randomData);
|
|
108
|
+
}
|
|
109
|
+
await unlink(filePath);
|
|
110
|
+
}
|
|
111
|
+
function getDefaultVaultPath() {
|
|
112
|
+
return join(homedir(), ".openpaw", "vault.json");
|
|
113
|
+
}
|
|
114
|
+
var Vault = class {
|
|
115
|
+
store;
|
|
116
|
+
key;
|
|
117
|
+
vaultPath;
|
|
118
|
+
constructor(key, vaultPath) {
|
|
119
|
+
if (key.length !== KEY_LENGTH) {
|
|
120
|
+
throw new Error(`Key must be ${KEY_LENGTH} bytes for AES-256-GCM`);
|
|
121
|
+
}
|
|
122
|
+
this.key = key;
|
|
123
|
+
this.vaultPath = vaultPath ?? getDefaultVaultPath();
|
|
124
|
+
this.store = { version: 1, credentials: [] };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Load vault from disk
|
|
128
|
+
*/
|
|
129
|
+
async load() {
|
|
130
|
+
try {
|
|
131
|
+
const content = await readFile(this.vaultPath, "utf8");
|
|
132
|
+
const parsed = JSON.parse(content);
|
|
133
|
+
this.store = VaultStoreSchema.parse(parsed);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error.code === "ENOENT") {
|
|
136
|
+
this.store = { version: 1, credentials: [] };
|
|
137
|
+
} else {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Save vault to disk
|
|
144
|
+
*/
|
|
145
|
+
async save() {
|
|
146
|
+
const dir = dirname(this.vaultPath);
|
|
147
|
+
await mkdir(dir, { recursive: true });
|
|
148
|
+
await writeFile(this.vaultPath, JSON.stringify(this.store, null, 2), "utf8");
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Import a new credential into the vault
|
|
152
|
+
*/
|
|
153
|
+
async import(service, type, value) {
|
|
154
|
+
const id = generateCredentialId(service, type);
|
|
155
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
156
|
+
const encryptedValue = encrypt(value, this.key);
|
|
157
|
+
const credential = {
|
|
158
|
+
id,
|
|
159
|
+
service,
|
|
160
|
+
type,
|
|
161
|
+
encryptedValue,
|
|
162
|
+
createdAt: now,
|
|
163
|
+
updatedAt: now
|
|
164
|
+
};
|
|
165
|
+
this.store.credentials.push(credential);
|
|
166
|
+
await this.save();
|
|
167
|
+
return credential;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* List all credentials (without decrypted values)
|
|
171
|
+
*/
|
|
172
|
+
list() {
|
|
173
|
+
return this.store.credentials.map(({ encryptedValue, ...rest }) => rest);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get a credential by ID and decrypt its value
|
|
177
|
+
*/
|
|
178
|
+
get(id) {
|
|
179
|
+
const credential = this.store.credentials.find((c) => c.id === id);
|
|
180
|
+
if (!credential) return null;
|
|
181
|
+
const value = decrypt(credential.encryptedValue, this.key);
|
|
182
|
+
return { credential, value };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get credential by service and type
|
|
186
|
+
*/
|
|
187
|
+
getByService(service, type) {
|
|
188
|
+
const credential = this.store.credentials.find(
|
|
189
|
+
(c) => c.service === service && (type === void 0 || c.type === type)
|
|
190
|
+
);
|
|
191
|
+
if (!credential) return null;
|
|
192
|
+
const value = decrypt(credential.encryptedValue, this.key);
|
|
193
|
+
return { credential, value };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Delete a credential by ID
|
|
197
|
+
*/
|
|
198
|
+
async delete(id) {
|
|
199
|
+
const index = this.store.credentials.findIndex((c) => c.id === id);
|
|
200
|
+
if (index === -1) return false;
|
|
201
|
+
this.store.credentials.splice(index, 1);
|
|
202
|
+
await this.save();
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get the vault file path
|
|
207
|
+
*/
|
|
208
|
+
getPath() {
|
|
209
|
+
return this.vaultPath;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
async function createVault(key, vaultPath) {
|
|
213
|
+
const vault = new Vault(key, vaultPath);
|
|
214
|
+
await vault.load();
|
|
215
|
+
return vault;
|
|
216
|
+
}
|
|
217
|
+
export {
|
|
218
|
+
CredentialSchema,
|
|
219
|
+
CredentialTypeSchema,
|
|
220
|
+
Vault,
|
|
221
|
+
VaultStoreSchema,
|
|
222
|
+
createVault,
|
|
223
|
+
decrypt,
|
|
224
|
+
decryptDetailed,
|
|
225
|
+
deriveKeyFromPassword,
|
|
226
|
+
encrypt,
|
|
227
|
+
encryptDetailed,
|
|
228
|
+
generateCredentialId,
|
|
229
|
+
generateMasterKey,
|
|
230
|
+
getDefaultVaultPath,
|
|
231
|
+
parseCredentialId,
|
|
232
|
+
secureWipe
|
|
233
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|