Skip to content

Commit

Permalink
feat: change gen-driftignore behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
eliecharra committed Mar 21, 2022
1 parent 0789289 commit 7d419b1
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 116 deletions.
10 changes: 1 addition & 9 deletions help/cli-commands/iac-gen-driftignore.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@ Use the `-d` option to output the debug logs.

## Options

### `--input`

Input where the JSON should be parsed from. Defaults to stdin.

### `--output`

Output file path to write the driftignore to. (default ".driftignore")

### `--exclude-changed`

Exclude resources that changed on cloud provider
Expand All @@ -52,5 +44,5 @@ Exclude resources not managed by IaC

```
$ snyk iac scan --output=json://output.json
$ snyk iac gen-driftignore --input=output.json --output=/dev/stdout
$ cat output.json | snyk iac gen-driftignore
```
16 changes: 10 additions & 6 deletions src/cli/commands/gen-driftignore.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { MethodArgs } from '../args';
import { processCommandArgs } from './process-command-args';
import * as legacyError from '../../lib/errors/legacy-errors';
import { runDriftCTL } from '../../lib/iac/drift';
import * as fs from 'fs';
import * as snykPolicyLib from 'snyk-policy';
import { getIacOrgSettings } from './test/iac-local-execution/org-settings/get-iac-org-settings';
import { UnsupportedEntitlementCommandError } from './test/iac-local-execution/assert-iac-options-flag';
import config from '../../lib/config';
import {
parseDriftAnalysisResults,
updateExcludeInPolicy,
} from '../../lib/iac/drift';

