speclock 3.5.3 → 3.5.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speclock",
3
- "version": "3.5.3",
3
+ "version": "3.5.4",
4
4
  "description": "AI constraint engine with Policy-as-Code DSL, OAuth/OIDC SSO, admin dashboard, telemetry, API key auth, RBAC, AES-256-GCM encryption, hard enforcement, semantic pre-commit, HMAC audit chain, SOC 2/HIPAA compliance. 100% detection, 0% false positives. 31 MCP tools + CLI. Enterprise platform.",
5
5
  "type": "module",
6
6
  "main": "src/mcp/server.js",
@@ -9,7 +9,7 @@
9
9
  import { readBrain, readEvents } from "./storage.js";
10
10
  import { verifyAuditChain } from "./audit.js";
11
11
 
12
- const VERSION = "3.5.3";
12
+ const VERSION = "3.5.4";
13
13
 
14
14
  // PHI-related keywords for HIPAA filtering
15
15
  const PHI_KEYWORDS = [
@@ -5,7 +5,7 @@
5
5
  // ===================================================================
6
6
 
7
7
  // ===================================================================
8
- // SYNONYM GROUPS (55 groups)
8
+ // SYNONYM GROUPS (75+ groups)
9
9
  // Each group contains words/phrases that are semantically equivalent.
10
10
  // ===================================================================
11
11
 
@@ -144,6 +144,40 @@ export const SYNONYM_GROUPS = [
144
144
  "archival", "preservation", "lifecycle"],
145
145
  ["consent", "user consent", "opt-in", "opt-out",
146
146
  "data subject", "right to erasure"],
147
+
148
+ // --- Logistics / Supply Chain ---
149
+ ["shipment", "shipping", "consignment", "delivery", "dispatch",
150
+ "freight", "cargo", "package", "parcel"],
151
+ ["manifest", "bill of lading", "shipping document", "waybill",
152
+ "consignment note", "packing list"],
153
+ ["warehouse", "fulfillment center", "distribution center",
154
+ "storage facility", "depot"],
155
+ ["carrier", "shipping provider", "logistics provider",
156
+ "transport company", "freight forwarder"],
157
+ ["eta", "estimated arrival", "delivery time", "arrival time",
158
+ "expected delivery"],
159
+ ["customs", "customs clearance", "import", "export",
160
+ "tariff", "duty", "declaration"],
161
+ ["tracking number", "tracking id", "shipment tracking",
162
+ "delivery tracking", "consignment tracking"],
163
+
164
+ // --- Travel / Booking ---
165
+ ["reservation", "booking", "appointment", "ticket",
166
+ "confirmation", "itinerary"],
167
+ ["passenger", "traveler", "guest", "visitor", "tourist"],
168
+ ["passport", "passport data", "travel document",
169
+ "identity document", "visa"],
170
+ ["flight", "airline", "aviation", "air travel"],
171
+ ["hotel", "accommodation", "lodging", "stay", "room"],
172
+
173
+ // --- E-commerce ---
174
+ ["checkout", "cart", "shopping cart", "basket",
175
+ "purchase flow", "buy flow"],
176
+ ["order", "purchase", "buy", "acquisition"],
177
+ ["product", "item", "sku", "merchandise", "product listing", "catalog"],
178
+ ["price", "pricing", "cost", "rate", "amount", "charge"],
179
+ ["coupon", "discount", "promo", "promotion", "voucher", "deal"],
180
+ ["inventory", "stock", "supply", "availability"],
147
181
  ];
148
182
 
149
183
  // ===================================================================
