mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-06 08:16:02 +00:00
1. Path traversal via group name — add validateGroupName() with regex [a-zA-Z0-9][a-zA-Z0-9_-]*, called in getGroupDir (defense in depth) 2. gRPC proto regex can't handle nested braces — replace serviceRe with extractServiceBlocks() brace-depth counter (init depth=1, skip malformed protos) 3. Service boundary detector directory exclusions — add EXCLUDED_DIRS set (vendor, target, build, dist, __pycache__, .venv, venv, .tox, .mypy_cache, .gradle, .mvn, out, bin) replacing inline node_modules 4. Double-close of LadybugDB pools — remove blanket closeLbug() from cli/group.ts; sync.ts per-id cleanup is sufficient Tests: 22 new tests across 5 files. Full suite: 4706 passed, 0 failed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
/**
|
|
* Smoke-test `gitnexus group` CLI via tsx (same pattern as cli-e2e.test.ts).
|
|
* Does not exercise LadybugDB-backed commands end-to-end (needs indexed fixtures).
|
|
*/
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import { spawnSync } from 'node:child_process';
|
|
import path from 'node:path';
|
|
import fs from 'node:fs';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import { createRequire } from 'node:module';
|
|
import os from 'node:os';
|
|
|
|
const testDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const repoRoot = path.resolve(testDir, '../../..');
|
|
const cliEntry = path.join(repoRoot, 'src/cli/index.ts');
|
|
const _require = createRequire(import.meta.url);
|
|
const tsxPkgDir = path.dirname(_require.resolve('tsx/package.json'));
|
|
const tsxImportUrl = pathToFileURL(path.join(tsxPkgDir, 'dist', 'loader.mjs')).href;
|
|
|
|
let tmpHome: string;
|
|
|
|
beforeAll(() => {
|
|
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gitnexus-group-cli-'));
|
|
});
|
|
|
|
afterAll(() => {
|
|
if (tmpHome && fs.existsSync(tmpHome)) {
|
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function runGroup(args: string[]) {
|
|
return spawnSync(process.execPath, ['--import', tsxImportUrl, cliEntry, 'group', ...args], {
|
|
cwd: repoRoot,
|
|
encoding: 'utf8',
|
|
timeout: 20000,
|
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
env: { ...process.env, GITNEXUS_HOME: tmpHome },
|
|
});
|
|
}
|
|
|
|
describe('group CLI', () => {
|
|
it('create + list', () => {
|
|
const c = runGroup(['create', 'acme']);
|
|
expect(c.status).toBe(0);
|
|
expect(c.stdout).toContain('Created group "acme"');
|
|
|
|
const l = runGroup(['list']);
|
|
expect(l.status).toBe(0);
|
|
expect(l.stdout).toContain('acme');
|
|
});
|
|
|
|
it('test_create_with_invalid_name_fails', () => {
|
|
const result = runGroup(['create', '../../evil']);
|
|
expect(result.status).not.toBe(0);
|
|
expect(result.stderr).toContain('Invalid group name');
|
|
});
|
|
|
|
it('test_sync_command_source_does_not_call_blanket_closeLbug', () => {
|
|
const cliGroupPath = path.join(repoRoot, 'src', 'cli', 'group.ts');
|
|
const source = fs.readFileSync(cliGroupPath, 'utf-8');
|
|
|
|
// closeLbug() without arguments (blanket close) must not appear.
|
|
// Match closeLbug() but not closeLbug(someArg)
|
|
const blanketClosePattern = /closeLbug\s*\(\s*\)/;
|
|
expect(source).not.toMatch(blanketClosePattern);
|
|
});
|
|
});
|