mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Release Candidate / Check if release candidate should run (push) Waiting to run
Release Candidate / ci (push) Blocked by required conditions
Release Candidate / Publish release candidate to npm (push) Blocked by required conditions
Release Candidate / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
* fix(windows): 32767-char tree-sitter crash + VECTOR extension SIGSEGV
tree-sitter 0.21.x on Windows crashes with SIGSEGV when parsing source
strings longer than 32 767 chars (signed 16-bit integer overflow in the
native binding). Five call sites passed raw file content without any
length guard:
- captures.ts (C# scope extraction)
- namespace-siblings.ts (extractFileStructure)
- parse-worker.ts (worker thread parse path)
- parsing-processor.ts (sequential parse fallback)
Fix: truncate at the last newline before the limit so the fragment stays
syntactically coherent. Files truncated mid-class produce ERROR roots;
captures.ts returns [] for any ERROR-root tree so the legacy DAG handles
the file silently without orphaned scope errors.
Additional C# scope fixes:
- scope-tree.ts: Module scopes may share the same range as a top-level
namespace_declaration (files with no leading `using` directives). The
rangeStrictlyContains check rejects equal ranges. Added
rangeNonStrictlyContains for Module parents.
- scope-extractor.ts: pass1BuildScopes stack-pop used strict containment;
same Module == Namespace range case caused orphaned scopes. Added
moduleAwareContains helper.
- scope-extractor-bridge.ts: empty captures from ERROR-root files still
called extractScope -> "no Module scope found" warning. Added early
return for empty/non-array captures.
- namespace-siblings.ts: three sites pushed onto binding arrays frozen by
finalize-algorithm. Fixed with spread-copy before mutation.
lbug-adapter.ts: INSTALL VECTOR in loadVectorExtension calls the KuzuDB
native extension installer, which crashes with SIGSEGV on Windows via an
unhandled error path in native code. JS try/catch cannot intercept native
signals. Skip extension loading on win32 — vector/embedding search is
unavailable on Windows but all graph index queries work correctly.
Verified on: Windows 11, Node.js 24, gitnexus 1.6.3, pcf8-game codebase
(61 757 nodes / 111 796 edges / 300 flows after fix).
* fix(windows): skip FTS extension load in pool-adapter on Windows to prevent SIGSEGV
LOAD EXTENSION fts crashes the process with SIGSEGV on Windows when the
FTS extension binary is not installed locally. This is an @ladybugdb/core
native bug — the extension loader hits an unhandled error path that raises
a native signal instead of a JS exception, so try/catch cannot protect here.
Add a process.platform === 'win32' guard in both doInitLbug and
initLbugWithDb. When skipped, bm25-index.js catches the resulting
Kuzu catalog errors (CREATE_FTS_INDEX not defined) and returns empty
BM25 results gracefully. All graph queries (cypher, context, impact)
are unaffected.
This is patch 9 of the Windows fix series for gitnexus on Windows:
patch 8 (same PR) already fixed INSTALL VECTOR SIGSEGV in lbug-adapter.ts.
pool-adapter.ts is the separate MCP-server code path that was not covered.
* fix: address codeql findings on PR #1433
The four `lastIndexOf('\n', ...)` calls were committed with a literal
newline inside the single-quoted string instead of the `\n` escape, so
the files do not parse — `tsc` and CodeQL both flagged them. Replace
the embedded newline with `'\n'`.
Also remove the two helpers that were superseded during review and
became dead code: `rangeNonStrictlyContains` in scope-tree.ts (the
equal-range carve-out is handled by `rangeStrictlyContains` +
`rangesEqual` in `canParentScope`) and `moduleAwareContains` in
scope-extractor.ts (`pass1BuildScopes` calls `canParentScope` directly).
* fix(windows): replace 32767-char truncation with chunked-input parsing
The tree-sitter 0.21.x Node binding crashes (SIGSEGV) on Windows when
parser.parse(string, ...) is handed a JS string longer than 32 767 chars.
The crash is in the bindings V8 string-to-buffer conversion and cannot
be intercepted from JS. Previous mitigation truncated source at the last
newline before that boundary, silently losing the file tail and producing
ERROR-root trees from mid-class cuts.
Switch to the callback (Parser.Input) overload via a new parseSourceSafe
helper. tree-sitter pulls source in 16 KiB chunks via repeated callback
invocations, bypassing the broken conversion path. Files are parsed in
full, no data loss, no platform-specific code path.
Removes the now-unnecessary ERROR-root short-circuit in csharp/captures.ts
and the empty-captures shim in scope-extractor-bridge.ts; both existed only
to swallow truncation-induced parse failures.
* fix(windows): cover all parse sites and correct vector-extension state
Address adversarial review on PR #1433:
1. Extend parseSourceSafe to all remaining parser.parse() call sites that
handle full file content. The first commit only converted the four
sites with active truncation hacks; cache-miss paths in
call-processor (x2), heritage-processor (x2), import-processor, and
the Go/Python/TypeScript captures + Go range-binding still called
parser.parse() directly. On Windows those would still SIGSEGV for
files > 32767 chars.
2. Stop setting vectorExtensionLoaded = true on the win32 short-circuit
in lbug-adapter.ts. The flag means "successfully loaded" and is
checked by an early-return at the top of loadVectorExtension; setting
it on the skip path made the second call return true and let
QUERY_VECTOR_INDEX run against a DB without the extension.
3. Drop the placeholder issues/... URL in the same comment.
4. Add unit tests for parseSourceSafe at boundary values: 16 KiB
(direct/callback boundary), the 32 767 Windows crash boundary,
single-line > chunk size, CRLF near boundary, and large all-Chinese
source. Confirms the callback path is correct for non-ASCII content,
which is also exercised by the existing csharp-captures large-file
test.
Researched the chunking concern: tree-sitter Node binding sets
TSInputEncodingUTF16 and divides byte_index by 2 in ByteCountToJS before
calling the JS callback, so the index argument is a UTF-16 code-unit
offset — matching String.prototype.slice. Splitting tokens across chunks
is safe by API contract; the lexer is chunk-agnostic.
* fix(windows): extend parseSourceSafe to group/embeddings + lint enforcement
Closes the remaining Windows SIGSEGV exposure flagged by the Codex
adversarial review on PR #1433. Six pre-existing parser.parse(content)
call sites bypassed parseSourceSafe and could crash the process on
Windows when a contract IDL, route file, or embedding-target source
exceeded 32 767 chars. Adds a lint rule so the regression vector closes
permanently.
Production code:
- Relocate parseSourceSafe from ingestion/utils/ to core/tree-sitter/
so group/ and embeddings/ can import without crossing into ingestion
internals. core/tree-sitter/ already houses parser-loader.ts and is
the natural shared facade. All 11 existing importers updated; no shim
left behind in the old location.
- Route through parseSourceSafe in 5 group extractors (grpc, thrift,
http-route, include, tree-sitter-scanner) and the embeddings
ensureAndParse helper.
- The seventh direct .parse() call in grpc-patterns/proto.ts:49 is a
module-load grammar smoke test parsing a 36-char literal. Trivially
safe by inspection, intentionally direct, filtered out by the lint
rule via the string-literal-arg skip.
Tests:
- 5 caller-side regression tests with a vi.spyOn assertion on
parseSourceSafe. The spy is what catches a regression: parser.parse
on a 40 000-char input succeeds on Linux/macOS, so a "no throw"
assertion alone would silently pass with the bypass reintroduced.
- The vi.mock boilerplate is centralised in
gitnexus/test/helpers/parse-source-safe-mock.ts, dynamic-imported
inside each mock factory so vitest's hoister does not race the
static import binding.
Lint:
- New custom ESLint rule gitnexus/require-safe-parse, scoped to
gitnexus/src/core/**, fails on direct <parser>.parse(<non-literal>,
...) calls and auto-fixes them to parseSourceSafe(<parser>, ...).
Skips JSON/URL/marked/Number/Math, string-literal first args
(smoke tests), test files, and the helper itself. Auto-fix rewrites
the call site only; the developer adds the import after tsc
surfaces the missing identifier — same tradeoff as
unused-imports/no-unused-imports.
Plan: docs/plans/2026-05-10-001-fix-windows-parse-safety-group-and-embeddings-plan.md
* fix(test): use mkdtempSync in http-route-extractor regression test
Address CodeQL js/insecure-temporary-file warning on the new Windows-
SIGSEGV regression test. The test was using path.join(tmpDir, "large-input")
which, when nested inside a Date.now()-based parent tmpDir, lets CodeQL flag
the directory as a predictable-name temp file with race-condition risk.
Switch to fs.mkdtempSync(path.join(tmpDir, "large-input-")) so the suffix
is a secure unique random string.
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
206 lines
7.8 KiB
JavaScript
206 lines
7.8 KiB
JavaScript
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
|
import tsParser from '@typescript-eslint/parser';
|
|
import unusedImports from 'eslint-plugin-unused-imports';
|
|
import reactHooks from 'eslint-plugin-react-hooks';
|
|
import prettierConfig from 'eslint-config-prettier';
|
|
import requireSafeParse from './eslint-rules/require-safe-parse.mjs';
|
|
|
|
// Local plugin hosting custom rules that enforce GitNexus-specific invariants
|
|
// (currently: the Windows-SIGSEGV-safe parser entrypoint).
|
|
const gitnexusLocalPlugin = {
|
|
rules: {
|
|
'require-safe-parse': requireSafeParse,
|
|
},
|
|
};
|
|
|
|
// Selectors that protect MCP-reachable code from corrupting the JSON-RPC
|
|
// stdio frame stream. The MCP-reachable block below uses these directly;
|
|
// the lbug-adapter file-specific block must spread them in too because
|
|
// ESLint flat config REPLACES (not merges) `no-restricted-syntax` when
|
|
// multiple matching configs target the same file. Extracting to a const
|
|
// makes the dependency mechanical instead of documentation-enforced.
|
|
const mcpStdoutWriteSelectors = [
|
|
{
|
|
selector:
|
|
"MemberExpression[object.type='MemberExpression'][object.object.name='process'][object.property.name='stdout'][property.name='write']",
|
|
message:
|
|
'Direct process.stdout.write is forbidden in MCP-reachable code. Route diagnostics through console.error or process.stderr.write — the MCP stdio transport owns stdout for JSON-RPC frames.',
|
|
},
|
|
{
|
|
selector:
|
|
"CallExpression[callee.type='MemberExpression'][callee.object.type='MemberExpression'][callee.object.object.name='process'][callee.object.property.name='stdout'][callee.property.name='write']",
|
|
message:
|
|
'Direct process.stdout.write is forbidden in MCP-reachable code. Route diagnostics through console.error or process.stderr.write — the MCP stdio transport owns stdout for JSON-RPC frames.',
|
|
},
|
|
{
|
|
// Catches the canonical destructuring shape:
|
|
// const { write } = process.stdout;
|
|
// (and any other ObjectPattern destructure rooted at process.stdout)
|
|
// which would otherwise capture a reference to the original write
|
|
// and bypass the sentinel.
|
|
selector:
|
|
"VariableDeclarator[init.type='MemberExpression'][init.object.name='process'][init.property.name='stdout'] > ObjectPattern",
|
|
message:
|
|
'Destructuring process.stdout is forbidden in MCP-reachable code — bypasses the sentinel. Use process.stderr.write for diagnostics.',
|
|
},
|
|
];
|
|
|
|
export default [
|
|
// Global ignores
|
|
{
|
|
ignores: [
|
|
'**/dist/**',
|
|
'**/node_modules/**',
|
|
'**/coverage/**',
|
|
'gitnexus/vendor/**',
|
|
'gitnexus-web/src/vendor/**',
|
|
'gitnexus/test/fixtures/**',
|
|
'gitnexus-web/test/fixtures/**',
|
|
'gitnexus-web/playwright-report/**',
|
|
'gitnexus-web/test-results/**',
|
|
'**/*.d.ts',
|
|
'.claude/**',
|
|
'.history/**',
|
|
],
|
|
},
|
|
|
|
// Base TypeScript config for all packages
|
|
{
|
|
files: ['**/*.{ts,tsx}'],
|
|
languageOptions: {
|
|
parser: tsParser,
|
|
parserOptions: {
|
|
ecmaVersion: 2022,
|
|
sourceType: 'module',
|
|
},
|
|
},
|
|
plugins: {
|
|
'@typescript-eslint': tsPlugin,
|
|
'unused-imports': unusedImports,
|
|
},
|
|
rules: {
|
|
// Unused imports — auto-fixable
|
|
'unused-imports/no-unused-imports': 'error',
|
|
'unused-imports/no-unused-vars': [
|
|
'warn',
|
|
{ vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' },
|
|
],
|
|
|
|
// TypeScript quality
|
|
'@typescript-eslint/no-unused-vars': 'off', // handled by unused-imports plugin
|
|
'no-unused-vars': 'off', // handled by unused-imports plugin
|
|
'@typescript-eslint/no-explicit-any': 'warn',
|
|
'@typescript-eslint/no-non-null-assertion': 'warn',
|
|
|
|
// General quality
|
|
'no-debugger': 'error',
|
|
'prefer-const': 'error',
|
|
'no-var': 'error',
|
|
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
|
},
|
|
},
|
|
|
|
// CLI/server packages — `console.log` IS the contract (CLI tool data output
|
|
// on stdout, e.g. `gitnexus query | jq`; server pretty-printed banners).
|
|
// Diagnostic logging (`warn`/`error`/`debug`/`info`) goes through pino like
|
|
// the rest of the codebase.
|
|
{
|
|
files: ['gitnexus/src/cli/**/*.ts', 'gitnexus/src/server/**/*.ts'],
|
|
rules: {
|
|
'no-console': ['error', { allow: ['log'] }],
|
|
},
|
|
},
|
|
|
|
// Forcing function for the pino migration. Severity is `error` — the
|
|
// codebase-wide migration is complete; new `console.*` in core source
|
|
// must fail lint. CLI/server are exempt above (legitimate stdout output).
|
|
// Tests, bin scripts, and the logger module itself remain exempt.
|
|
{
|
|
files: ['gitnexus/src/**/*.ts'],
|
|
ignores: ['gitnexus/src/cli/**', 'gitnexus/src/server/**', 'gitnexus/src/core/logger.ts'],
|
|
rules: {
|
|
'no-console': 'error',
|
|
},
|
|
},
|
|
|
|
// MCP-reachable code: forbid stdout-corrupting writes. The MCP stdio
|
|
// transport writes JSON-RPC frames to stdout; per the spec, the server
|
|
// MUST NOT write anything to stdout that is not a valid MCP message.
|
|
// Diagnostics must go to stderr (console.error). Direct process.stdout.write
|
|
// bypasses the gate and is also forbidden in these dirs.
|
|
// cli/mcp.ts is included here even though it lives under cli/ — it is the
|
|
// MCP entrypoint and inherits stricter discipline than the rest of cli/.
|
|
{
|
|
files: [
|
|
'gitnexus/src/mcp/**/*.ts',
|
|
'gitnexus/src/core/lbug/**/*.ts',
|
|
'gitnexus/src/core/embeddings/**/*.ts',
|
|
'gitnexus/src/core/tree-sitter/**/*.ts',
|
|
'gitnexus/src/cli/mcp.ts',
|
|
],
|
|
rules: {
|
|
'no-console': ['error', { allow: ['error'] }],
|
|
'no-restricted-syntax': ['error', ...mcpStdoutWriteSelectors],
|
|
},
|
|
},
|
|
|
|
// Windows SIGSEGV protection: every tree-sitter parse in `core/` must route
|
|
// through parseSourceSafe. Direct `<parser>.parse(content, ...)` crashes on
|
|
// Windows for inputs > 32 767 chars (V8 string-conversion bug, uncatchable
|
|
// from JS). The rule auto-fixes the call site; the developer adds the
|
|
// missing import after the fix runs. Out of scope: tests (skipped by the
|
|
// rule), the helper itself (`safe-parse.ts`), and the `grpc-patterns/proto.ts`
|
|
// grammar-load smoke test (filtered by string-literal-arg skip in the rule).
|
|
{
|
|
files: ['gitnexus/src/core/**/*.ts'],
|
|
plugins: {
|
|
gitnexus: gitnexusLocalPlugin,
|
|
},
|
|
rules: {
|
|
'gitnexus/require-safe-parse': 'error',
|
|
},
|
|
},
|
|
|
|
// React-specific rules for gitnexus-web
|
|
{
|
|
files: ['gitnexus-web/src/**/*.{ts,tsx}'],
|
|
plugins: {
|
|
'react-hooks': reactHooks,
|
|
},
|
|
rules: {
|
|
'react-hooks/rules-of-hooks': 'error',
|
|
'react-hooks/exhaustive-deps': 'warn',
|
|
},
|
|
},
|
|
|
|
// Prevent direct conn.close() / db.close() in the LadybugDB adapter (#1376).
|
|
// All close operations must go through safeClose() so the WAL is always
|
|
// flushed before the connection is released. The sole authorised call site
|
|
// inside safeClose itself uses an eslint-disable-next-line override.
|
|
//
|
|
// ESLint flat config REPLACES (not merges) `no-restricted-syntax` when
|
|
// multiple matching configs target the same file. lbug-adapter.ts is also
|
|
// covered by the MCP-reachable block above, so we spread the shared
|
|
// mcpStdoutWriteSelectors here alongside the safeClose selectors. Without
|
|
// this, lbug-adapter would silently lose its MCP stdout-write protection.
|
|
{
|
|
files: ['gitnexus/src/core/lbug/lbug-adapter.ts'],
|
|
rules: {
|
|
'no-restricted-syntax': [
|
|
'error',
|
|
...mcpStdoutWriteSelectors,
|
|
{
|
|
selector: "CallExpression[callee.object.name='conn'][callee.property.name='close']",
|
|
message: 'Use safeClose() instead of calling conn.close() directly (#1376).',
|
|
},
|
|
{
|
|
selector: "CallExpression[callee.object.name='db'][callee.property.name='close']",
|
|
message: 'Use safeClose() instead of calling db.close() directly (#1376).',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
|
|
// Disable formatting rules (prettier handles those)
|
|
prettierConfig,
|
|
];
|