sparda-mcp 0.13.2 → 0.13.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/flight/box.js +11 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparda-mcp",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "mcpName": "io.github.zyx77550/sparda-mcp",
5
5
  "description": "Compile backends to behavior graphs. Statically prove security, simulate APIs, and replay bugs.",
6
6
  "type": "module",
package/src/flight/box.js CHANGED
@@ -29,8 +29,11 @@ export function createFlightBox() {
29
29
  const als = new AsyncLocalStorage();
30
30
  const originals = {};
31
31
  let armed = false;
32
- let insideUUID = false;
33
- let insideFetch = false;
32
+ // Node 18: fetch (undici) and crypto.randomUUID call Date.now()/Math.random()
33
+ // internally those must NOT be recorded as entropy taps. The suppression
34
+ // flag lives ON THE STORE (per-request), not on the box: a global flag would
35
+ // let one request's fetch window swallow a concurrent request's entropy taps
36
+ // and silently corrupt its flight.
34
37
 
35
38
  function arm() {
36
39
  if (armed) return;
@@ -42,7 +45,7 @@ export function createFlightBox() {
42
45
 
43
46
  Date.now = function spardaDateNow() {
44
47
  const store = als.getStore();
45
- if (!store || insideUUID || insideFetch) return originals.dateNow();
48
+ if (!store || store.suppressEntropy) return originals.dateNow();
46
49
  if (store.mode === 'record') {
47
50
  const v = originals.dateNow();
48
51
  tapOut(store, 'time', 'Date.now', v);
@@ -53,7 +56,7 @@ export function createFlightBox() {
53
56
 
54
57
  Math.random = function spardaRandom() {
55
58
  const store = als.getStore();
56
- if (!store || insideUUID || insideFetch) return originals.random();
59
+ if (!store || store.suppressEntropy) return originals.random();
57
60
  if (store.mode === 'record') {
58
61
  const v = originals.random();
59
62
  tapOut(store, 'random', 'Math.random', v);
@@ -69,13 +72,13 @@ export function createFlightBox() {
69
72
  const store = als.getStore();
70
73
  if (!store) return originals.randomUUID();
71
74
  if (store.mode === 'record') {
72
- insideUUID = true;
75
+ store.suppressEntropy = true;
73
76
  try {
74
77
  const v = originals.randomUUID();
75
78
  tapOut(store, 'uuid', 'crypto.randomUUID', v);
76
79
  return v;
77
80
  } finally {
78
- insideUUID = false;
81
+ store.suppressEntropy = false;
79
82
  }
80
83
  }
81
84
  return takeTap(store, 'uuid', 'crypto.randomUUID');
@@ -90,7 +93,7 @@ export function createFlightBox() {
90
93
  const method = (init?.method ?? 'GET').toUpperCase();
91
94
  const label = `${method} ${url}`;
92
95
  if (store.mode === 'record') {
93
- insideFetch = true;
96
+ store.suppressEntropy = true;
94
97
  try {
95
98
  const res = await originals.fetch(input, init);
96
99
  const text = (await res.text()).slice(0, MAX_BODY_BYTES);
@@ -98,7 +101,7 @@ export function createFlightBox() {
98
101
  tapOut(store, 'http', label, { status: res.status, headers, body: text });
99
102
  return new Response(text, { status: res.status, headers });
100
103
  } finally {
101
- insideFetch = false;
104
+ store.suppressEntropy = false;
102
105
  }
103
106
  }
104
107
  const rec = takeTap(store, 'http', label);