mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 17:41:16 +00:00
* fix(security): scope cross-source reads to the caller grant; close get_page exact-path leak One shared resolveRequestedScope() routes every source-scoped read op (query, code_callers/callees, search_by_image, code_blast/flow, get_page) through a single fail-closed trust+grant ladder: a remote caller's __all__ collapses to its granted sources (never the whole brain) and an explicit out-of-grant source_id is rejected. get_page's exact-match path now honors a federated grant via getPage(sourceIds[]) in both engines. Legacy bearer tokens carry their stored permissions.source_id grant (bounded, never widened). Also retries getConfig on transient connection loss. Closes #1924, #1371, #1393, #1336, #1603. * fix(ingest): non-string frontmatter no longer aborts lint/sync; embed/hook/catalog papercuts Parser coerces a non-string title to a string and falls back to inference for slug/type (never fabricating a "123" slug), with a lint NON_STRING_FIELD finding surfacing the malformed frontmatter; a defensive guard in content-sanity stops a non-string title from crashing the whole lint/sync run brain-wide. Plus: embed --catch-up no longer arms the overflowed 32-bit budget timer (and surfaces unembeddable chunks); the frontmatter pre-commit hook ships a correct .md/.mdx regex; and the skill catalog parses YAML block-scalar descriptions. Closes #1883, #1658, #1556, #1948, #1946, #1840, #1711. * v0.42.37.0 fix(security,ingest): source-isolation grant enforcement + non-string frontmatter guard + papercuts Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add NON_STRING_FIELD frontmatter validation class to docs for v0.42.37.0 The v0.42.37.0 non-string-frontmatter fix added an eighth validation class (NON_STRING_FIELD / lint code frontmatter-non-string-field). Update the two current-state docs that enumerate the validation classes: - skills/frontmatter-guard/SKILL.md (seven->eight + table row) - docs/integrations/pre-commit.md (seven->eight + table row) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
46 lines
2.0 KiB
TypeScript
46 lines
2.0 KiB
TypeScript
/**
|
|
* #1336 — legacy bearer tokens honor their stored federated_read grant.
|
|
*
|
|
* Pre-fix the legacy access_tokens path hardcoded `sourceId: 'default'` and never
|
|
* populated `allowedSources`, so a token whose `permissions.source_id` granted
|
|
* multiple sources could not read across them (and MCP reads silently returned
|
|
* empty in non-default brains). The grant is now parsed and threaded.
|
|
*
|
|
* Bounded by design: never widened to "all" — an empty/garbage value keeps the
|
|
* 'default' floor and no federated grant.
|
|
*/
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { parseLegacyTokenScope } from '../src/mcp/http-transport.ts';
|
|
|
|
describe('parseLegacyTokenScope', () => {
|
|
test('array grant → allowedSources (federated read) with first as scalar floor', () => {
|
|
expect(parseLegacyTokenScope(['dept-x', 'shared'])).toEqual({ sourceId: 'dept-x', allowedSources: ['dept-x', 'shared'] });
|
|
});
|
|
|
|
test('single-element array → that source as both floor and grant', () => {
|
|
expect(parseLegacyTokenScope(['only'])).toEqual({ sourceId: 'only', allowedSources: ['only'] });
|
|
});
|
|
|
|
test('string grant → scalar source, no federated array', () => {
|
|
expect(parseLegacyTokenScope('team-a')).toEqual({ sourceId: 'team-a' });
|
|
});
|
|
|
|
test('absent grant → default floor, never widened', () => {
|
|
expect(parseLegacyTokenScope(undefined)).toEqual({ sourceId: 'default' });
|
|
expect(parseLegacyTokenScope(null)).toEqual({ sourceId: 'default' });
|
|
});
|
|
|
|
test('empty array → default floor, no grant (NOT "all")', () => {
|
|
expect(parseLegacyTokenScope([])).toEqual({ sourceId: 'default' });
|
|
});
|
|
|
|
test('garbage (number / empty string) → default floor', () => {
|
|
expect(parseLegacyTokenScope(123)).toEqual({ sourceId: 'default' });
|
|
expect(parseLegacyTokenScope('')).toEqual({ sourceId: 'default' });
|
|
});
|
|
|
|
test('array with non-string junk is filtered to valid sources', () => {
|
|
expect(parseLegacyTokenScope(['a', 5, '', 'b'])).toEqual({ sourceId: 'a', allowedSources: ['a', 'b'] });
|
|
});
|
|
});
|