@i4ctime/q-ring 0.16.0 → 0.16.2
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 +22 -2
- package/dist/{chunk-NCM5GHNW.js → chunk-KFILBHOY.js} +58 -7
- package/dist/chunk-KFILBHOY.js.map +1 -0
- package/dist/{chunk-5LFKBZ3Q.js → chunk-SQKTDYSO.js} +80 -7
- package/dist/chunk-SQKTDYSO.js.map +1 -0
- package/dist/dashboard-2MLSJZQR.js +658 -0
- package/dist/dashboard-2MLSJZQR.js.map +1 -0
- package/dist/dashboard-WVPR5BQO.js +658 -0
- package/dist/dashboard-WVPR5BQO.js.map +1 -0
- package/dist/index.js +96 -27
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +16 -6
- package/dist/mcp.js.map +1 -1
- package/package.json +12 -12
- package/dist/chunk-5LFKBZ3Q.js.map +0 -1
- package/dist/chunk-NCM5GHNW.js.map +0 -1
- package/dist/dashboard-A3GJQCJX.js +0 -658
- package/dist/dashboard-A3GJQCJX.js.map +0 -1
- package/dist/dashboard-R3FWTFFW.js +0 -658
- package/dist/dashboard-R3FWTFFW.js.map +0 -1
|
@@ -335,7 +335,8 @@ function isProcessAlive(pid) {
|
|
|
335
335
|
}
|
|
336
336
|
}
|
|
337
337
|
function withFileLock(name, fn, opts = {}) {
|
|
338
|
-
const
|
|
338
|
+
const baseDir = process.env.QRING_LOCK_DIR ?? (process.env.QRING_AUDIT_DIR ? join3(process.env.QRING_AUDIT_DIR, ".locks") : join3(homedir(), ".config", "q-ring"));
|
|
339
|
+
const lockDir = join3(baseDir, opts.dir ?? "locks");
|
|
339
340
|
mkdirSync(lockDir, { recursive: true, mode: 448 });
|
|
340
341
|
const safe = Buffer.from(name, "utf8").toString("base64url");
|
|
341
342
|
const lockPath = join3(lockDir, `${safe}.lock`);
|
|
@@ -577,6 +578,13 @@ function setAuditAgentLabel(label) {
|
|
|
577
578
|
function getAuditAgentLabel() {
|
|
578
579
|
return auditAgentLabel;
|
|
579
580
|
}
|
|
581
|
+
function sanitizeAuditText(value, max) {
|
|
582
|
+
const cleaned = Array.from(value).filter((ch) => {
|
|
583
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
584
|
+
return cp >= 32 && cp !== 127;
|
|
585
|
+
}).join("");
|
|
586
|
+
return cleaned.length > max ? `${cleaned.slice(0, max)}\u2026` : cleaned;
|
|
587
|
+
}
|
|
580
588
|
function getAuditDir() {
|
|
581
589
|
if (process.env.QRING_AUDIT_DIR) {
|
|
582
590
|
if (!existsSync2(process.env.QRING_AUDIT_DIR)) {
|
|
@@ -631,6 +639,10 @@ function logAudit(event) {
|
|
|
631
639
|
if (full.agent === void 0 && auditAgentLabel) {
|
|
632
640
|
full.agent = auditAgentLabel;
|
|
633
641
|
}
|
|
642
|
+
if (full.key !== void 0) full.key = sanitizeAuditText(full.key, 256);
|
|
643
|
+
if (full.detail !== void 0) full.detail = sanitizeAuditText(full.detail, 600);
|
|
644
|
+
if (full.scope !== void 0) full.scope = sanitizeAuditText(full.scope, 128);
|
|
645
|
+
if (full.env !== void 0) full.env = sanitizeAuditText(full.env, 128);
|
|
634
646
|
const line = JSON.stringify(full);
|
|
635
647
|
const path = getAuditPath();
|
|
636
648
|
appendFileSync(path, line + "\n", { mode: 384 });
|
|
@@ -643,7 +655,12 @@ function logAudit(event) {
|
|
|
643
655
|
},
|
|
644
656
|
{ timeoutMs: 5e3 }
|
|
645
657
|
);
|
|
646
|
-
} catch {
|
|
658
|
+
} catch (err) {
|
|
659
|
+
if (process.env.QRING_DEBUG) {
|
|
660
|
+
console.error(
|
|
661
|
+
`q-ring: audit event dropped (${event.action}${event.key ? ` ${event.key}` : ""}): ${err instanceof Error ? err.message : String(err)}`
|
|
662
|
+
);
|
|
663
|
+
}
|
|
647
664
|
}
|
|
648
665
|
}
|
|
649
666
|
var MAX_AUDIT_BYTES = 12 * 1024 * 1024;
|
|
@@ -762,13 +779,33 @@ function exportAudit(opts = {}) {
|
|
|
762
779
|
const until = new Date(opts.until).getTime();
|
|
763
780
|
events = events.filter((e) => new Date(e.timestamp).getTime() <= until);
|
|
764
781
|
}
|
|
782
|
+
if (opts.excludeActions?.length) {
|
|
783
|
+
const excluded = new Set(opts.excludeActions);
|
|
784
|
+
events = events.filter((e) => !excluded.has(e.action));
|
|
785
|
+
}
|
|
765
786
|
if (opts.format === "json") {
|
|
766
787
|
return JSON.stringify(events, null, 2);
|
|
767
788
|
}
|
|
768
789
|
if (opts.format === "csv") {
|
|
790
|
+
const csvField = (v) => {
|
|
791
|
+
let s = v === void 0 ? "" : String(v);
|
|
792
|
+
if (/^[=+\-@\t\r]/.test(s)) s = `'${s}`;
|
|
793
|
+
return `"${s.replace(/"/g, '""')}"`;
|
|
794
|
+
};
|
|
769
795
|
const header = "timestamp,action,key,scope,env,source,agent,pid,correlationId,detail";
|
|
770
796
|
const rows = events.map(
|
|
771
|
-
(e) =>
|
|
797
|
+
(e) => [
|
|
798
|
+
csvField(e.timestamp),
|
|
799
|
+
csvField(e.action),
|
|
800
|
+
csvField(e.key),
|
|
801
|
+
csvField(e.scope),
|
|
802
|
+
csvField(e.env),
|
|
803
|
+
csvField(e.source),
|
|
804
|
+
csvField(e.agent),
|
|
805
|
+
csvField(e.pid),
|
|
806
|
+
csvField(e.correlationId),
|
|
807
|
+
csvField(e.detail)
|
|
808
|
+
].join(",")
|
|
772
809
|
);
|
|
773
810
|
return [header, ...rows].join("\n");
|
|
774
811
|
}
|
|
@@ -1762,7 +1799,8 @@ function notifyUser(title, body) {
|
|
|
1762
1799
|
let args;
|
|
1763
1800
|
if (process.platform === "linux") {
|
|
1764
1801
|
command = "notify-send";
|
|
1765
|
-
|
|
1802
|
+
const pango = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1803
|
+
args = ["--app-name=q-ring", "--urgency=critical", pango(title), pango(body)];
|
|
1766
1804
|
} else if (process.platform === "darwin") {
|
|
1767
1805
|
command = "osascript";
|
|
1768
1806
|
const clean = (s) => s.replace(/["\\]/g, "");
|
|
@@ -1802,7 +1840,7 @@ function recordCanaryTrip(trip) {
|
|
|
1802
1840
|
scope: trip.scope,
|
|
1803
1841
|
env: trip.env,
|
|
1804
1842
|
source: trip.source,
|
|
1805
|
-
detail: `CANARY TRIPPED: honeytoken read via ${trip.source}`
|
|
1843
|
+
detail: `CANARY TRIPPED: ${trip.detail ?? `honeytoken read via ${trip.source}`}`
|
|
1806
1844
|
});
|
|
1807
1845
|
if (!notificationsEnabled()) return;
|
|
1808
1846
|
const now = Date.now();
|
|
@@ -2245,8 +2283,12 @@ function deleteSecret(key, opts = {}) {
|
|
|
2245
2283
|
for (const { service, scope } of scopes) {
|
|
2246
2284
|
const entry = new Entry(service, key);
|
|
2247
2285
|
try {
|
|
2286
|
+
const existing = readEnvelope(service, key);
|
|
2248
2287
|
if (entry.deleteCredential()) {
|
|
2249
2288
|
deleted = true;
|
|
2289
|
+
if (existing?.meta.canary) {
|
|
2290
|
+
recordCanaryTrip({ key, scope, source, detail: "canary deleted" });
|
|
2291
|
+
}
|
|
2250
2292
|
logAudit({ action: "delete", key, scope, source });
|
|
2251
2293
|
fireHooks({
|
|
2252
2294
|
action: "delete",
|
|
@@ -2262,6 +2304,27 @@ function deleteSecret(key, opts = {}) {
|
|
|
2262
2304
|
}
|
|
2263
2305
|
return deleted;
|
|
2264
2306
|
}
|
|
2307
|
+
function disarmCanary(key, opts = {}) {
|
|
2308
|
+
const scopes = resolveScope(opts);
|
|
2309
|
+
for (const { service, scope } of scopes) {
|
|
2310
|
+
const envelope = readEnvelope(service, key);
|
|
2311
|
+
if (!envelope) continue;
|
|
2312
|
+
if (!envelope.meta.canary) return false;
|
|
2313
|
+
delete envelope.meta.canary;
|
|
2314
|
+
delete envelope.meta.canaryFormat;
|
|
2315
|
+
envelope.meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2316
|
+
writeEnvelope(service, key, envelope);
|
|
2317
|
+
logAudit({
|
|
2318
|
+
action: "write",
|
|
2319
|
+
key,
|
|
2320
|
+
scope,
|
|
2321
|
+
source: opts.source ?? "cli",
|
|
2322
|
+
detail: "canary disarmed"
|
|
2323
|
+
});
|
|
2324
|
+
return true;
|
|
2325
|
+
}
|
|
2326
|
+
return false;
|
|
2327
|
+
}
|
|
2265
2328
|
function hasSecret(key, opts = {}) {
|
|
2266
2329
|
const source = opts.source ?? "cli";
|
|
2267
2330
|
if (source === "mcp") {
|
|
@@ -2365,6 +2428,9 @@ function exportSecrets(opts = {}) {
|
|
|
2365
2428
|
const value = collapseValue(entry.envelope, env);
|
|
2366
2429
|
if (value !== null) {
|
|
2367
2430
|
rawValues.set(entry.key, value);
|
|
2431
|
+
if (entry.envelope.meta.canary) {
|
|
2432
|
+
recordCanaryTrip({ key: entry.key, scope: entry.scope, env, source });
|
|
2433
|
+
}
|
|
2368
2434
|
}
|
|
2369
2435
|
}
|
|
2370
2436
|
}
|
|
@@ -2377,7 +2443,13 @@ function exportSecrets(opts = {}) {
|
|
|
2377
2443
|
console.warn(`Warning: skipped exporting ${key} due to template error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2378
2444
|
}
|
|
2379
2445
|
}
|
|
2380
|
-
|
|
2446
|
+
const exportedKeys = [...merged.keys()];
|
|
2447
|
+
const keyList = exportedKeys.length > 20 ? `${exportedKeys.slice(0, 20).join(",")} +${exportedKeys.length - 20} more` : exportedKeys.join(",");
|
|
2448
|
+
logAudit({
|
|
2449
|
+
action: "export",
|
|
2450
|
+
source,
|
|
2451
|
+
detail: `format=${format} keys=${keyList}`
|
|
2452
|
+
});
|
|
2381
2453
|
if (format === "json") {
|
|
2382
2454
|
const obj = {};
|
|
2383
2455
|
for (const [key, value] of merged) {
|
|
@@ -2701,6 +2773,7 @@ export {
|
|
|
2701
2773
|
getEnvelope,
|
|
2702
2774
|
setSecret,
|
|
2703
2775
|
deleteSecret,
|
|
2776
|
+
disarmCanary,
|
|
2704
2777
|
hasSecret,
|
|
2705
2778
|
listSecrets,
|
|
2706
2779
|
exportSecrets,
|
|
@@ -2716,4 +2789,4 @@ export {
|
|
|
2716
2789
|
forget,
|
|
2717
2790
|
clearMemory
|
|
2718
2791
|
};
|
|
2719
|
-
//# sourceMappingURL=chunk-
|
|
2792
|
+
//# sourceMappingURL=chunk-SQKTDYSO.js.map
|