@i4ctime/q-ring 0.9.7 → 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/{chunk-WG4ZKN7Q.js → chunk-JV4JFWV6.js} +94 -45
- package/dist/chunk-JV4JFWV6.js.map +1 -0
- package/dist/{chunk-5JBU7TWN.js → chunk-ZV7LRKBA.js} +94 -45
- package/dist/chunk-ZV7LRKBA.js.map +1 -0
- package/dist/{dashboard-JT5ZNLT5.js → dashboard-4H474M2N.js} +3 -3
- package/dist/{dashboard-Q5OQRQCX.js.map → dashboard-4H474M2N.js.map} +1 -1
- package/dist/{dashboard-Q5OQRQCX.js → dashboard-5L4ILERJ.js} +3 -3
- package/dist/{dashboard-JT5ZNLT5.js.map → dashboard-5L4ILERJ.js.map} +1 -1
- package/dist/index.js +44 -8
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +46 -12
- package/dist/mcp.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-5JBU7TWN.js.map +0 -1
- package/dist/chunk-WG4ZKN7Q.js.map +0 -1
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-
|
|
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) =>
|
|
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
|
|
1069
|
+
function deriveLegacyKey() {
|
|
1059
1070
|
const fingerprint = `qring-memory:${hostname()}:${userInfo().username}`;
|
|
1060
1071
|
return createHash("sha256").update(fingerprint).digest();
|
|
1061
1072
|
}
|
|
1062
|
-
function
|
|
1063
|
-
|
|
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
|
|
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
|
|
1221
|
-
|
|
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-
|
|
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.`);
|