fix(scope): a clone under cwd is not the caller's own working tree
isOwnWorkingTree() treated any target at or below cwd as own, so a foreign clone under cwd (cwd = $HOME, a vendor clone in a project) still had its .llm-security-ignore, policy.json and custom SIG rules read. The target must now also share cwd's git root: nearest ancestor with a `.git` entry (dir for a clone, file for a submodule/worktree), or none for both. No git spawn. tmpdir stays foreign. Chosen per the PM order: it is exactly the line between "my repo" and "something I fetched", and the failure direction is safe (foreign => config ignored => more findings, never fewer). Red first: tests/lib/own-working-tree.test.mjs 4 fail / 5 pass (the 5 are known-positives), tests/scanners/nested-clone-scope.test.mjs 5 fail / 4 pass on the old rule. Green after; reverting the git-root comparison turns 9 red. Suite 2306 / 2300 pass / 0 fail / 6 skip; hooks 370/0 (implicit root untouched); golden 109/7/4, 61/61. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c4471617a4
commit
a3f7ee4897
4 changed files with 347 additions and 4 deletions
18
CHANGELOG.md
18
CHANGELOG.md
|
|
@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **A clone under the working directory is no longer "own working tree".**
|
||||||
|
v8.1.0 honoured `.llm-security-ignore` and `.llm-security/policy.json`
|
||||||
|
(including `sig.custom_rules_path`) for any target at or below the working
|
||||||
|
directory, so a foreign clone there — the working directory at `$HOME`, or a
|
||||||
|
cloned vendor folder inside a project — could still suppress or tune its own
|
||||||
|
scan. The target must now also have the same git root as the working
|
||||||
|
directory: the nearest folder above it holding a `.git` (a directory for a
|
||||||
|
clone, a file for a submodule or worktree), or no git root for either
|
||||||
|
(`scanners/lib/own-working-tree.mjs`). Temp-directory targets stay foreign.
|
||||||
|
|
||||||
|
**Behaviour change:** scanning a nested clone, submodule or worktree from
|
||||||
|
the parent repository no longer applies that checkout's ignore file or
|
||||||
|
policy; it now gets the defaults and one stderr line per ignored file.
|
||||||
|
Scanning the repository itself or any plain folder inside it is unchanged,
|
||||||
|
and so are hooks, which read the policy from the project root.
|
||||||
|
|
||||||
## [8.1.0] - 2026-09-22
|
## [8.1.0] - 2026-09-22
|
||||||
|
|
||||||
Antivirus surface. A Windows user should be able to clone the repository and
|
Antivirus surface. A Windows user should be able to clone the repository and
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,41 @@
|
||||||
// Configuration that lives INSIDE a scanned target (.llm-security-ignore,
|
// Configuration that lives INSIDE a scanned target (.llm-security-ignore,
|
||||||
// .llm-security/policy.json and the custom SIG ruleset it can point at) is
|
// .llm-security/policy.json and the custom SIG ruleset it can point at) is
|
||||||
// honored only when the target is the caller's own working directory (or a
|
// honored only when the target is the caller's own working directory (or a
|
||||||
// subdirectory of it), and NEVER when the target resolves under the OS temp
|
// subdirectory of it in the same git working tree), and NEVER when the target resolves under the OS temp
|
||||||
// directory (where git-clone.mjs materializes clones) — the second check is
|
// directory (where git-clone.mjs materializes clones) — the second check is
|
||||||
// defense-in-depth for the case a caller's own cwd sits under tmpdir.
|
// defense-in-depth for the case a caller's own cwd sits under tmpdir.
|
||||||
// Otherwise a foreign/cloned target could configure the scan of itself.
|
// Otherwise a foreign/cloned target could configure the scan of itself.
|
||||||
//
|
//
|
||||||
// S3b (v8.1.0, 2026-09-22) introduced this check in scan-orchestrator.mjs for
|
// S3b (v8.1.0, 2026-09-22) introduced this check in scan-orchestrator.mjs for
|
||||||
// the ignore file; S3c moved it here so policy-loader.mjs shares the one rule.
|
// the ignore file; S3c moved it here so policy-loader.mjs shares the one rule.
|
||||||
|
//
|
||||||
|
// v8.1.1 narrowed "at or below cwd": the target must also have the SAME git
|
||||||
|
// root as cwd — the nearest ancestor holding a `.git` (a directory for a
|
||||||
|
// clone, a file for a submodule or worktree), or no git root for either. A
|
||||||
|
// clone under cwd (cwd = $HOME, or a vendor clone inside a project) is
|
||||||
|
// therefore foreign. No git subprocess: the walk only stats `.git`. The
|
||||||
|
// failure direction is safe — foreign means the target's config is ignored,
|
||||||
|
// so more findings, never fewer.
|
||||||
|
|
||||||
import { resolve, sep } from 'node:path';
|
import { resolve, sep, join, dirname } from 'node:path';
|
||||||
import { realpathSync } from 'node:fs';
|
import { realpathSync, existsSync } from 'node:fs';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nearest ancestor of `start` (inclusive) that holds a `.git` entry, or null.
|
||||||
|
* @param {string} start - a realpath
|
||||||
|
* @returns {string|null}
|
||||||
|
*/
|
||||||
|
function gitRoot(start) {
|
||||||
|
let dir = start;
|
||||||
|
for (;;) {
|
||||||
|
if (existsSync(join(dir, '.git'))) return dir;
|
||||||
|
const parent = dirname(dir);
|
||||||
|
if (parent === dir) return null;
|
||||||
|
dir = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} targetPath
|
* @param {string} targetPath
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
|
|
@ -34,5 +57,6 @@ export function isOwnWorkingTree(targetPath) {
|
||||||
if (resolvedTarget === resolvedTmp || resolvedTarget.startsWith(resolvedTmp + sep)) {
|
if (resolvedTarget === resolvedTmp || resolvedTarget.startsWith(resolvedTmp + sep)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return resolvedTarget === resolvedCwd || resolvedTarget.startsWith(resolvedCwd + sep);
|
const underCwd = resolvedTarget === resolvedCwd || resolvedTarget.startsWith(resolvedCwd + sep);
|
||||||
|
return underCwd && gitRoot(resolvedTarget) === gitRoot(resolvedCwd);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
102
tests/lib/own-working-tree.test.mjs
Normal file
102
tests/lib/own-working-tree.test.mjs
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
// own-working-tree.test.mjs — isOwnWorkingTree() rule (v8.1.1).
|
||||||
|
//
|
||||||
|
// v8.1.0 treated any target at or below the process cwd as the caller's own
|
||||||
|
// working tree. That is too wide: a foreign clone that sits UNDER cwd (cwd =
|
||||||
|
// $HOME, or a project with a cloned vendor dir) still had its .llm-security
|
||||||
|
// config read. v8.1.1 narrows the rule: the target must be at or below cwd
|
||||||
|
// AND have the same git root as cwd — the nearest ancestor holding a `.git`
|
||||||
|
// (directory for a clone, file for a submodule/worktree), or none for both.
|
||||||
|
// A nested clone therefore counts as foreign. The failure direction is safe:
|
||||||
|
// foreign means the target's config is ignored, so more findings, never fewer.
|
||||||
|
//
|
||||||
|
// Fixtures live under $HOME (outside os.tmpdir(), which is always foreign,
|
||||||
|
// and outside this repo's git tree); $HOME itself must not be a git root.
|
||||||
|
|
||||||
|
import { describe, it, before, after } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
|
||||||
|
import { homedir } from 'node:os';
|
||||||
|
import { spawnSync } from 'node:child_process';
|
||||||
|
import { isOwnWorkingTree } from '../../scanners/lib/own-working-tree.mjs';
|
||||||
|
import { mkOwnTreeDir } from '../helpers/own-tree.mjs';
|
||||||
|
|
||||||
|
function gitInit(dir) {
|
||||||
|
mkdirSync(dir, { recursive: true });
|
||||||
|
const r = spawnSync('git', ['init', '-q', dir], { encoding: 'utf8' });
|
||||||
|
assert.equal(r.status, 0, `git init failed: ${r.stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ownFrom(cwd, target) {
|
||||||
|
const prev = process.cwd();
|
||||||
|
process.chdir(cwd);
|
||||||
|
try {
|
||||||
|
return isOwnWorkingTree(target);
|
||||||
|
} finally {
|
||||||
|
process.chdir(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('isOwnWorkingTree(): same git root as cwd (v8.1.1)', () => {
|
||||||
|
let root;
|
||||||
|
let repo;
|
||||||
|
let homeish;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
assert.ok(!existsSync(join(homedir(), '.git')), 'precondition: $HOME is not a git root');
|
||||||
|
root = mkOwnTreeDir('owt-unit-');
|
||||||
|
// A repo the user works in, with a plain subdir, a nested clone and a
|
||||||
|
// submodule-style checkout (`.git` is a file).
|
||||||
|
repo = join(root, 'repo');
|
||||||
|
gitInit(repo);
|
||||||
|
mkdirSync(join(repo, 'sub', 'deeper'), { recursive: true });
|
||||||
|
gitInit(join(repo, 'vendor', 'clone'));
|
||||||
|
mkdirSync(join(repo, 'vendor', 'clone', 'inner'), { recursive: true });
|
||||||
|
mkdirSync(join(repo, 'submod'), { recursive: true });
|
||||||
|
writeFileSync(join(repo, 'submod', '.git'), 'gitdir: ../.git/modules/submod\n');
|
||||||
|
// A $HOME-like dir: no git root, holding one clone and one plain dir.
|
||||||
|
homeish = join(root, 'homeish');
|
||||||
|
mkdirSync(join(homeish, 'plain'), { recursive: true });
|
||||||
|
gitInit(join(homeish, 'clone'));
|
||||||
|
});
|
||||||
|
|
||||||
|
after(() => { rmSync(root, { recursive: true, force: true }); });
|
||||||
|
|
||||||
|
it('cwd itself is own', () => {
|
||||||
|
assert.equal(ownFrom(repo, repo), true);
|
||||||
|
assert.equal(ownFrom(repo, '.'), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('(b) a subdir of the same repo is own (known-positive)', () => {
|
||||||
|
assert.equal(ownFrom(repo, join(repo, 'sub')), true);
|
||||||
|
assert.equal(ownFrom(join(repo, 'sub'), join(repo, 'sub', 'deeper')), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('(a) a nested clone under the repo is foreign', () => {
|
||||||
|
assert.equal(ownFrom(repo, join(repo, 'vendor', 'clone')), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('(a) a subdir inside a nested clone is foreign', () => {
|
||||||
|
assert.equal(ownFrom(repo, join(repo, 'vendor', 'clone', 'inner')), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('a submodule/worktree checkout (`.git` file) is foreign', () => {
|
||||||
|
assert.equal(ownFrom(repo, join(repo, 'submod')), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('(c) a clone under a cwd with no git root is foreign', () => {
|
||||||
|
assert.equal(ownFrom(homeish, join(homeish, 'clone')), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('a plain subdir of a cwd with no git root is own', () => {
|
||||||
|
assert.equal(ownFrom(homeish, join(homeish, 'plain')), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('a clone the user has cd\'d into is own (it is cwd)', () => {
|
||||||
|
assert.equal(ownFrom(join(homeish, 'clone'), '.'), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('a target outside cwd is foreign', () => {
|
||||||
|
assert.equal(ownFrom(join(repo, 'sub'), repo), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
199
tests/scanners/nested-clone-scope.test.mjs
Normal file
199
tests/scanners/nested-clone-scope.test.mjs
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
// nested-clone-scope.test.mjs — a foreign clone that sits UNDER the caller's
|
||||||
|
// cwd must not configure its own scan (v8.1.1).
|
||||||
|
//
|
||||||
|
// v8.1.0 (S3b/S3c) honored .llm-security-ignore and .llm-security/policy.json
|
||||||
|
// for any target at or below cwd. A clone under cwd (cwd = $HOME, or a vendor
|
||||||
|
// clone inside a project) could therefore still ship `**` in its ignore file
|
||||||
|
// and a policy that raises the entropy thresholds and loads its own SIG rules.
|
||||||
|
// v8.1.1: own tree = at or below cwd AND the same git root as cwd.
|
||||||
|
//
|
||||||
|
// Fixture: the known HIGH entropy blob + custom SIG rule from
|
||||||
|
// policy-scope.test.mjs, plus a `**` ignore file (would suppress everything).
|
||||||
|
// (a) cwd = a repo, target = a nested clone (own `.git`) WITH the config:
|
||||||
|
// findings identical to the same clone WITHOUT the config files.
|
||||||
|
// (b) cwd = a repo, target = a plain subdir of that SAME repo with the
|
||||||
|
// policy: still honored (known-positive — keeps (a) from being vacuous).
|
||||||
|
// (c) cwd = a $HOME-like dir with no git root, target = a clone under it:
|
||||||
|
// foreign, the known finding survives.
|
||||||
|
// All under $HOME: outside os.tmpdir() (always foreign) and this repo's tree.
|
||||||
|
|
||||||
|
import { describe, it, before, after } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { resolve, dirname, join } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { spawn, spawnSync } from 'node:child_process';
|
||||||
|
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||||
|
import crypto from 'node:crypto';
|
||||||
|
import { mkOwnTreeDir } from '../helpers/own-tree.mjs';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const ORCHESTRATOR = resolve(__dirname, '../../scanners/scan-orchestrator.mjs');
|
||||||
|
|
||||||
|
// Not a real credential — a known-positive blob for the entropy scanner only.
|
||||||
|
const HIGH_ENTROPY_BLOB = crypto.randomBytes(72).toString('base64');
|
||||||
|
const CUSTOM_MARKER = 'NESTEDSCOPEMARKER_4417';
|
||||||
|
const CUSTOM_RULE_ID = 'CUSTOM-NESTED-001';
|
||||||
|
const UNREACHABLE = { entropy: 99, minLen: 1_000_000 };
|
||||||
|
|
||||||
|
function writeFixture(dir, { withPolicy, withIgnore }) {
|
||||||
|
mkdirSync(dir, { recursive: true });
|
||||||
|
writeFileSync(join(dir, 'config.js'), `const payload = "${HIGH_ENTROPY_BLOB}";\nmodule.exports = { payload };\n`);
|
||||||
|
writeFileSync(join(dir, 'notes.txt'), `prefix ${CUSTOM_MARKER} suffix\n`);
|
||||||
|
writeFileSync(join(dir, 'custom-sigs.json'), JSON.stringify({
|
||||||
|
rules: [{
|
||||||
|
id: CUSTOM_RULE_ID,
|
||||||
|
family: 'webshell',
|
||||||
|
severity: 'high',
|
||||||
|
pattern: 'NESTEDSCOPEMARKER_[0-9]+',
|
||||||
|
description: 'Target-supplied custom rule (must only load for the own working tree)',
|
||||||
|
}],
|
||||||
|
}));
|
||||||
|
if (withPolicy) {
|
||||||
|
mkdirSync(join(dir, '.llm-security'), { recursive: true });
|
||||||
|
writeFileSync(join(dir, '.llm-security', 'policy.json'), JSON.stringify({
|
||||||
|
entropy: { thresholds: { critical: UNREACHABLE, high: UNREACHABLE, medium: UNREACHABLE } },
|
||||||
|
sig: { custom_rules_path: 'custom-sigs.json' },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (withIgnore) writeFileSync(join(dir, '.llm-security-ignore'), '**\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function git(dir, args) {
|
||||||
|
const r = spawnSync('git', ['-C', dir, ...args], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
GIT_AUTHOR_NAME: 't', GIT_AUTHOR_EMAIL: 't@example.invalid',
|
||||||
|
GIT_COMMITTER_NAME: 't', GIT_COMMITTER_EMAIL: 't@example.invalid',
|
||||||
|
GIT_AUTHOR_DATE: '2026-01-01T00:00:00Z', GIT_COMMITTER_DATE: '2026-01-01T00:00:00Z',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.equal(r.status, 0, `git ${args.join(' ')} failed: ${r.stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A "clone": its own git root with every fixture file committed. */
|
||||||
|
function makeClone(dir, opts) {
|
||||||
|
writeFixture(dir, opts);
|
||||||
|
git(dir, ['init', '-q']);
|
||||||
|
git(dir, ['add', '-A']);
|
||||||
|
git(dir, ['commit', '-q', '-m', 'fixture']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runOrchestrator(target, cwd) {
|
||||||
|
return new Promise((resolveP) => {
|
||||||
|
const stdout = [];
|
||||||
|
const stderr = [];
|
||||||
|
const child = spawn('node', [ORCHESTRATOR, target], { cwd, timeout: 180_000, stdio: ['ignore', 'pipe', 'pipe'] });
|
||||||
|
child.stdout.on('data', (c) => stdout.push(c));
|
||||||
|
child.stderr.on('data', (c) => stderr.push(c));
|
||||||
|
child.on('close', (code) => {
|
||||||
|
resolveP({
|
||||||
|
code: code ?? 1,
|
||||||
|
env: JSON.parse(Buffer.concat(stdout).toString('utf8')),
|
||||||
|
stderr: Buffer.concat(stderr).toString('utf8'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const entropyFindings = (env) => env?.scanners?.entropy?.findings || [];
|
||||||
|
const customSigFindings = (env) => (env?.scanners?.sig?.findings || [])
|
||||||
|
.filter((f) => String(f.evidence || '').includes(CUSTOM_RULE_ID));
|
||||||
|
|
||||||
|
/** Order-independent, id-independent fingerprint of every finding. */
|
||||||
|
function findingKeys(env) {
|
||||||
|
const keys = [];
|
||||||
|
for (const [name, result] of Object.entries(env?.scanners || {})) {
|
||||||
|
for (const f of result.findings || []) {
|
||||||
|
keys.push(`${name}|${f.severity}|${f.title}|${f.file}|${f.line ?? ''}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('a clone under the caller\'s cwd is a foreign target (v8.1.1)', () => {
|
||||||
|
let root;
|
||||||
|
let work;
|
||||||
|
let homeish;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
root = mkOwnTreeDir('nested-clone-scope-');
|
||||||
|
work = join(root, 'work');
|
||||||
|
mkdirSync(work, { recursive: true });
|
||||||
|
git(work, ['init', '-q']);
|
||||||
|
homeish = join(root, 'homeish');
|
||||||
|
mkdirSync(homeish, { recursive: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
after(() => { rmSync(root, { recursive: true, force: true }); });
|
||||||
|
|
||||||
|
describe('(a) cwd = repo, target = nested clone with policy.json + `**` ignore', () => {
|
||||||
|
let withRun;
|
||||||
|
let withoutRun;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
// Same parent, so the only difference the scanners can see is the config.
|
||||||
|
makeClone(join(work, 'vendor', 'clone'), { withPolicy: true, withIgnore: true });
|
||||||
|
makeClone(join(work, 'vendor', 'clone-noconfig'), { withPolicy: false, withIgnore: false });
|
||||||
|
withRun = await runOrchestrator(join(work, 'vendor', 'clone'), work);
|
||||||
|
withoutRun = await runOrchestrator(join(work, 'vendor', 'clone-noconfig'), work);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('the known HIGH entropy finding survives', () => {
|
||||||
|
assert.equal(entropyFindings(withRun.env).length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('the clone-supplied custom SIG rule is not loaded', () => {
|
||||||
|
assert.equal(customSigFindings(withRun.env).length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('nothing is suppressed', () => {
|
||||||
|
assert.ok(!withRun.env.suppressed, `suppressed must be falsy, got ${withRun.env.suppressed}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('same verdict and same findings as the same clone without the config files', () => {
|
||||||
|
assert.equal(withRun.env.aggregate.verdict, withoutRun.env.aggregate.verdict);
|
||||||
|
assert.deepEqual(findingKeys(withRun.env), findingKeys(withoutRun.env));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stderr says both config files were not honored', () => {
|
||||||
|
assert.match(withRun.stderr, /\.llm-security-ignore.*ignored/i);
|
||||||
|
assert.match(withRun.stderr, /policy\.json.*ignored/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('(b) cwd = repo, target = plain subdir of the SAME repo (known-positive)', () => {
|
||||||
|
let run;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
writeFixture(join(work, 'inner'), { withPolicy: true, withIgnore: false });
|
||||||
|
run = await runOrchestrator(join(work, 'inner'), work);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('the raised thresholds silence the entropy finding', () => {
|
||||||
|
assert.equal(entropyFindings(run.env).length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('the custom SIG rule loads and fires', () => {
|
||||||
|
assert.equal(customSigFindings(run.env).length, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('(c) cwd = dir with no git root, target = clone under it', () => {
|
||||||
|
let run;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
makeClone(join(homeish, 'clone'), { withPolicy: true, withIgnore: true });
|
||||||
|
run = await runOrchestrator(join(homeish, 'clone'), homeish);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('the known HIGH entropy finding survives and nothing is suppressed', () => {
|
||||||
|
assert.equal(entropyFindings(run.env).length, 1);
|
||||||
|
assert.ok(!run.env.suppressed);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('the clone-supplied custom SIG rule is not loaded', () => {
|
||||||
|
assert.equal(customSigFindings(run.env).length, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue