@i4ctime/q-ring 0.9.6 → 0.9.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/dist/mcp.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  checkDecay,
4
4
  checkExecPolicy,
5
5
  checkKeyReadPolicy,
6
+ checkSSRF,
6
7
  checkToolPolicy,
7
8
  collapseEnvironment,
8
9
  deleteSecret,
@@ -32,7 +33,7 @@ import {
32
33
  tunnelList,
33
34
  tunnelRead,
34
35
  verifyAuditChain
35
- } from "./chunk-5JBU7TWN.js";
36
+ } from "./chunk-ZV7LRKBA.js";
36
37
 
37
38
  // src/mcp.ts
38
39
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -411,7 +412,10 @@ async function execCommand(opts2) {
411
412
  throw new Error(`Policy Denied: ${policyDecision.reason}`);
412
413
  }
413
414
  if (profile.denyCommands) {
414
- const denied = profile.denyCommands.find((d) => fullCommand.includes(d));
415
+ const denied = profile.denyCommands.find((d) => {
416
+ const pattern = new RegExp(`(^|[\\s/])${d.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(\\s|$)`, "i");
417
+ return pattern.test(fullCommand);
418
+ });
415
419
  if (denied) {
416
420
  throw new Error(`Exec profile "${profile.name}" denies command containing "${denied}"`);
417
421
  }
@@ -900,6 +904,10 @@ var httpProvider = {
900
904
  if (!url) {
901
905
  return { valid: false, status: "unknown", message: "No validation URL configured", latencyMs: 0, provider: "http" };
902
906
  }
907
+ const ssrfBlock = await checkSSRF(url);
908
+ if (ssrfBlock) {
909
+ return { valid: false, status: "error", message: `SSRF blocked: ${ssrfBlock}`, latencyMs: Date.now() - start, provider: "http" };
910
+ }
903
911
  try {
904
912
  const { statusCode } = await makeRequest(url, {
905
913
  Authorization: `Bearer ${value}`,
@@ -1044,7 +1052,10 @@ import { existsSync as existsSync2, readFileSync as readFileSync4, writeFileSync
1044
1052
  import { join as join2 } from "path";
1045
1053
  import { homedir, hostname, userInfo } from "os";
1046
1054
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, createHash, randomBytes as randomBytes3 } from "crypto";
1055
+ import { Entry } from "@napi-rs/keyring";
1047
1056
  var MEMORY_FILE = "agent-memory.enc";
1057
+ var KEYRING_SERVICE = "qring-memory-key";
1058
+ var KEYRING_ACCOUNT = "encryption-key";
1048
1059
  function getMemoryDir() {
1049
1060
  const dir = join2(homedir(), ".config", "q-ring");
1050
1061
  if (!existsSync2(dir)) {
@@ -1055,29 +1066,54 @@ function getMemoryDir() {
1055
1066
  function getMemoryPath() {
1056
1067
  return join2(getMemoryDir(), MEMORY_FILE);
1057
1068
  }
1058
- function deriveKey2() {
1069
+ function deriveLegacyKey() {
1059
1070
  const fingerprint = `qring-memory:${hostname()}:${userInfo().username}`;
1060
1071
  return createHash("sha256").update(fingerprint).digest();
1061
1072
  }
1062
- function encrypt(data) {
1063
- const key = deriveKey2();
1073
+ function getOrCreateKey() {
1074
+ try {
1075
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
1076
+ const stored = entry.getPassword();
1077
+ if (stored) return Buffer.from(stored, "base64");
1078
+ const key = randomBytes3(32);
1079
+ entry.setPassword(key.toString("base64"));
1080
+ return key;
1081
+ } catch {
1082
+ console.warn("[q-ring] OS keyring unavailable for memory key \u2014 falling back to machine-derived key");
1083
+ return deriveLegacyKey();
1084
+ }
1085
+ }
1086
+ function encryptWith(data, key) {
1064
1087
  const iv = randomBytes3(12);
1065
1088
  const cipher = createCipheriv2("aes-256-gcm", key, iv);
1066
1089
  const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
1067
1090
  const tag = cipher.getAuthTag();
1068
1091
  return `${iv.toString("base64")}:${tag.toString("base64")}:${encrypted.toString("base64")}`;
1069
1092
  }
1070
- function decrypt(blob) {
1093
+ function decryptWith(blob, key) {
1071
1094
  const parts = blob.split(":");
1072
1095
  if (parts.length !== 3) throw new Error("Invalid encrypted format");
1073
1096
  const iv = Buffer.from(parts[0], "base64");
1074
1097
  const tag = Buffer.from(parts[1], "base64");
1075
1098
  const encrypted = Buffer.from(parts[2], "base64");
1076
- const key = deriveKey2();
1077
1099
  const decipher = createDecipheriv2("aes-256-gcm", key, iv);
1078
1100
  decipher.setAuthTag(tag);
1079
1101
  return decipher.update(encrypted) + decipher.final("utf8");
1080
1102
  }
1103
+ function encrypt(data) {
1104
+ return encryptWith(data, getOrCreateKey());
1105
+ }
1106
+ function decrypt(blob) {
1107
+ const key = getOrCreateKey();
1108
+ try {
1109
+ return decryptWith(blob, key);
1110
+ } catch {
1111
+ const legacy = deriveLegacyKey();
1112
+ const plain = decryptWith(blob, legacy);
1113
+ writeFileSync2(getMemoryPath(), encryptWith(plain, key), "utf8");
1114
+ return plain;
1115
+ }
1116
+ }
1081
1117
  function loadStore() {
1082
1118
  const path = getMemoryPath();
1083
1119
  if (!existsSync2(path)) {
@@ -1217,10 +1253,8 @@ function createMcpServer() {
1217
1253
  );
1218
1254
  }
1219
1255
  if (params.filter) {
1220
- const regex = new RegExp(
1221
- "^" + params.filter.replace(/\*/g, ".*") + "$",
1222
- "i"
1223
- );
1256
+ const escaped = params.filter.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1257
+ const regex = new RegExp("^" + escaped + "$", "i");
1224
1258
  entries = entries.filter((e) => regex.test(e.key));
1225
1259
  }
1226
1260
  if (entries.length === 0) return text("No secrets found");
@@ -2191,7 +2225,7 @@ ${result.stderr}`);
2191
2225
  if (dashboardInstance) {
2192
2226
  return text(`Dashboard already running at http://127.0.0.1:${dashboardInstance.port}`);
2193
2227
  }
2194
- const { startDashboardServer } = await import("./dashboard-Q5OQRQCX.js");
2228
+ const { startDashboardServer } = await import("./dashboard-4H474M2N.js");
2195
2229
  dashboardInstance = startDashboardServer({ port: params.port });
2196
2230
  return text(`Dashboard started at http://127.0.0.1:${dashboardInstance.port}
2197
2231
  Open this URL in a browser to see live quantum status.`);