@socialneuron/mcp-server 1.9.1 → 1.9.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/CHANGELOG.md +16 -1
- package/dist/http.js +122 -9
- package/dist/index.js +115 -8
- package/dist/sn.js +115 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to `@socialneuron/mcp-server` will be documented in this fil
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.9.2] - 2026-07-26
|
|
8
|
+
|
|
9
|
+
Security release. Upgrade is recommended for all users.
|
|
10
|
+
|
|
11
|
+
### Security
|
|
12
|
+
|
|
13
|
+
- **Prompt-injection scanner bypass via lookalike characters.** Instruction phrases written with Cyrillic or Greek characters that resemble Latin ones passed both existing scans and reached the model verbatim. Text is now also scanned in a confusable-folded form. Detection only — the folded form is never used for output, which would corrupt legitimate non-Latin text.
|
|
14
|
+
- **Secret redaction failed open on common credential formats.** AWS access key IDs, Google API keys, Slack tokens, bearer tokens, and PEM private keys were not matched by the output scanner and could pass through unredacted. Patterns for each are now covered.
|
|
15
|
+
- **SSRF guard missed IPv4-mapped IPv6 addresses.** URL parsing canonicalizes an embedded IPv4 literal to hexadecimal, so `http://[::ffff:169.254.169.254]/` reached the guard in a form the blocklist did not match, leaving cloud metadata endpoints and private ranges reachable. Mapped addresses are now expanded before the blocklist is applied.
|
|
16
|
+
- **Scope attenuation could widen authority.** Requesting a narrower scope on the hosted endpoint used exact-string matching, so a token holding a parent scope that asked for one of its children matched nothing and was silently given its original scopes back. The request now resolves through the scope hierarchy and is refused when no requested scope is granted.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Instruction scanner no longer blocks ordinary copy.** "Forget everything you know about …" and similar phrasings were scored as prompt injection, discarding the entire tool result. The affected phrase now requires a qualifier such as "previous" or "above"; injection phrasings are unchanged.
|
|
21
|
+
|
|
7
22
|
## [1.9.1] - 2026-07-18
|
|
8
23
|
|
|
9
24
|
### Fixed
|
|
@@ -153,7 +168,7 @@ Released without changelog entries (see git history): OAuth connector flow + DCR
|
|
|
153
168
|
|
|
154
169
|
### Source
|
|
155
170
|
|
|
156
|
-
Anchored by
|
|
171
|
+
Anchored by an internal pricing-consistency audit that aligned pricing, currency handling, and the published comparison data — this release ships the corresponding MCP-protocol-level fix.
|
|
157
172
|
|
|
158
173
|
## [1.7.6] - 2026-04-22
|
|
159
174
|
|
package/dist/http.js
CHANGED
|
@@ -2264,26 +2264,35 @@ function applyToolProfile(server, profile) {
|
|
|
2264
2264
|
var constants_default = {
|
|
2265
2265
|
ZERO_WIDTH_CHARS: "[\\u200B\\u200C\\u200D\\u2060\\uFEFF\\u{E0020}-\\u{E007F}]",
|
|
2266
2266
|
INSTRUCTION_PHRASES: [
|
|
2267
|
-
"(?:^|[^A-Za-z])ignore (?:all|previous|prior)
|
|
2268
|
-
"(?:^|[^A-Za-z])disregard (?:the
|
|
2269
|
-
"(?:^|[^A-Za-z])forget (?:all|previous|prior|
|
|
2267
|
+
"(?:^|[^A-Za-z])ignore (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*instructions",
|
|
2268
|
+
"(?:^|[^A-Za-z])disregard (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*(?:instructions|prompts?|context)",
|
|
2269
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*instructions",
|
|
2270
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:previous |prior |preceding |earlier |above )+(?:everything|context)",
|
|
2271
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:everything|context) (?:above|previously|prior|before|you (?:were|have been))",
|
|
2270
2272
|
"(?:^|[^A-Za-z])you are now (?:a |an )",
|
|
2271
2273
|
"(?:^|[^A-Za-z])system:\\s",
|
|
2272
2274
|
"<\\|im_start\\|>",
|
|
2273
2275
|
"<\\|im_end\\|>",
|
|
2274
2276
|
"</?im_(?:start|end)>",
|
|
2275
2277
|
"<\\|assistant\\|>",
|
|
2276
|
-
"(?:^|[^A-Za-z])\\[INST\\]"
|
|
2278
|
+
"(?:^|[^A-Za-z])\\[INST\\]",
|
|
2279
|
+
"(?:^|[^A-Za-z])disregard (?:the|all) (?:above|prior|previous)",
|
|
2280
|
+
"(?:^|[^A-Za-z])forget (?:all|previous|prior|the above)"
|
|
2277
2281
|
],
|
|
2278
2282
|
PII_PATTERNS: {
|
|
2279
2283
|
email: "[\\w.+-]+@[\\w-]+\\.[\\w.-]+",
|
|
2280
2284
|
phone_us: "(?<!\\d|-)(?:\\+?1[-\\s.])?\\(?\\d{3}\\)?[-\\s.]?\\d{3}[-\\s.]?\\d{4}(?!\\d|-)",
|
|
2281
2285
|
phone_intl: "\\+\\d{1,3}[-\\s.]?\\d{1,4}[-\\s.]?\\d{1,4}[-\\s.]?\\d{4,}",
|
|
2282
2286
|
jwt: "eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+",
|
|
2283
|
-
api_key: "(?:sk|pk|snk|xoxb|ghp|github_pat)_[A-Za-z0-9_-]{16,}",
|
|
2287
|
+
api_key: "(?:sk|pk|snk|sno|xoxb|ghp|github_pat)_[A-Za-z0-9_-]{16,}",
|
|
2284
2288
|
credit_card: "(?<!\\d|-)(?:\\d{4}[ -]?){3}\\d{4}(?!\\d|-)",
|
|
2285
2289
|
ssn: "\\d{3}-\\d{2}-\\d{4}",
|
|
2286
|
-
ip: "(?<![\\d.])(?:\\d{1,3}\\.){3}\\d{1,3}(?![\\d.])"
|
|
2290
|
+
ip: "(?<![\\d.])(?:\\d{1,3}\\.){3}\\d{1,3}(?![\\d.])",
|
|
2291
|
+
aws_access_key: "AKIA[0-9A-Z]{16}",
|
|
2292
|
+
google_api_key: "AIza[0-9A-Za-z_\\-]{35}",
|
|
2293
|
+
slack_token: "xox[abprs]-[A-Za-z0-9-]{10,}|xapp-[0-9]-[A-Za-z0-9-]{10,}",
|
|
2294
|
+
bearer_token: "Bearer\\s+[A-Za-z0-9._~+/=-]{20,}",
|
|
2295
|
+
private_key_block: "-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----"
|
|
2287
2296
|
},
|
|
2288
2297
|
MAX_LENGTH: 1e4,
|
|
2289
2298
|
MAX_OUTPUT_LENGTH: 1e6
|
|
@@ -2304,6 +2313,83 @@ function normalize(text) {
|
|
|
2304
2313
|
out = out.replace(EXCESSIVE_WHITESPACE, " ");
|
|
2305
2314
|
return out;
|
|
2306
2315
|
}
|
|
2316
|
+
var CONFUSABLES = {
|
|
2317
|
+
// Cyrillic, lowercase
|
|
2318
|
+
\u0430: "a",
|
|
2319
|
+
\u0432: "b",
|
|
2320
|
+
\u0435: "e",
|
|
2321
|
+
\u0455: "s",
|
|
2322
|
+
\u0456: "i",
|
|
2323
|
+
\u0458: "j",
|
|
2324
|
+
\u043A: "k",
|
|
2325
|
+
\u043C: "m",
|
|
2326
|
+
\u043D: "h",
|
|
2327
|
+
\u043E: "o",
|
|
2328
|
+
\u0440: "p",
|
|
2329
|
+
\u0441: "c",
|
|
2330
|
+
\u0442: "t",
|
|
2331
|
+
\u0443: "y",
|
|
2332
|
+
\u0445: "x",
|
|
2333
|
+
"\u0501": "d",
|
|
2334
|
+
\u0451: "e",
|
|
2335
|
+
\u04BB: "h",
|
|
2336
|
+
\u0475: "v",
|
|
2337
|
+
"\u051B": "q",
|
|
2338
|
+
\u0461: "w",
|
|
2339
|
+
"\u04CF": "l",
|
|
2340
|
+
"\u051D": "w",
|
|
2341
|
+
// Cyrillic, uppercase
|
|
2342
|
+
\u0410: "A",
|
|
2343
|
+
\u0412: "B",
|
|
2344
|
+
\u0415: "E",
|
|
2345
|
+
\u0405: "S",
|
|
2346
|
+
\u0406: "I",
|
|
2347
|
+
\u0408: "J",
|
|
2348
|
+
\u041A: "K",
|
|
2349
|
+
\u041C: "M",
|
|
2350
|
+
\u041D: "H",
|
|
2351
|
+
\u041E: "O",
|
|
2352
|
+
\u0420: "P",
|
|
2353
|
+
\u0421: "C",
|
|
2354
|
+
\u0422: "T",
|
|
2355
|
+
\u0423: "Y",
|
|
2356
|
+
\u0425: "X",
|
|
2357
|
+
// Greek, lowercase
|
|
2358
|
+
\u03B1: "a",
|
|
2359
|
+
\u03B2: "b",
|
|
2360
|
+
\u03B5: "e",
|
|
2361
|
+
\u03B9: "i",
|
|
2362
|
+
\u03BA: "k",
|
|
2363
|
+
\u03BD: "v",
|
|
2364
|
+
\u03BF: "o",
|
|
2365
|
+
\u03C1: "p",
|
|
2366
|
+
\u03C4: "t",
|
|
2367
|
+
\u03C5: "u",
|
|
2368
|
+
\u03C7: "x",
|
|
2369
|
+
\u03F2: "c",
|
|
2370
|
+
\u03B3: "y",
|
|
2371
|
+
\u03B7: "n",
|
|
2372
|
+
// Greek, uppercase
|
|
2373
|
+
\u0391: "A",
|
|
2374
|
+
\u0392: "B",
|
|
2375
|
+
\u0395: "E",
|
|
2376
|
+
\u0396: "Z",
|
|
2377
|
+
\u0397: "H",
|
|
2378
|
+
\u0399: "I",
|
|
2379
|
+
\u039A: "K",
|
|
2380
|
+
\u039C: "M",
|
|
2381
|
+
\u039D: "N",
|
|
2382
|
+
\u039F: "O",
|
|
2383
|
+
\u03A1: "P",
|
|
2384
|
+
\u03A4: "T",
|
|
2385
|
+
\u03A5: "Y",
|
|
2386
|
+
\u03A7: "X"
|
|
2387
|
+
};
|
|
2388
|
+
var CONFUSABLE_RE = new RegExp(`[${Object.keys(CONFUSABLES).join("")}]`, "g");
|
|
2389
|
+
function skeleton(text) {
|
|
2390
|
+
if (typeof text !== "string") return "";
|
|
2391
|
+
return text.replace(CONFUSABLE_RE, (ch) => CONFUSABLES[ch] ?? ch);
|
|
2392
|
+
}
|
|
2307
2393
|
|
|
2308
2394
|
// src/lib/agent-harness/detectors/zeroWidth.ts
|
|
2309
2395
|
var ZERO_WIDTH_RE = new RegExp(CONSTANTS.ZERO_WIDTH_CHARS, "u");
|
|
@@ -2372,6 +2458,14 @@ function scan(text, options) {
|
|
|
2372
2458
|
flagged.add(ipNorm.pattern);
|
|
2373
2459
|
risk = Math.max(risk, 0.9);
|
|
2374
2460
|
}
|
|
2461
|
+
const skeletonText = skeleton(normalized);
|
|
2462
|
+
if (skeletonText !== normalized) {
|
|
2463
|
+
const ipSkel = detectInstructionPhrase(skeletonText);
|
|
2464
|
+
if (ipSkel.found) {
|
|
2465
|
+
flagged.add(ipSkel.pattern);
|
|
2466
|
+
risk = Math.max(risk, 0.9);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2375
2469
|
const flaggedArr = Array.from(flagged);
|
|
2376
2470
|
if ((options.mode === "block" || options.mode === "sanitize") && flaggedArr.length > 0) {
|
|
2377
2471
|
return { passed: false, risk_score: risk, flagged_patterns: flaggedArr, pii_redacted: false };
|
|
@@ -2509,7 +2603,7 @@ function checkRateLimit(category, key) {
|
|
|
2509
2603
|
init_supabase();
|
|
2510
2604
|
|
|
2511
2605
|
// src/lib/version.ts
|
|
2512
|
-
var MCP_VERSION = "1.9.
|
|
2606
|
+
var MCP_VERSION = "1.9.2";
|
|
2513
2607
|
|
|
2514
2608
|
// src/tools/ideation.ts
|
|
2515
2609
|
function asEnvelope(data) {
|
|
@@ -4203,9 +4297,22 @@ var BLOCKED_HOSTNAMES = [
|
|
|
4203
4297
|
];
|
|
4204
4298
|
var ALLOWED_PROTOCOLS = ["http:", "https:"];
|
|
4205
4299
|
var BLOCKED_PORTS = [22, 23, 25, 110, 143, 445, 3306, 5432, 6379, 27017, 11211];
|
|
4300
|
+
function expandIPv4Mapped(ip) {
|
|
4301
|
+
const dotted = /^::ffff:((?:\d{1,3}\.){3}\d{1,3})$/i.exec(ip);
|
|
4302
|
+
if (dotted) return dotted[1];
|
|
4303
|
+
const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i.exec(ip);
|
|
4304
|
+
if (hex) {
|
|
4305
|
+
const high = parseInt(hex[1], 16);
|
|
4306
|
+
const low = parseInt(hex[2], 16);
|
|
4307
|
+
return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
|
|
4308
|
+
}
|
|
4309
|
+
return null;
|
|
4310
|
+
}
|
|
4206
4311
|
function isBlockedIP(ip) {
|
|
4207
4312
|
const normalized = ip.replace(/^\[/, "").replace(/\]$/, "");
|
|
4208
4313
|
if (normalized.includes(":")) {
|
|
4314
|
+
const mapped = expandIPv4Mapped(normalized);
|
|
4315
|
+
if (mapped) return BLOCKED_IP_PATTERNS.some((pattern) => pattern.test(mapped));
|
|
4209
4316
|
return BLOCKED_IPV6_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
4210
4317
|
}
|
|
4211
4318
|
return BLOCKED_IP_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
@@ -19336,8 +19443,14 @@ async function authenticateRequest(req, res, next) {
|
|
|
19336
19443
|
const scopeParam = req.query.scope;
|
|
19337
19444
|
if (scopeParam) {
|
|
19338
19445
|
const requestedScopes = scopeParam.split(",").map((s) => s.trim()).filter(Boolean);
|
|
19339
|
-
scopes = requestedScopes.filter((s) => authInfo.scopes
|
|
19340
|
-
if (scopes.length === 0)
|
|
19446
|
+
scopes = requestedScopes.filter((s) => hasScope(authInfo.scopes, s));
|
|
19447
|
+
if (scopes.length === 0) {
|
|
19448
|
+
res.status(403).json({
|
|
19449
|
+
error: "insufficient_scope",
|
|
19450
|
+
error_description: "None of the requested scopes are granted by this token."
|
|
19451
|
+
});
|
|
19452
|
+
return;
|
|
19453
|
+
}
|
|
19341
19454
|
}
|
|
19342
19455
|
req.auth = {
|
|
19343
19456
|
userId: authInfo.extra?.userId ?? authInfo.clientId,
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ var MCP_VERSION;
|
|
|
19
19
|
var init_version = __esm({
|
|
20
20
|
"src/lib/version.ts"() {
|
|
21
21
|
"use strict";
|
|
22
|
-
MCP_VERSION = "1.9.
|
|
22
|
+
MCP_VERSION = "1.9.2";
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -2285,26 +2285,35 @@ var init_constants = __esm({
|
|
|
2285
2285
|
constants_default = {
|
|
2286
2286
|
ZERO_WIDTH_CHARS: "[\\u200B\\u200C\\u200D\\u2060\\uFEFF\\u{E0020}-\\u{E007F}]",
|
|
2287
2287
|
INSTRUCTION_PHRASES: [
|
|
2288
|
-
"(?:^|[^A-Za-z])ignore (?:all|previous|prior)
|
|
2289
|
-
"(?:^|[^A-Za-z])disregard (?:the
|
|
2290
|
-
"(?:^|[^A-Za-z])forget (?:all|previous|prior|
|
|
2288
|
+
"(?:^|[^A-Za-z])ignore (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*instructions",
|
|
2289
|
+
"(?:^|[^A-Za-z])disregard (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*(?:instructions|prompts?|context)",
|
|
2290
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*instructions",
|
|
2291
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:previous |prior |preceding |earlier |above )+(?:everything|context)",
|
|
2292
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:everything|context) (?:above|previously|prior|before|you (?:were|have been))",
|
|
2291
2293
|
"(?:^|[^A-Za-z])you are now (?:a |an )",
|
|
2292
2294
|
"(?:^|[^A-Za-z])system:\\s",
|
|
2293
2295
|
"<\\|im_start\\|>",
|
|
2294
2296
|
"<\\|im_end\\|>",
|
|
2295
2297
|
"</?im_(?:start|end)>",
|
|
2296
2298
|
"<\\|assistant\\|>",
|
|
2297
|
-
"(?:^|[^A-Za-z])\\[INST\\]"
|
|
2299
|
+
"(?:^|[^A-Za-z])\\[INST\\]",
|
|
2300
|
+
"(?:^|[^A-Za-z])disregard (?:the|all) (?:above|prior|previous)",
|
|
2301
|
+
"(?:^|[^A-Za-z])forget (?:all|previous|prior|the above)"
|
|
2298
2302
|
],
|
|
2299
2303
|
PII_PATTERNS: {
|
|
2300
2304
|
email: "[\\w.+-]+@[\\w-]+\\.[\\w.-]+",
|
|
2301
2305
|
phone_us: "(?<!\\d|-)(?:\\+?1[-\\s.])?\\(?\\d{3}\\)?[-\\s.]?\\d{3}[-\\s.]?\\d{4}(?!\\d|-)",
|
|
2302
2306
|
phone_intl: "\\+\\d{1,3}[-\\s.]?\\d{1,4}[-\\s.]?\\d{1,4}[-\\s.]?\\d{4,}",
|
|
2303
2307
|
jwt: "eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+",
|
|
2304
|
-
api_key: "(?:sk|pk|snk|xoxb|ghp|github_pat)_[A-Za-z0-9_-]{16,}",
|
|
2308
|
+
api_key: "(?:sk|pk|snk|sno|xoxb|ghp|github_pat)_[A-Za-z0-9_-]{16,}",
|
|
2305
2309
|
credit_card: "(?<!\\d|-)(?:\\d{4}[ -]?){3}\\d{4}(?!\\d|-)",
|
|
2306
2310
|
ssn: "\\d{3}-\\d{2}-\\d{4}",
|
|
2307
|
-
ip: "(?<![\\d.])(?:\\d{1,3}\\.){3}\\d{1,3}(?![\\d.])"
|
|
2311
|
+
ip: "(?<![\\d.])(?:\\d{1,3}\\.){3}\\d{1,3}(?![\\d.])",
|
|
2312
|
+
aws_access_key: "AKIA[0-9A-Z]{16}",
|
|
2313
|
+
google_api_key: "AIza[0-9A-Za-z_\\-]{35}",
|
|
2314
|
+
slack_token: "xox[abprs]-[A-Za-z0-9-]{10,}|xapp-[0-9]-[A-Za-z0-9-]{10,}",
|
|
2315
|
+
bearer_token: "Bearer\\s+[A-Za-z0-9._~+/=-]{20,}",
|
|
2316
|
+
private_key_block: "-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----"
|
|
2308
2317
|
},
|
|
2309
2318
|
MAX_LENGTH: 1e4,
|
|
2310
2319
|
MAX_OUTPUT_LENGTH: 1e6
|
|
@@ -2331,13 +2340,90 @@ function normalize(text) {
|
|
|
2331
2340
|
out = out.replace(EXCESSIVE_WHITESPACE, " ");
|
|
2332
2341
|
return out;
|
|
2333
2342
|
}
|
|
2334
|
-
|
|
2343
|
+
function skeleton(text) {
|
|
2344
|
+
if (typeof text !== "string") return "";
|
|
2345
|
+
return text.replace(CONFUSABLE_RE, (ch) => CONFUSABLES[ch] ?? ch);
|
|
2346
|
+
}
|
|
2347
|
+
var OVERRIDE_CHARS, HTML_COMMENT, EXCESSIVE_WHITESPACE, CONFUSABLES, CONFUSABLE_RE;
|
|
2335
2348
|
var init_normalize = __esm({
|
|
2336
2349
|
"src/lib/agent-harness/normalize.ts"() {
|
|
2337
2350
|
"use strict";
|
|
2338
2351
|
OVERRIDE_CHARS = /[--]/g;
|
|
2339
2352
|
HTML_COMMENT = /<!--[\s\S]*?-->/g;
|
|
2340
2353
|
EXCESSIVE_WHITESPACE = /\s{2,}/g;
|
|
2354
|
+
CONFUSABLES = {
|
|
2355
|
+
// Cyrillic, lowercase
|
|
2356
|
+
\u0430: "a",
|
|
2357
|
+
\u0432: "b",
|
|
2358
|
+
\u0435: "e",
|
|
2359
|
+
\u0455: "s",
|
|
2360
|
+
\u0456: "i",
|
|
2361
|
+
\u0458: "j",
|
|
2362
|
+
\u043A: "k",
|
|
2363
|
+
\u043C: "m",
|
|
2364
|
+
\u043D: "h",
|
|
2365
|
+
\u043E: "o",
|
|
2366
|
+
\u0440: "p",
|
|
2367
|
+
\u0441: "c",
|
|
2368
|
+
\u0442: "t",
|
|
2369
|
+
\u0443: "y",
|
|
2370
|
+
\u0445: "x",
|
|
2371
|
+
"\u0501": "d",
|
|
2372
|
+
\u0451: "e",
|
|
2373
|
+
\u04BB: "h",
|
|
2374
|
+
\u0475: "v",
|
|
2375
|
+
"\u051B": "q",
|
|
2376
|
+
\u0461: "w",
|
|
2377
|
+
"\u04CF": "l",
|
|
2378
|
+
"\u051D": "w",
|
|
2379
|
+
// Cyrillic, uppercase
|
|
2380
|
+
\u0410: "A",
|
|
2381
|
+
\u0412: "B",
|
|
2382
|
+
\u0415: "E",
|
|
2383
|
+
\u0405: "S",
|
|
2384
|
+
\u0406: "I",
|
|
2385
|
+
\u0408: "J",
|
|
2386
|
+
\u041A: "K",
|
|
2387
|
+
\u041C: "M",
|
|
2388
|
+
\u041D: "H",
|
|
2389
|
+
\u041E: "O",
|
|
2390
|
+
\u0420: "P",
|
|
2391
|
+
\u0421: "C",
|
|
2392
|
+
\u0422: "T",
|
|
2393
|
+
\u0423: "Y",
|
|
2394
|
+
\u0425: "X",
|
|
2395
|
+
// Greek, lowercase
|
|
2396
|
+
\u03B1: "a",
|
|
2397
|
+
\u03B2: "b",
|
|
2398
|
+
\u03B5: "e",
|
|
2399
|
+
\u03B9: "i",
|
|
2400
|
+
\u03BA: "k",
|
|
2401
|
+
\u03BD: "v",
|
|
2402
|
+
\u03BF: "o",
|
|
2403
|
+
\u03C1: "p",
|
|
2404
|
+
\u03C4: "t",
|
|
2405
|
+
\u03C5: "u",
|
|
2406
|
+
\u03C7: "x",
|
|
2407
|
+
\u03F2: "c",
|
|
2408
|
+
\u03B3: "y",
|
|
2409
|
+
\u03B7: "n",
|
|
2410
|
+
// Greek, uppercase
|
|
2411
|
+
\u0391: "A",
|
|
2412
|
+
\u0392: "B",
|
|
2413
|
+
\u0395: "E",
|
|
2414
|
+
\u0396: "Z",
|
|
2415
|
+
\u0397: "H",
|
|
2416
|
+
\u0399: "I",
|
|
2417
|
+
\u039A: "K",
|
|
2418
|
+
\u039C: "M",
|
|
2419
|
+
\u039D: "N",
|
|
2420
|
+
\u039F: "O",
|
|
2421
|
+
\u03A1: "P",
|
|
2422
|
+
\u03A4: "T",
|
|
2423
|
+
\u03A5: "Y",
|
|
2424
|
+
\u03A7: "X"
|
|
2425
|
+
};
|
|
2426
|
+
CONFUSABLE_RE = new RegExp(`[${Object.keys(CONFUSABLES).join("")}]`, "g");
|
|
2341
2427
|
}
|
|
2342
2428
|
});
|
|
2343
2429
|
|
|
@@ -2429,6 +2515,14 @@ function scan(text, options) {
|
|
|
2429
2515
|
flagged.add(ipNorm.pattern);
|
|
2430
2516
|
risk = Math.max(risk, 0.9);
|
|
2431
2517
|
}
|
|
2518
|
+
const skeletonText = skeleton(normalized);
|
|
2519
|
+
if (skeletonText !== normalized) {
|
|
2520
|
+
const ipSkel = detectInstructionPhrase(skeletonText);
|
|
2521
|
+
if (ipSkel.found) {
|
|
2522
|
+
flagged.add(ipSkel.pattern);
|
|
2523
|
+
risk = Math.max(risk, 0.9);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2432
2526
|
const flaggedArr = Array.from(flagged);
|
|
2433
2527
|
if ((options.mode === "block" || options.mode === "sanitize") && flaggedArr.length > 0) {
|
|
2434
2528
|
return { passed: false, risk_score: risk, flagged_patterns: flaggedArr, pii_redacted: false };
|
|
@@ -4211,9 +4305,22 @@ var init_content = __esm({
|
|
|
4211
4305
|
|
|
4212
4306
|
// src/lib/ssrf.ts
|
|
4213
4307
|
import { promises as dnsPromises } from "node:dns";
|
|
4308
|
+
function expandIPv4Mapped(ip) {
|
|
4309
|
+
const dotted = /^::ffff:((?:\d{1,3}\.){3}\d{1,3})$/i.exec(ip);
|
|
4310
|
+
if (dotted) return dotted[1];
|
|
4311
|
+
const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i.exec(ip);
|
|
4312
|
+
if (hex) {
|
|
4313
|
+
const high = parseInt(hex[1], 16);
|
|
4314
|
+
const low = parseInt(hex[2], 16);
|
|
4315
|
+
return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
|
|
4316
|
+
}
|
|
4317
|
+
return null;
|
|
4318
|
+
}
|
|
4214
4319
|
function isBlockedIP(ip) {
|
|
4215
4320
|
const normalized = ip.replace(/^\[/, "").replace(/\]$/, "");
|
|
4216
4321
|
if (normalized.includes(":")) {
|
|
4322
|
+
const mapped = expandIPv4Mapped(normalized);
|
|
4323
|
+
if (mapped) return BLOCKED_IP_PATTERNS.some((pattern) => pattern.test(mapped));
|
|
4217
4324
|
return BLOCKED_IPV6_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
4218
4325
|
}
|
|
4219
4326
|
return BLOCKED_IP_PATTERNS.some((pattern) => pattern.test(normalized));
|
package/dist/sn.js
CHANGED
|
@@ -19,7 +19,7 @@ var MCP_VERSION;
|
|
|
19
19
|
var init_version = __esm({
|
|
20
20
|
"src/lib/version.ts"() {
|
|
21
21
|
"use strict";
|
|
22
|
-
MCP_VERSION = "1.9.
|
|
22
|
+
MCP_VERSION = "1.9.2";
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -3688,26 +3688,35 @@ var init_constants = __esm({
|
|
|
3688
3688
|
constants_default = {
|
|
3689
3689
|
ZERO_WIDTH_CHARS: "[\\u200B\\u200C\\u200D\\u2060\\uFEFF\\u{E0020}-\\u{E007F}]",
|
|
3690
3690
|
INSTRUCTION_PHRASES: [
|
|
3691
|
-
"(?:^|[^A-Za-z])ignore (?:all|previous|prior)
|
|
3692
|
-
"(?:^|[^A-Za-z])disregard (?:the
|
|
3693
|
-
"(?:^|[^A-Za-z])forget (?:all|previous|prior|
|
|
3691
|
+
"(?:^|[^A-Za-z])ignore (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*instructions",
|
|
3692
|
+
"(?:^|[^A-Za-z])disregard (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*(?:instructions|prompts?|context)",
|
|
3693
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:previous |prior |preceding |earlier |above )*instructions",
|
|
3694
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:previous |prior |preceding |earlier |above )+(?:everything|context)",
|
|
3695
|
+
"(?:^|[^A-Za-z])forget (?:all |any |the )*(?:everything|context) (?:above|previously|prior|before|you (?:were|have been))",
|
|
3694
3696
|
"(?:^|[^A-Za-z])you are now (?:a |an )",
|
|
3695
3697
|
"(?:^|[^A-Za-z])system:\\s",
|
|
3696
3698
|
"<\\|im_start\\|>",
|
|
3697
3699
|
"<\\|im_end\\|>",
|
|
3698
3700
|
"</?im_(?:start|end)>",
|
|
3699
3701
|
"<\\|assistant\\|>",
|
|
3700
|
-
"(?:^|[^A-Za-z])\\[INST\\]"
|
|
3702
|
+
"(?:^|[^A-Za-z])\\[INST\\]",
|
|
3703
|
+
"(?:^|[^A-Za-z])disregard (?:the|all) (?:above|prior|previous)",
|
|
3704
|
+
"(?:^|[^A-Za-z])forget (?:all|previous|prior|the above)"
|
|
3701
3705
|
],
|
|
3702
3706
|
PII_PATTERNS: {
|
|
3703
3707
|
email: "[\\w.+-]+@[\\w-]+\\.[\\w.-]+",
|
|
3704
3708
|
phone_us: "(?<!\\d|-)(?:\\+?1[-\\s.])?\\(?\\d{3}\\)?[-\\s.]?\\d{3}[-\\s.]?\\d{4}(?!\\d|-)",
|
|
3705
3709
|
phone_intl: "\\+\\d{1,3}[-\\s.]?\\d{1,4}[-\\s.]?\\d{1,4}[-\\s.]?\\d{4,}",
|
|
3706
3710
|
jwt: "eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+",
|
|
3707
|
-
api_key: "(?:sk|pk|snk|xoxb|ghp|github_pat)_[A-Za-z0-9_-]{16,}",
|
|
3711
|
+
api_key: "(?:sk|pk|snk|sno|xoxb|ghp|github_pat)_[A-Za-z0-9_-]{16,}",
|
|
3708
3712
|
credit_card: "(?<!\\d|-)(?:\\d{4}[ -]?){3}\\d{4}(?!\\d|-)",
|
|
3709
3713
|
ssn: "\\d{3}-\\d{2}-\\d{4}",
|
|
3710
|
-
ip: "(?<![\\d.])(?:\\d{1,3}\\.){3}\\d{1,3}(?![\\d.])"
|
|
3714
|
+
ip: "(?<![\\d.])(?:\\d{1,3}\\.){3}\\d{1,3}(?![\\d.])",
|
|
3715
|
+
aws_access_key: "AKIA[0-9A-Z]{16}",
|
|
3716
|
+
google_api_key: "AIza[0-9A-Za-z_\\-]{35}",
|
|
3717
|
+
slack_token: "xox[abprs]-[A-Za-z0-9-]{10,}|xapp-[0-9]-[A-Za-z0-9-]{10,}",
|
|
3718
|
+
bearer_token: "Bearer\\s+[A-Za-z0-9._~+/=-]{20,}",
|
|
3719
|
+
private_key_block: "-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----"
|
|
3711
3720
|
},
|
|
3712
3721
|
MAX_LENGTH: 1e4,
|
|
3713
3722
|
MAX_OUTPUT_LENGTH: 1e6
|
|
@@ -3734,13 +3743,90 @@ function normalize(text) {
|
|
|
3734
3743
|
out = out.replace(EXCESSIVE_WHITESPACE, " ");
|
|
3735
3744
|
return out;
|
|
3736
3745
|
}
|
|
3737
|
-
|
|
3746
|
+
function skeleton(text) {
|
|
3747
|
+
if (typeof text !== "string") return "";
|
|
3748
|
+
return text.replace(CONFUSABLE_RE, (ch) => CONFUSABLES[ch] ?? ch);
|
|
3749
|
+
}
|
|
3750
|
+
var OVERRIDE_CHARS, HTML_COMMENT, EXCESSIVE_WHITESPACE, CONFUSABLES, CONFUSABLE_RE;
|
|
3738
3751
|
var init_normalize = __esm({
|
|
3739
3752
|
"src/lib/agent-harness/normalize.ts"() {
|
|
3740
3753
|
"use strict";
|
|
3741
3754
|
OVERRIDE_CHARS = /[--]/g;
|
|
3742
3755
|
HTML_COMMENT = /<!--[\s\S]*?-->/g;
|
|
3743
3756
|
EXCESSIVE_WHITESPACE = /\s{2,}/g;
|
|
3757
|
+
CONFUSABLES = {
|
|
3758
|
+
// Cyrillic, lowercase
|
|
3759
|
+
\u0430: "a",
|
|
3760
|
+
\u0432: "b",
|
|
3761
|
+
\u0435: "e",
|
|
3762
|
+
\u0455: "s",
|
|
3763
|
+
\u0456: "i",
|
|
3764
|
+
\u0458: "j",
|
|
3765
|
+
\u043A: "k",
|
|
3766
|
+
\u043C: "m",
|
|
3767
|
+
\u043D: "h",
|
|
3768
|
+
\u043E: "o",
|
|
3769
|
+
\u0440: "p",
|
|
3770
|
+
\u0441: "c",
|
|
3771
|
+
\u0442: "t",
|
|
3772
|
+
\u0443: "y",
|
|
3773
|
+
\u0445: "x",
|
|
3774
|
+
"\u0501": "d",
|
|
3775
|
+
\u0451: "e",
|
|
3776
|
+
\u04BB: "h",
|
|
3777
|
+
\u0475: "v",
|
|
3778
|
+
"\u051B": "q",
|
|
3779
|
+
\u0461: "w",
|
|
3780
|
+
"\u04CF": "l",
|
|
3781
|
+
"\u051D": "w",
|
|
3782
|
+
// Cyrillic, uppercase
|
|
3783
|
+
\u0410: "A",
|
|
3784
|
+
\u0412: "B",
|
|
3785
|
+
\u0415: "E",
|
|
3786
|
+
\u0405: "S",
|
|
3787
|
+
\u0406: "I",
|
|
3788
|
+
\u0408: "J",
|
|
3789
|
+
\u041A: "K",
|
|
3790
|
+
\u041C: "M",
|
|
3791
|
+
\u041D: "H",
|
|
3792
|
+
\u041E: "O",
|
|
3793
|
+
\u0420: "P",
|
|
3794
|
+
\u0421: "C",
|
|
3795
|
+
\u0422: "T",
|
|
3796
|
+
\u0423: "Y",
|
|
3797
|
+
\u0425: "X",
|
|
3798
|
+
// Greek, lowercase
|
|
3799
|
+
\u03B1: "a",
|
|
3800
|
+
\u03B2: "b",
|
|
3801
|
+
\u03B5: "e",
|
|
3802
|
+
\u03B9: "i",
|
|
3803
|
+
\u03BA: "k",
|
|
3804
|
+
\u03BD: "v",
|
|
3805
|
+
\u03BF: "o",
|
|
3806
|
+
\u03C1: "p",
|
|
3807
|
+
\u03C4: "t",
|
|
3808
|
+
\u03C5: "u",
|
|
3809
|
+
\u03C7: "x",
|
|
3810
|
+
\u03F2: "c",
|
|
3811
|
+
\u03B3: "y",
|
|
3812
|
+
\u03B7: "n",
|
|
3813
|
+
// Greek, uppercase
|
|
3814
|
+
\u0391: "A",
|
|
3815
|
+
\u0392: "B",
|
|
3816
|
+
\u0395: "E",
|
|
3817
|
+
\u0396: "Z",
|
|
3818
|
+
\u0397: "H",
|
|
3819
|
+
\u0399: "I",
|
|
3820
|
+
\u039A: "K",
|
|
3821
|
+
\u039C: "M",
|
|
3822
|
+
\u039D: "N",
|
|
3823
|
+
\u039F: "O",
|
|
3824
|
+
\u03A1: "P",
|
|
3825
|
+
\u03A4: "T",
|
|
3826
|
+
\u03A5: "Y",
|
|
3827
|
+
\u03A7: "X"
|
|
3828
|
+
};
|
|
3829
|
+
CONFUSABLE_RE = new RegExp(`[${Object.keys(CONFUSABLES).join("")}]`, "g");
|
|
3744
3830
|
}
|
|
3745
3831
|
});
|
|
3746
3832
|
|
|
@@ -3832,6 +3918,14 @@ function scan(text, options) {
|
|
|
3832
3918
|
flagged.add(ipNorm.pattern);
|
|
3833
3919
|
risk = Math.max(risk, 0.9);
|
|
3834
3920
|
}
|
|
3921
|
+
const skeletonText = skeleton(normalized);
|
|
3922
|
+
if (skeletonText !== normalized) {
|
|
3923
|
+
const ipSkel = detectInstructionPhrase(skeletonText);
|
|
3924
|
+
if (ipSkel.found) {
|
|
3925
|
+
flagged.add(ipSkel.pattern);
|
|
3926
|
+
risk = Math.max(risk, 0.9);
|
|
3927
|
+
}
|
|
3928
|
+
}
|
|
3835
3929
|
const flaggedArr = Array.from(flagged);
|
|
3836
3930
|
if ((options.mode === "block" || options.mode === "sanitize") && flaggedArr.length > 0) {
|
|
3837
3931
|
return { passed: false, risk_score: risk, flagged_patterns: flaggedArr, pii_redacted: false };
|
|
@@ -5614,9 +5708,22 @@ var init_content2 = __esm({
|
|
|
5614
5708
|
|
|
5615
5709
|
// src/lib/ssrf.ts
|
|
5616
5710
|
import { promises as dnsPromises } from "node:dns";
|
|
5711
|
+
function expandIPv4Mapped(ip) {
|
|
5712
|
+
const dotted = /^::ffff:((?:\d{1,3}\.){3}\d{1,3})$/i.exec(ip);
|
|
5713
|
+
if (dotted) return dotted[1];
|
|
5714
|
+
const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i.exec(ip);
|
|
5715
|
+
if (hex) {
|
|
5716
|
+
const high = parseInt(hex[1], 16);
|
|
5717
|
+
const low = parseInt(hex[2], 16);
|
|
5718
|
+
return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
|
|
5719
|
+
}
|
|
5720
|
+
return null;
|
|
5721
|
+
}
|
|
5617
5722
|
function isBlockedIP(ip) {
|
|
5618
5723
|
const normalized = ip.replace(/^\[/, "").replace(/\]$/, "");
|
|
5619
5724
|
if (normalized.includes(":")) {
|
|
5725
|
+
const mapped = expandIPv4Mapped(normalized);
|
|
5726
|
+
if (mapped) return BLOCKED_IP_PATTERNS.some((pattern) => pattern.test(mapped));
|
|
5620
5727
|
return BLOCKED_IPV6_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
5621
5728
|
}
|
|
5622
5729
|
return BLOCKED_IP_PATTERNS.some((pattern) => pattern.test(normalized));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socialneuron/mcp-server",
|
|
3
3
|
"mcpName": "com.socialneuron/mcp-server",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.2",
|
|
5
5
|
"description": "Official MCP server, REST API & CLI for Social Neuron — create, schedule, and optimize social content with 35+ AI models across YouTube, TikTok, and more.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|