@i4ctime/q-ring 0.15.0 → 0.16.1

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.
@@ -40,7 +40,9 @@ var SecretMetadataSchema = z.object({
40
40
  validationUrl: z.string().optional(),
41
41
  requiresApproval: z.boolean().optional(),
42
42
  jitProvider: z.string().optional(),
43
- jitExpiresAt: z.string().optional()
43
+ jitExpiresAt: z.string().optional(),
44
+ canary: z.boolean().optional(),
45
+ canaryFormat: z.string().optional()
44
46
  });
45
47
  var QuantumEnvelopeSchema = z.object({
46
48
  v: z.literal(1),
@@ -73,7 +75,9 @@ function createEnvelope(value, opts) {
73
75
  rotationPrefix: opts?.rotationPrefix,
74
76
  provider: opts?.provider,
75
77
  requiresApproval: opts?.requiresApproval,
76
- jitProvider: opts?.jitProvider
78
+ jitProvider: opts?.jitProvider,
79
+ canary: opts?.canary,
80
+ canaryFormat: opts?.canaryFormat
77
81
  }
78
82
  };
79
83
  }
@@ -561,6 +565,18 @@ function writeStoredAnchor(hash) {
561
565
  } catch {
562
566
  }
563
567
  }
568
+ var auditAgentLabel = null;
569
+ function setAuditAgentLabel(label) {
570
+ if (label === null) {
571
+ auditAgentLabel = null;
572
+ return;
573
+ }
574
+ const cleaned = label.replace(/[^\x20-\x7e]/g, "").trim().slice(0, 128);
575
+ auditAgentLabel = cleaned.length > 0 ? cleaned : null;
576
+ }
577
+ function getAuditAgentLabel() {
578
+ return auditAgentLabel;
579
+ }
564
580
  function getAuditDir() {
565
581
  if (process.env.QRING_AUDIT_DIR) {
566
582
  if (!existsSync2(process.env.QRING_AUDIT_DIR)) {
@@ -612,6 +628,9 @@ function logAudit(event) {
612
628
  pid: process.pid,
613
629
  prevHash
614
630
  };
631
+ if (full.agent === void 0 && auditAgentLabel) {
632
+ full.agent = auditAgentLabel;
633
+ }
615
634
  const line = JSON.stringify(full);
616
635
  const path = getAuditPath();
617
636
  appendFileSync(path, line + "\n", { mode: 384 });
@@ -656,6 +675,7 @@ function queryAudit(query = {}) {
656
675
  if (query.action) events = events.filter((e) => e.action === query.action);
657
676
  if (query.source) events = events.filter((e) => e.source === query.source);
658
677
  if (query.correlationId) events = events.filter((e) => e.correlationId === query.correlationId);
678
+ if (query.agent) events = events.filter((e) => e.agent === query.agent);
659
679
  if (query.since) {
660
680
  const since = new Date(query.since).getTime();
661
681
  events = events.filter((e) => new Date(e.timestamp).getTime() >= since);
@@ -746,9 +766,9 @@ function exportAudit(opts = {}) {
746
766
  return JSON.stringify(events, null, 2);
747
767
  }
748
768
  if (opts.format === "csv") {
749
- const header = "timestamp,action,key,scope,env,source,pid,correlationId,detail";
769
+ const header = "timestamp,action,key,scope,env,source,agent,pid,correlationId,detail";
750
770
  const rows = events.map(
751
- (e) => `${e.timestamp},${e.action},${e.key ?? ""},${e.scope ?? ""},${e.env ?? ""},${e.source},${e.pid},${e.correlationId ?? ""},${(e.detail ?? "").replace(/,/g, ";")}`
771
+ (e) => `${e.timestamp},${e.action},${e.key ?? ""},${e.scope ?? ""},${e.env ?? ""},${e.source},${(e.agent ?? "").replace(/,/g, ";")},${e.pid},${e.correlationId ?? ""},${(e.detail ?? "").replace(/,/g, ";")}`
752
772
  );
753
773
  return [header, ...rows].join("\n");
754
774
  }
@@ -1727,6 +1747,31 @@ function notifyApprovalRequested(key, source) {
1727
1747
  );
1728
1748
  }
1729
1749
 
1750
+ // src/core/canary-alert.ts
1751
+ var TRIP_THROTTLE_MS = 30 * 1e3;
1752
+ var lastAlerted = /* @__PURE__ */ new Map();
1753
+ function recordCanaryTrip(trip) {
1754
+ const agent = getAuditAgentLabel();
1755
+ logAudit({
1756
+ action: "canary",
1757
+ key: trip.key,
1758
+ scope: trip.scope,
1759
+ env: trip.env,
1760
+ source: trip.source,
1761
+ detail: `CANARY TRIPPED: honeytoken read via ${trip.source}`
1762
+ });
1763
+ if (!notificationsEnabled()) return;
1764
+ const now = Date.now();
1765
+ const last = lastAlerted.get(trip.key);
1766
+ if (last !== void 0 && now - last < TRIP_THROTTLE_MS) return;
1767
+ lastAlerted.set(trip.key, now);
1768
+ const who = agent ? `${trip.source} (${agent})` : trip.source;
1769
+ notifyUser(
1770
+ "q-ring: CANARY TRIPPED",
1771
+ `Honeytoken "${trip.key}" was read by ${who}. This credential is fake \u2014 but something reached for it. Investigate: qring audit --action canary`
1772
+ );
1773
+ }
1774
+
1730
1775
  // src/core/provision.ts
1731
1776
  import { execFileSync, spawnSync } from "child_process";
1732
1777
  import { z as z3 } from "zod";
@@ -2008,6 +2053,9 @@ function getSecret(key, opts = {}) {
2008
2053
  const latest = readEnvelope(service, key) ?? envelope;
2009
2054
  writeEnvelope(service, key, recordAccess(latest));
2010
2055
  logAudit({ action: "read", key, scope, env, source });
2056
+ if (envelope.meta.canary) {
2057
+ recordCanaryTrip({ key, scope, env, source });
2058
+ }
2011
2059
  }
2012
2060
  return value;
2013
2061
  }
@@ -2040,6 +2088,8 @@ function setSecret(key, value, opts = {}) {
2040
2088
  const prov = opts.provider ?? existing?.meta.provider;
2041
2089
  const reqApp = opts.requiresApproval ?? existing?.meta.requiresApproval;
2042
2090
  const jitProv = opts.jitProvider ?? existing?.meta.jitProvider;
2091
+ const canaryFlag = opts.canary ?? existing?.meta.canary;
2092
+ const canaryFmt = opts.canaryFormat ?? existing?.meta.canaryFormat;
2043
2093
  const mergedTags = opts.tags ?? existing?.meta.tags;
2044
2094
  const ttlForPolicy = opts.ttlSeconds ?? existing?.meta.ttlSeconds;
2045
2095
  const life = checkSecretLifecyclePolicy(
@@ -2067,7 +2117,9 @@ function setSecret(key, value, opts = {}) {
2067
2117
  rotationPrefix: rotPfx,
2068
2118
  provider: prov,
2069
2119
  requiresApproval: reqApp,
2070
- jitProvider: jitProv
2120
+ jitProvider: jitProv,
2121
+ canary: canaryFlag,
2122
+ canaryFormat: canaryFmt
2071
2123
  });
2072
2124
  } else {
2073
2125
  envelope = createEnvelope(value, {
@@ -2080,7 +2132,9 @@ function setSecret(key, value, opts = {}) {
2080
2132
  rotationPrefix: rotPfx,
2081
2133
  provider: prov,
2082
2134
  requiresApproval: reqApp,
2083
- jitProvider: jitProv
2135
+ jitProvider: jitProv,
2136
+ canary: canaryFlag,
2137
+ canaryFormat: canaryFmt
2084
2138
  });
2085
2139
  }
2086
2140
  if (existing) {
@@ -2572,6 +2626,7 @@ export {
2572
2626
  checkDecay,
2573
2627
  readProjectConfig,
2574
2628
  collapseEnvironment,
2629
+ setAuditAgentLabel,
2575
2630
  logAudit,
2576
2631
  queryAudit,
2577
2632
  verifyAuditChain,
@@ -2610,4 +2665,4 @@ export {
2610
2665
  listMemory,
2611
2666
  forget
2612
2667
  };
2613
- //# sourceMappingURL=chunk-TPPPTL4J.js.map
2668
+ //# sourceMappingURL=chunk-NCM5GHNW.js.map