Files
portal/tools/remote-read-allow.test.mjs
T
2026-06-18 08:04:50 +03:00

144 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest';
import { classifyRemoteReadCommand } from './remote-read-allow.mjs';
const allow = (cmd) => classifyRemoteReadCommand(cmd).ok;
describe('remote-read-allow — ssh liderra-prod read-only', () => {
it('пропускает одиночный grep (проверочная команда из задачи)', () => {
expect(allow(`ssh liderra-prod 'grep -n "function register" -A 60 /var/www/liderra/app/app/Http/Controllers/Api/AuthController.php'`)).toBe(true);
});
it('пропускает cat/ls/head/tail/stat/wc/file', () => {
expect(allow(`ssh liderra-prod 'cat /var/www/liderra/.env.example'`)).toBe(true);
expect(allow(`ssh liderra-prod 'ls -la /var/www/liderra'`)).toBe(true);
expect(allow(`ssh liderra-prod 'head -100 /var/log/nginx/access.log'`)).toBe(true);
expect(allow(`ssh liderra-prod 'tail -f /var/log/liderra/laravel.log'`)).toBe(true);
expect(allow(`ssh liderra-prod 'stat /var/www/liderra/artisan'`)).toBe(true);
expect(allow(`ssh liderra-prod 'wc -l /var/log/nginx/error.log'`)).toBe(true);
});
it('пропускает sed в print-форме', () => {
expect(allow(`ssh liderra-prod 'sed -n 5,20p /etc/nginx/nginx.conf'`)).toBe(true);
});
it('блокирует sed in-place (-i) и sed с write-командой w', () => {
expect(allow(`ssh liderra-prod 'sed -i s/a/b/ /etc/nginx/nginx.conf'`)).toBe(false);
expect(allow(`ssh liderra-prod 'sed --in-place s/a/b/ file'`)).toBe(false);
expect(allow(`ssh liderra-prod 'sed s/a/b/w out file'`)).toBe(false);
expect(allow(`ssh liderra-prod 'sed -f script.sed file'`)).toBe(false);
});
it('блокирует мутации (touch/mv/cp/chmod/tee)', () => {
expect(allow(`ssh liderra-prod 'touch /tmp/x'`)).toBe(false);
expect(allow(`ssh liderra-prod 'mv a b'`)).toBe(false);
expect(allow(`ssh liderra-prod 'cp a b'`)).toBe(false);
expect(allow(`ssh liderra-prod 'chmod 777 /etc/passwd'`)).toBe(false);
expect(allow(`ssh liderra-prod 'tee /etc/x'`)).toBe(false);
expect(allow(`ssh liderra-prod 'rm -rf /var/www'`)).toBe(false);
});
it('блокирует чужой хост и ssh-опции (-L/-o/...)', () => {
expect(allow(`ssh otherhost 'cat /etc/passwd'`)).toBe(false);
expect(allow(`ssh -L 8080:localhost:80 liderra-prod 'cat x'`)).toBe(false);
expect(allow(`ssh -o ProxyCommand=evil liderra-prod 'cat x'`)).toBe(false);
expect(allow(`ssh root@otherhost 'cat x'`)).toBe(false);
});
it('блокирует пайпы/цепочки/редиректы/sub-shell в удалённой команде', () => {
expect(allow(`ssh liderra-prod 'cat a | tee b'`)).toBe(false);
expect(allow(`ssh liderra-prod 'cat a && rm b'`)).toBe(false);
expect(allow(`ssh liderra-prod 'cat a; rm b'`)).toBe(false);
expect(allow(`ssh liderra-prod 'cat $(whoami)'`)).toBe(false);
expect(allow(`ssh liderra-prod 'cat a > b'`)).toBe(false);
});
it('блокирует цепочку/редирект на внешнем уровне', () => {
expect(allow(`ssh liderra-prod 'cat a' && rm b`)).toBe(false);
expect(allow(`ssh liderra-prod 'cat a' > out`)).toBe(false);
expect(allow(`ssh liderra-prod 'cat a' | tee b`)).toBe(false);
});
});
describe('remote-read-allow — psql SELECT-only', () => {
it('пропускает SELECT/WITH/SHOW/EXPLAIN через -c', () => {
expect(allow(`ssh liderra-prod 'psql -c "SELECT count(*) FROM deals"'`)).toBe(true);
expect(allow(`ssh liderra-prod 'psql -c "WITH t AS (SELECT 1) SELECT * FROM t"'`)).toBe(true);
expect(allow(`ssh liderra-prod 'psql -c "SHOW server_version"'`)).toBe(true);
expect(allow(`ssh liderra-prod 'psql -c "EXPLAIN SELECT 1"'`)).toBe(true);
expect(allow(`ssh liderra-prod 'psql -c "SELECT 1;"'`)).toBe(true);
});
it('блокирует write-SQL (INSERT/UPDATE/DELETE/DROP/...)', () => {
expect(allow(`ssh liderra-prod 'psql -c "UPDATE deals SET x=1"'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -c "INSERT INTO deals VALUES (1)"'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -c "DELETE FROM deals"'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -c "DROP TABLE deals"'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -c "TRUNCATE deals"'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -c "GRANT ALL ON deals TO x"'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -c "COPY deals TO PROGRAM \\'sh\\'"'`)).toBe(false);
});
it('блокирует множественные стейтменты и SELECT…INTO', () => {
expect(allow(`ssh liderra-prod 'psql -c "SELECT 1; DROP TABLE x"'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -c "SELECT * INTO newt FROM deals"'`)).toBe(false);
});
it('блокирует -f (файл-скрипт) и -o (запись результата в файл)', () => {
expect(allow(`ssh liderra-prod 'psql -f /tmp/evil.sql'`)).toBe(false);
expect(allow(`ssh liderra-prod 'psql -o /tmp/out -c "SELECT 1"'`)).toBe(false);
});
it('блокирует psql без явного -c (интерактив)', () => {
expect(allow(`ssh liderra-prod 'psql liderra'`)).toBe(false);
});
});
describe('remote-read-allow — gh GET only', () => {
it('пропускает run list|view, workflow list|view', () => {
expect(allow(`gh run list`)).toBe(true);
expect(allow(`gh run view 12345`)).toBe(true);
expect(allow(`gh run view 12345 --log`)).toBe(true);
expect(allow(`gh workflow list`)).toBe(true);
expect(allow(`gh workflow view deploy.yml`)).toBe(true);
});
it('пропускает gh api GET (по умолчанию и явно)', () => {
expect(allow(`gh api repos/liderra/portal/actions/runs`)).toBe(true);
expect(allow(`gh api -X GET repos/liderra/portal/commits`)).toBe(true);
});
it('блокирует gh мутации (rerun/cancel/workflow run/pr merge)', () => {
expect(allow(`gh run rerun 12345`)).toBe(false);
expect(allow(`gh run cancel 12345`)).toBe(false);
expect(allow(`gh run delete 12345`)).toBe(false);
expect(allow(`gh workflow run deploy.yml`)).toBe(false);
expect(allow(`gh workflow enable deploy.yml`)).toBe(false);
expect(allow(`gh pr merge 1`)).toBe(false);
expect(allow(`gh release create v1`)).toBe(false);
});
it('блокирует gh api с не-GET методом и телом запроса', () => {
expect(allow(`gh api -X POST repos/liderra/portal/dispatches`)).toBe(false);
expect(allow(`gh api --method DELETE repos/x/y`)).toBe(false);
expect(allow(`gh api -XPOST repos/x/y`)).toBe(false);
expect(allow(`gh api repos/x/y -f key=val`)).toBe(false);
expect(allow(`gh api graphql -f query=mutation`)).toBe(false);
expect(allow(`gh api repos/x/y --input body.json`)).toBe(false);
});
it('блокирует gh api без пути', () => {
expect(allow(`gh api`)).toBe(false);
});
});
describe('remote-read-allow — общие отказы', () => {
it('не пропускает локальные команды и мусор', () => {
expect(allow(`cat /etc/passwd`)).toBe(false);
expect(allow(`ls`)).toBe(false);
expect(allow(`scp a liderra-prod:b`)).toBe(false);
expect(allow(``)).toBe(false);
expect(allow(null)).toBe(false);
expect(allow(`ssh liderra-prod`)).toBe(false);
});
});