export default async (...args: MethodArgs): Promise<any> => {
const { options } = processCommandArgs(...args);
Expand All @@ -24,11 +29,10 @@ export default async (...args: MethodArgs): Promise<any> => {
}

try {
const ret = await runDriftCTL({
options: { kind: 'gen-driftignore', ...options },
stdio: 'inherit',
});
process.exit(ret.code);
const analysis = parseDriftAnalysisResults(fs.readFileSync(0).toString());
const policy = await snykPolicyLib.load();
await updateExcludeInPolicy(policy, analysis, options);
await snykPolicyLib.save(policy);
} catch (e) {
const err = new Error('Error running `iac gen-driftignore` ' + e);
return Promise.reject(err);
Expand Down
62 changes: 28 additions & 34 deletions src/lib/iac/drift.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { makeRequest } from '../request';
import config from '../../lib/config';
import * as path from 'path';
import * as crypto from 'crypto';

import {
createIgnorePattern,
verifyServiceMappingExists,
Expand Down Expand Up @@ -118,47 +119,13 @@ export const generateArgs = (options: DriftCTLOptions): string[] => {
return generateScanFlags(options as DescribeOptions);
}

if (options.kind === 'gen-driftignore') {
return generateGenDriftIgnoreFlags(options as GenDriftIgnoreOptions);
}

if (options.kind === 'fmt') {
return generateFmtFlags(options as FmtOptions);
}

throw 'Unsupported command';
};

export const generateGenDriftIgnoreFlags = (
options: GenDriftIgnoreOptions,
): string[] => {
const args: string[] = ['gen-driftignore', ...driftctlDefaultOptions];

if (options.input) {
args.push('--input');
args.push(options.input);
}

if (options.output) {
args.push('--output');
args.push(options.output);
}

if (options['exclude-changed']) {
args.push('--exclude-changed');
}

if (options['exclude-missing']) {
args.push('--exclude-missing');
}

if (options['exclude-unmanaged']) {
args.push('--exclude-unmanaged');
}

return args;
};

const generateFmtFlags = (options: FmtOptions): string[] => {
const args: string[] = ['fmt', ...driftctlDefaultOptions];

Expand Down Expand Up @@ -539,3 +506,30 @@ export function driftignoreFromPolicy(policy: Policy | undefined): string[] {
}
return policy.exclude[excludeSection];
}

export const updateExcludeInPolicy = (
policy: Policy,
analysis: DriftAnalysis,
options: GenDriftIgnoreOptions,
): void => {
const excludedResources = driftignoreFromPolicy(policy);
const addResource = (res) => excludedResources.push(`${res.type}.${res.id}`);

if (!options['exclude-changed'] && analysis.summary.total_changed > 0) {
analysis.differences?.forEach((change) => addResource(change.res));
}

if (!options['exclude-missing'] && analysis.summary.total_missing > 0) {
analysis.missing?.forEach((res) => addResource(res));
}

if (!options['exclude-unmanaged'] && analysis.summary.total_unmanaged > 0) {
analysis.unmanaged?.forEach((res) => addResource(res));
}

if (!policy.exclude) {
policy.exclude = {};
}

policy.exclude['iac-drift'] = excludedResources;
};
4 changes: 1 addition & 3 deletions src/lib/iac/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ export interface FmtOptions extends DriftCTLOptions {
'html-file-output': string;
}

export interface GenDriftIgnoreOptions extends DriftCTLOptions {
input?: string;
output?: string;
export interface GenDriftIgnoreOptions {
'exclude-changed'?: boolean;
'exclude-missing'?: boolean;
'exclude-unmanaged'?: boolean;
Expand Down
75 changes: 75 additions & 0 deletions test/fixtures/iac/drift/analysis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"options": {
"deep": true,
"only_managed": false,
"only_unmanaged": false
},
"summary": {
"total_resources": 6,
"total_changed": 1,
"total_unmanaged": 2,
"total_missing": 2,
"total_managed": 2,
"total_iac_source_count": 3
},
"managed": [
{
"id": "AKIA5QYBVVD25KFXJHYJ",
"type": "aws_iam_access_key"
},
{
"id": "test-managed",
"type": "aws_iam_user"
}
],
"unmanaged": [
{
"id": "driftctl",
"type": "aws_s3_bucket_policy"
},
{
"id": "driftctl",
"type": "aws_s3_bucket_notification"
}
],
"missing": [
{
"id": "test-driftctl2",
"type": "aws_iam_user"
},
{
"id": "AKIA5QYBVVD2Y6PBAAPY",
"type": "aws_iam_access_key"
}
],
"differences": [
{
"res": {
"id": "AKIA5QYBVVD25KFXJHYJ",
"type": "aws_iam_access_key"
},
"changelog": [
{
"type": "update",
"path": [
"status"
],
"from": "Active",
"to": "Inactive",
"computed": false
}
]
}
],
"coverage": 33,
"alerts": {
"aws_iam_access_key": [
{
"message": "This is an alert"
}
]
},
"scan_duration": 123,
"provider_name": "AWS",
"provider_version": "2.18.5"
}
54 changes: 29 additions & 25 deletions test/jest/acceptance/iac/gen-driftignore.spec.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import { startMockServer } from './helpers';
import { run as Run, startMockServer } from './helpers';
import * as os from 'os';
import * as path from 'path';
import { getFixturePath } from '../../util/getFixturePath';
import * as fs from 'fs';
import * as uuid from 'uuid';
import * as rimraf from 'rimraf';
import * as path from 'path';
import { findAndLoadPolicy } from '../../../../src/lib/policy';

jest.setTimeout(50000);

describe('iac gen-driftignore', () => {
let run: (
cmd: string,
env: Record<string, string>,
) => Promise<{ stdout: string; stderr: string; exitCode: number }>;
let run: typeof Run;
let teardown: () => void;

let tmpFolderPath: string;
let outputFile: string;
beforeEach(() => {
tmpFolderPath = fs.mkdtempSync(path.join(os.tmpdir(), 'dctl-'));
outputFile = path.join(tmpFolderPath, uuid.v4());
fs.closeSync(fs.openSync(path.join(tmpFolderPath, '.snyk'), 'w'));
});
afterEach(() => {
rimraf.sync(tmpFolderPath);
Expand Down Expand Up @@ -48,26 +43,35 @@ describe('iac gen-driftignore', () => {
return; // skip following tests
}

it('gen-driftignore successfully executed from SNYK_DRIFTCTL_PATH env var when org has the entitlement', async () => {
const { stderr, exitCode } = await run(
`snyk iac gen-driftignore --input=something.json --output=stdout --exclude-changed --exclude-missing --exclude-unmanaged`,
{
SNYK_FIXTURE_OUTPUT_PATH: outputFile,
SNYK_DRIFTCTL_PATH: path.join(
getFixturePath('iac'),
'drift',
'args-echo.sh',
),
},
it('gen-driftignore successfully executed when org has the entitlement', async () => {
const analysisPath = path.join(
__dirname,
'../../../fixtures/iac/drift/analysis.json',
);

const output = fs.readFileSync(outputFile).toString();
const snykBinaryPath = path.join(__dirname, '../../../../bin/snyk');

expect(output).toContain('DCTL_IS_SNYK=true');
expect(output).toContain(
'ARGS=gen-driftignore --no-version-check --input something.json --output stdout --exclude-changed --exclude-missing --exclude-unmanaged',
const { stderr, stdout, exitCode } = await run(
`cat ${analysisPath} | ${snykBinaryPath} iac gen-driftignore`,
{},
tmpFolderPath,
);

const policy = await findAndLoadPolicy(tmpFolderPath, 'iac', {});
const expectedExcludes = {
'iac-drift': [
'aws_iam_access_key.AKIA5QYBVVD25KFXJHYJ',
'aws_iam_user.test-driftctl2',
'aws_iam_access_key.AKIA5QYBVVD2Y6PBAAPY',
'aws_s3_bucket_policy.driftctl',
'aws_s3_bucket_notification.driftctl',
],
};

expect(stdout).toBe('');
expect(stderr).toMatch('');
expect(exitCode).toBe(0);
expect(policy).toBeDefined();
expect(policy?.exclude).toStrictEqual(expectedExcludes);
});
});
Loading

0 comments on commit 7d419b1

Please sign in to comment.