ce2a4b0e1a
Memind CI / Test, build, and release guards (push) Has been cancelled
对比/清单/行程等口吻按人群场景交给分类器;本机 .env 可覆盖 admin-db 的 router 开关。 顺带在 MindSpace MCP token 过期时轮换 Goose 会话,避免旧会话空 Finish。
154 lines
3.6 KiB
JavaScript
154 lines
3.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
mintMindSpaceMcpScopedToken,
|
|
mindSpaceMcpScopedTokenNeedsRotation,
|
|
peekMindSpaceMcpScopedTokenExpiryMs,
|
|
verifyMindSpaceMcpScopedToken,
|
|
} from './mindspace-mcp-scoped-token.mjs';
|
|
|
|
const secret = 'mindspace-test-secret-1234';
|
|
|
|
test('MindSpace MCP token binds user, session, package, workspace, and tools', () => {
|
|
const token = mintMindSpaceMcpScopedToken({
|
|
secret,
|
|
userId: 'user-1',
|
|
sessionId: 'session-1',
|
|
packageId: 'cp_session-1',
|
|
workspaceRef:
|
|
'mindspace://users/user-1/workspace',
|
|
tools: ['write_file', 'read_file'],
|
|
now: 1_000_000,
|
|
tokenId: 'token-1',
|
|
});
|
|
|
|
const claims = verifyMindSpaceMcpScopedToken({
|
|
token,
|
|
secret,
|
|
tool: 'write_file',
|
|
now: 1_001_000,
|
|
});
|
|
|
|
assert.deepEqual(claims, {
|
|
userId: 'user-1',
|
|
sessionId: 'session-1',
|
|
packageId: 'cp_session-1',
|
|
workspaceRef:
|
|
'mindspace://users/user-1/workspace',
|
|
tools: ['read_file', 'write_file'],
|
|
issuedAt: 1_000_000,
|
|
expiresAt: 1_900_000,
|
|
tokenId: 'token-1',
|
|
});
|
|
});
|
|
|
|
test('MindSpace MCP token rejects tampering, expiry, and tools outside the allowlist', () => {
|
|
const token = mintMindSpaceMcpScopedToken({
|
|
secret,
|
|
userId: 'user-1',
|
|
sessionId: 'session-1',
|
|
packageId: 'cp_session-1',
|
|
workspaceRef:
|
|
'mindspace://users/user-1/workspace',
|
|
tools: ['read_file'],
|
|
ttlSeconds: 60,
|
|
now: 1_000_000,
|
|
tokenId: 'token-1',
|
|
});
|
|
|
|
assert.throws(
|
|
() =>
|
|
verifyMindSpaceMcpScopedToken({
|
|
token: `${token}x`,
|
|
secret,
|
|
now: 1_001_000,
|
|
}),
|
|
/signature/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
verifyMindSpaceMcpScopedToken({
|
|
token,
|
|
secret,
|
|
tool: 'write_file',
|
|
now: 1_001_000,
|
|
}),
|
|
(error) =>
|
|
error?.code === 'mcp_tool_forbidden',
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
verifyMindSpaceMcpScopedToken({
|
|
token,
|
|
secret,
|
|
now: 2_000_000,
|
|
clockSkewSeconds: 0,
|
|
}),
|
|
(error) =>
|
|
error?.code === 'expired_mcp_token',
|
|
);
|
|
});
|
|
|
|
test('MindSpace MCP token refuses incomplete scopes and weak secrets', () => {
|
|
assert.throws(
|
|
() =>
|
|
mintMindSpaceMcpScopedToken({
|
|
secret: 'short',
|
|
userId: 'user-1',
|
|
sessionId: 'session-1',
|
|
packageId: 'cp_session-1',
|
|
workspaceRef:
|
|
'mindspace://users/user-1/workspace',
|
|
tools: ['read_file'],
|
|
}),
|
|
(error) =>
|
|
error?.code ===
|
|
'invalid_mcp_token_secret',
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
mintMindSpaceMcpScopedToken({
|
|
secret,
|
|
userId: 'user-1',
|
|
sessionId: 'session-1',
|
|
packageId: 'cp_session-1',
|
|
workspaceRef:
|
|
'mindspace://users/user-1/workspace',
|
|
tools: [],
|
|
}),
|
|
(error) =>
|
|
error?.code === 'invalid_mcp_scope',
|
|
);
|
|
});
|
|
|
|
test('MindSpace MCP token peek reports expiry and rotation window', () => {
|
|
const token = mintMindSpaceMcpScopedToken({
|
|
secret,
|
|
userId: 'user-1',
|
|
sessionId: 'session-1',
|
|
packageId: 'cp_session-1',
|
|
workspaceRef: 'mindspace://users/user-1/workspace',
|
|
tools: ['write_file'],
|
|
ttlSeconds: 3600,
|
|
now: 1_000_000,
|
|
tokenId: 'token-1',
|
|
});
|
|
|
|
assert.equal(peekMindSpaceMcpScopedTokenExpiryMs(token), 4_600_000);
|
|
assert.equal(
|
|
mindSpaceMcpScopedTokenNeedsRotation(token, { now: 1_000_000, skewSeconds: 120 }),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
mindSpaceMcpScopedTokenNeedsRotation(token, {
|
|
now: 4_540_000,
|
|
skewSeconds: 120,
|
|
}),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
mindSpaceMcpScopedTokenNeedsRotation('not-a-token', { now: 1_000_000 }),
|
|
true,
|
|
);
|
|
});
|