Files
admin b723e2bd7d Reorganize: Move all skills to skills/ folder
- Created skills/ directory
- Moved 272 skills to skills/ subfolder
- Kept agents/ at root level
- Kept installation scripts and docs at root level

Repository structure:
- skills/           - All 272 skills from skills.sh
- agents/           - Agent definitions
- *.sh, *.ps1       - Installation scripts
- README.md, etc.   - Documentation

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-23 18:05:17 +00:00

230 lines
6.3 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { analyzeCommand } from '../src/core/analyze.ts';
import { loadConfig } from '../src/core/config.ts';
function writeConfig(dir: string, data: object): void {
const path = join(dir, '.safety-net.json');
writeFileSync(path, JSON.stringify(data), 'utf-8');
}
function runGuard(command: string, cwd?: string): string | null {
const config = loadConfig(cwd);
return analyzeCommand(command, { cwd, config })?.reason ?? null;
}
function assertBlocked(command: string, reasonContains: string, cwd?: string): void {
const result = runGuard(command, cwd);
expect(result).not.toBeNull();
expect(result).toContain(reasonContains);
}
function assertAllowed(command: string, cwd?: string): void {
const result = runGuard(command, cwd);
expect(result).toBeNull();
}
describe('custom rules integration', () => {
let tempDir: string;
beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), 'safety-net-custom-rules-'));
});
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true });
});
test('custom rule blocks command', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A', '--all', '.'],
reason: 'Use specific files.',
},
],
});
assertBlocked('git add -A', '[block-git-add-all] Use specific files.', tempDir);
});
test('custom rule blocks with dot', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A', '--all', '.'],
reason: 'Use specific files.',
},
],
});
assertBlocked('git add .', '[block-git-add-all]', tempDir);
});
test('custom rule allows non-matching command', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'Use specific files.',
},
],
});
assertAllowed('git add file.txt', tempDir);
});
test('builtin rule takes precedence', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'custom-reset-rule',
command: 'git',
subcommand: 'reset',
block_args: ['--soft'],
reason: 'Custom reason.',
},
],
});
// Built-in rule blocks git reset --hard, not custom rule
assertBlocked('git reset --hard', 'git reset --hard destroys', tempDir);
});
test('multiple custom rules - any match triggers block', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'No blanket add.',
},
{
name: 'block-npm-global',
command: 'npm',
subcommand: 'install',
block_args: ['-g'],
reason: 'No global installs.',
},
],
});
assertBlocked('git add -A', '[block-git-add-all]', tempDir);
assertBlocked('npm install -g pkg', '[block-npm-global]', tempDir);
});
test('rule without subcommand matches any invocation', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-npm-global',
command: 'npm',
block_args: ['-g', '--global'],
reason: 'No global.',
},
],
});
assertBlocked('npm install -g pkg', '[block-npm-global]', tempDir);
assertBlocked('npm uninstall -g pkg', '[block-npm-global]', tempDir);
});
test('no config uses builtin only', () => {
// tempDir has no config file
assertBlocked('git reset --hard', 'git reset --hard destroys', tempDir);
assertAllowed('git add -A', tempDir);
});
test('empty rules list uses builtin only', () => {
writeConfig(tempDir, { version: 1, rules: [] });
assertBlocked('git reset --hard', 'git reset --hard destroys', tempDir);
assertAllowed('git add -A', tempDir);
});
test('invalid config uses builtin only', () => {
const path = join(tempDir, '.safety-net.json');
writeFileSync(path, '{"version": 2}', 'utf-8');
assertBlocked('git reset --hard', 'git reset --hard destroys', tempDir);
assertAllowed('echo hello', tempDir);
});
test('custom rules not applied to embedded commands', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'No blanket add.',
},
],
});
// Direct command is blocked
assertBlocked('git add -A', '[block-git-add-all]', tempDir);
// Embedded in bash -c is NOT blocked by custom rule (per spec)
assertAllowed("bash -c 'git add -A'", tempDir);
});
test('custom rules apply to xargs', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-xargs-grep',
command: 'xargs',
block_args: ['grep'],
reason: 'Use ripgrep instead.',
},
],
});
assertBlocked('find . | xargs grep pattern', '[block-xargs-grep]', tempDir);
});
test('custom rules apply to parallel', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-parallel-curl',
command: 'parallel',
block_args: ['curl'],
reason: 'No parallel curl.',
},
],
});
assertBlocked('parallel curl ::: url1 url2', '[block-parallel-curl]', tempDir);
});
test('attached option value not false positive', () => {
writeConfig(tempDir, {
version: 1,
rules: [
{
name: 'block-p-flag',
command: 'git',
block_args: ['-p'],
reason: 'No -p allowed.',
},
],
});
// -C/path/to/project contains 'p' in the path, but should NOT match -p
assertAllowed('git -C/path/to/project status', tempDir);
});
});