@@ -202,6 +236,37 @@ export const EUPHEMISM_MAP = {
202
236
  "work around": ["bypass", "circumvent"],
203
237
  "shortcut": ["bypass", "skip"],
204
238
 
239
+ // Financial / accounting euphemisms
240
+ "reconcile": ["modify", "adjust", "change", "alter"],
241
+ "reverse": ["undo", "revert", "modify", "change"],
242
+ "recalculate": ["modify", "change", "update", "alter"],
243
+ "backdate": ["modify", "tamper", "falsify", "change"],
244
+ "rebalance": ["modify", "adjust", "change", "redistribute"],
245
+ "reclassify": ["modify", "change", "recategorize"],
246
+ "redistribute": ["modify", "change", "move", "reallocate"],
247
+ "reallocate": ["modify", "change", "move"],
248
+ "write off": ["delete", "remove", "eliminate"],
249
+ "write down": ["modify", "reduce", "change"],
250
+ "void": ["delete", "cancel", "remove", "nullify"],
251
+ "post": ["modify", "change", "write", "record"],
252
+ "unpost": ["revert", "undo", "modify", "delete"],
253
+ "journal": ["modify", "record", "change"],
254
+ "accrue": ["modify", "add", "change"],
255
+ "amortize": ["modify", "reduce", "change"],
256
+ "depreciate": ["modify", "reduce", "change"],
257
+
258
+ // Logistics euphemisms
259
+ "reroute": ["modify", "change", "redirect"],
260
+ "divert": ["modify", "change", "redirect", "reroute"],
261
+ "reassign": ["modify", "change", "move", "transfer"],
262
+ "remanifest": ["modify", "change", "update"],
263
+ "deconsolidate": ["split", "separate", "modify"],
264
+
265
+ // Travel / booking euphemisms
266
+ "rebook": ["modify", "change", "replace", "cancel"],
267
+ "no-show": ["cancel", "void", "remove"],
268
+ "waitlist": ["modify", "change", "queue"],
269
+
205
270
  // Database euphemisms
206
271
  "truncate": ["delete", "remove", "wipe", "empty"],
207
272
  "vacuum": ["delete", "remove", "clean"],
@@ -285,7 +350,7 @@ export const CONCEPT_MAP = {
285
350
  "hipaa": ["phi", "patient data", "health information",
286
351
  "medical records", "compliance"],
287
352
 
288
- // Financial
353
+ // Financial / Fintech
289
354
  "pci": ["cardholder data", "payment data", "card data",
290
355
  "pci dss", "payment security"],
291
356
  "cardholder data": ["pci", "payment data", "card data", "credit card", "pan"],
@@ -293,6 +358,74 @@ export const CONCEPT_MAP = {
293
358
  "trade": ["executed trade", "trade record", "order", "position"],
294
359
  "executed trade": ["trade", "trade record", "order"],
295
360
  "trade record": ["trade", "executed trade", "transaction record"],
361
+ "transaction": ["payment", "transfer", "ledger entry", "posting",
362
+ "settlement", "billing", "charge", "balance"],
363
+ "ledger": ["transaction", "financial records", "accounting",
364
+ "general ledger", "journal", "balance sheet", "posting"],
365
+ "balance": ["account balance", "ledger", "transaction", "funds",
366
+ "financial records", "settlement"],
367
+ "account": ["balance", "ledger", "financial records", "customer account",
368
+ "account balance"],
369
+ "settlement": ["transaction", "payment", "clearing", "reconciliation",
370
+ "transfer"],
371
+ "fraud detection": ["fraud", "fraud prevention", "anti-fraud", "fraud monitoring",
372
+ "suspicious activity", "transaction monitoring"],
373
+ "fraud": ["fraud detection", "fraud prevention", "suspicious activity",
374
+ "anti-fraud"],
375
+ "posting": ["transaction", "ledger entry", "journal entry", "record"],
376
+ "reconciliation": ["balance", "ledger", "account", "transaction", "audit"],
377
+ "checkout": ["payment", "cart", "purchase", "transaction", "billing",
378
+ "payment processing", "order"],
379
+ "revenue": ["payment", "billing", "income", "sales", "earnings",
380
+ "transaction"],
381
+ "invoice": ["billing", "payment", "charge", "transaction", "accounts receivable"],
382
+
383
+ // Logistics / Supply Chain
384
+ "shipment": ["cargo", "freight", "consignment", "delivery", "package",
385
+ "manifest", "tracking", "shipping"],
386
+ "manifest": ["shipment", "cargo", "freight", "bill of lading",
387
+ "shipping document", "consignment"],
388
+ "cargo": ["shipment", "freight", "manifest", "consignment", "goods"],
389
+ "freight": ["shipment", "cargo", "manifest", "logistics"],
390
+ "delivery": ["shipment", "shipping", "tracking", "eta", "transit"],
391
+ "eta": ["delivery time", "estimated arrival", "timestamp",
392
+ "delivery", "tracking", "schedule"],
393
+ "warehouse": ["inventory", "stock", "storage", "fulfillment"],
394
+ "inventory": ["warehouse", "stock", "supply", "goods"],
395
+ "customs": ["import", "export", "tariff", "duty", "clearance",
396
+ "border", "declaration"],
397
+ "carrier": ["shipping provider", "logistics provider", "trucker",
398
+ "transport", "shipping company"],
399
+ "tracking": ["shipment tracking", "delivery tracking", "status",
400
+ "location", "transit"],
401
+ "bill of lading": ["manifest", "shipping document", "consignment note"],
402
+
403
+ // Travel / Booking
404
+ "reservation": ["booking", "appointment", "ticket", "confirmation",
405
+ "itinerary", "payment record"],
406
+ "booking": ["reservation", "appointment", "ticket", "itinerary",
407
+ "confirmation"],
408
+ "passenger": ["traveler", "guest", "customer", "pii", "personal data",
409
+ "user data", "passenger data"],
410
+ "itinerary": ["booking", "reservation", "travel plan", "route",
411
+ "schedule", "flight"],
412
+ "passport": ["pii", "personal data", "identity document", "travel document",
413
+ "passenger data"],
414
+ "passport data": ["pii", "personal data", "identity", "passenger",
415
+ "travel document"],
416
+ "flight": ["booking", "reservation", "itinerary", "travel"],
417
+ "hotel": ["booking", "reservation", "accommodation", "lodging"],
418
+ "rate limiting": ["throttle", "request limit", "api limit", "rate limit",
419
+ "quota", "access control"],
420
+
421
+ // E-commerce
422
+ "cart": ["checkout", "purchase", "shopping cart"],
423
+ "payment processing":["payment", "checkout", "billing", "transaction",
424
+ "stripe", "payment gateway"],
425
+ "payment gateway": ["payment processing", "stripe", "paypal", "checkout",
426
+ "billing", "transaction"],
427
+ "product": ["item", "sku", "catalog", "merchandise", "product listing"],
428
+ "price": ["pricing", "cost", "amount", "rate", "charge"],
296
429
 
297
430
  // Audit/logging
298
431
  "audit logging": ["audit log", "audit trail", "logging", "monitoring"],
@@ -326,6 +459,13 @@ export const CONCEPT_MAP = {
326
459
  "ban records": ["ban record", "banned users", "suspension records",
327
460
  "blocked users", "moderation records"],
328
461
 
462
+ // Approval / moderation concepts
463
+ "approve": ["moderation", "content review", "content moderation",
464
+ "review queue"],
465
+ "batch approve": ["bypass moderation", "skip review", "auto-approve",
466
+ "content moderation", "moderation"],
467
+ "approval queue": ["moderation", "review queue", "content review"],
468
+
329
469
  // Authentication/2FA
330
470
  "2fa": ["two-factor", "two factor authentication", "mfa",
331
471
  "multi-factor", "authentication", "auth"],
@@ -568,13 +708,30 @@ export function tokenize(text) {
568
708
  }
569
709
 
570
710
  // Extract single words (>= 2 chars)
571
- const words = lower
711
+ const rawWords = lower
572
712
  .replace(/[^a-z0-9\-\/&]+/g, " ")
573
713
  .split(/\s+/)
574
714
  .filter(w => w.length >= 2);
575
715
 
576
- const all = [...new Set([...phrases, ...words])];
577
- return { words, phrases, all };
716
+ // Basic plural normalization add both singular and plural forms
717
+ // so "databases" matches "database" and vice versa
718
+ const words = [...rawWords];
719
+ for (const w of rawWords) {
720
+ if (w.endsWith("ses") && w.length > 4) {
721
+ // "databases" → "database"
722
+ words.push(w.slice(0, -1));
723
+ } else if (w.endsWith("ies") && w.length > 4) {
724
+ // "entries" → "entry"
725
+ words.push(w.slice(0, -3) + "y");
726
+ } else if (w.endsWith("s") && !w.endsWith("ss") && !w.endsWith("us") && w.length > 3) {
727
+ // "records" → "record", "logs" → "log"
728
+ words.push(w.slice(0, -1));
729
+ }
730
+ }
731
+ const uniqueWords = [...new Set(words)];
732
+
733
+ const all = [...new Set([...phrases, ...uniqueWords])];
734
+ return { words: uniqueWords, phrases, all };
578
735
  }
579
736
 
580
737
  // ===================================================================
@@ -969,13 +1126,43 @@ export function scoreConflict({ actionText, lockText }) {
969
1126
  // However, subject-level synonyms like "content safety" → "CSAM detection"
970
1127
  // should still count as subject relevance (same concept, different words).
971
1128
  const ACTION_VERBS_SET = new Set([
972
- "modify", "change", "alter", "update", "delete", "remove", "add", "create",
973
- "disable", "enable", "replace", "swap", "switch", "move", "migrate",
974
- "install", "uninstall", "deploy", "rewrite", "revise", "restructure",
975
- "refactor", "clean", "purge", "wipe", "drop", "kill", "destroy",
976
- "reduce", "simplify", "fix", "repair", "restore", "recover", "break",
977
- "expose", "hide", "connect", "disconnect", "merge", "split", "truncate",
978
- "bypass", "skip", "ignore", "override", "adjust", "tweak", "tune",
1129
+ // Modification verbs
1130
+ "modify", "change", "alter", "update", "mutate", "transform", "rewrite",
1131
+ "revise", "amend", "adjust", "tweak", "tune", "rework", "overhaul",
1132
+ // Destructive verbs
1133
+ "delete", "remove", "drop", "kill", "destroy", "purge", "wipe", "erase",
1134
+ "eliminate", "obliterate", "expunge", "nuke", "truncate", "clear", "empty",
1135
+ "flush", "reset", "void",
1136
+ // Creation verbs
1137
+ "add", "create", "introduce", "insert", "generate", "produce", "spawn",
1138
+ // Toggle verbs
1139
+ "disable", "enable", "activate", "deactivate", "start", "stop", "halt",
1140
+ "pause", "suspend", "freeze",
1141
+ // Replacement verbs
1142
+ "replace", "swap", "substitute", "switch", "exchange", "override", "overwrite",
1143
+ // Movement verbs
1144
+ "move", "relocate", "migrate", "transfer", "shift", "rearrange", "reorganize",
1145
+ "merge", "split", "separate", "partition", "divide", "fork",
1146
+ // Installation verbs
1147
+ "install", "uninstall", "deploy", "connect", "disconnect", "detach",
1148
+ // Structural verbs
1149
+ "refactor", "restructure", "simplify", "reduce", "consolidate",
1150
+ "clean", "normalize", "flatten",
1151
+ // Recovery verbs
1152
+ "fix", "repair", "restore", "recover", "break", "revert", "rollback",
1153
+ // Visibility verbs
1154
+ "expose", "hide", "reveal", "leak",
1155
+ // Bypass verbs
1156
+ "bypass", "skip", "ignore", "circumvent",
1157
+ // Financial verbs (new)
1158
+ "reconcile", "reverse", "recalculate", "backdate", "rebalance",
1159
+ "post", "unpost", "accrue", "amortize", "depreciate", "journal",
1160
+ // Logistics verbs (new)
1161
+ "reroute", "divert", "reassign", "deconsolidate",
1162
+ // Booking verbs (new)
1163
+ "rebook", "cancel",
1164
+ // Upgrade/downgrade
1165
+ "upgrade", "downgrade", "patch", "bump", "advance",
979
1166
  ]);
980
1167
 
981
1168
  // Check if any synonym/concept match involves a non-verb term (= subject match)
@@ -992,9 +1179,11 @@ export function scoreConflict({ actionText, lockText }) {
992
1179
  euphemismMatches.length > 0;
993
1180
 
994
1181
  // If the ONLY matches are verb-level (euphemism/synonym) with no subject
995
- // overlap, drastically reduce the score — these are likely false positives
1182
+ // overlap, reduce the score — these are likely false positives.
1183
+ // Use 0.25 (not 0.15) to avoid killing legitimate cross-domain detections
1184
+ // where concept links are present but subject wording differs.
996
1185
  if (!hasSubjectMatch && (synonymMatches.length > 0 || euphemismMatches.length > 0)) {
997
- score = Math.floor(score * 0.15);
1186
+ score = Math.floor(score * 0.25);
998
1187
  }
999
1188
 
1000
1189
  const prohibitedVerb = extractProhibitedVerb(lockText);
@@ -1047,6 +1236,26 @@ export function scoreConflict({ actionText, lockText }) {
1047
1236
  // IS a modification. Only truly read-only and activation verbs are safe.
1048
1237
  ]);
1049
1238
 
1239
+ // OBSERVABILITY ACTIONS: "add logging", "add monitoring", "add tracking"
1240
+ // are constructive observability actions, NOT modifications to the locked system.
1241
+ // If the action verb is "add/create/implement" AND the object is an
1242
+ // observability concept, treat it as safe.
1243
+ const OBSERVABILITY_KEYWORDS = new Set([
1244
+ "logging", "log", "logs", "monitoring", "monitor", "tracking",
1245
+ "tracing", "trace", "metrics", "alerting", "alerts", "alert",
1246
+ "observability", "telemetry", "analytics", "reporting", "auditing",
1247
+ "profiling", "instrumentation", "dashboard",
1248
+ ]);
1249
+ const actionLower = actionText.toLowerCase();
1250
+ const actionWords = actionLower.split(/\s+/);
1251
+ const hasObservabilityObject = actionWords.some(w => OBSERVABILITY_KEYWORDS.has(w));
1252
+ const CONSTRUCTIVE_VERBS = new Set(["add", "create", "implement", "introduce", "set up", "enable"]);
1253
+ if (CONSTRUCTIVE_VERBS.has(actionPrimaryVerb) && hasObservabilityObject) {
1254
+ intentAligned = true;
1255
+ reasons.push(
1256
+ `intent alignment: observability action "${actionPrimaryVerb} ... ${actionWords.find(w => OBSERVABILITY_KEYWORDS.has(w))}" is non-destructive`);
1257
+ }
1258
+
1050
1259
  const PROHIBITED_ACTION_VERBS = new Set([
1051
1260
  "modify", "change", "alter", "delete", "remove", "disable",
1052
1261
  "drop", "break", "weaken", "expose", "install", "push",
@@ -91,7 +91,7 @@ import { fileURLToPath } from "url";
91
91
  import _path from "path";
92
92
 
93
93
  const PROJECT_ROOT = process.env.SPECLOCK_PROJECT_ROOT || process.cwd();
94
- const VERSION = "3.5.3";
94
+ const VERSION = "3.5.4";
95
95
  const AUTHOR = "Sandeep Roy";
96
96
  const START_TIME = Date.now();
97
97
 
package/src/mcp/server.js CHANGED
@@ -100,7 +100,7 @@ const PROJECT_ROOT =
100
100
  args.project || process.env.SPECLOCK_PROJECT_ROOT || process.cwd();
101
101
 
102
102
  // --- MCP Server ---
103
- const VERSION = "3.5.3";
103
+ const VERSION = "3.5.4";
104
104
  const AUTHOR = "Sandeep Roy";
105
105
 
106
106
  const server = new McpServer(