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 23, 2022
1 parent 0789289 commit 17ee376
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ src/cli/commands/test/iac-local-execution/ @snyk/group-infrastructure-as-code
src/cli/commands/test/iac-output.ts @snyk/group-infrastructure-as-code
src/cli/commands/test/iac-test-shim.ts @snyk/group-infrastructure-as-code
src/cli/commands/describe.ts @snyk/group-infrastructure-as-code
src/cli/commands/gen-driftignore.ts @snyk/group-infrastructure-as-code
src/cli/commands/update-exclude-policy.ts @snyk/group-infrastructure-as-code
src/cli/commands/apps @snyk/moose
src/lib/apps @snyk/moose
src/lib/cloud-config-projects.ts @snyk/group-infrastructure-as-code
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
# snyk iac gen-driftignore -- generate ignore rules based on scan result
# snyk iac update-exclude-policy -- generate ignore rules based on scan result

## Usage

`snyk iac gen-driftignore [<OPTIONS>]`
`snyk iac update-exclude-policy [<OPTIONS>]`

## Description

The `snyk iac gen-driftignore` can generate driftignore rules to be used by `snyk iac scan`.

For more information see the [driftctl documentation](https://docs.driftctl.com/).
The `snyk iac update-exclude-policy` can generate exclude policy rules to be used by `snyk iac scan`.

## Exit codes

Possible exit codes and their meaning:

**0**: success, driftignore generated successfully
**1**: error, something wrong happened during ignore file generation
**0**: success, exclude rules generated successfully
**1**: error, something wrong happened during exclude rules generation

## Configure the Snyk CLI

Expand All @@ -28,14 +26,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 +42,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 update-exclude-policy
```
4 changes: 2 additions & 2 deletions src/cli/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ async function callModule(mod, args) {
const commands = {
auth: async (...args) => callModule(import('./auth'), args),
config: async (...args) => callModule(import('./config'), args),
'gen-driftignore': async (...args) =>
callModule(import('./gen-driftignore'), args),
'update-exclude-policy': async (...args) =>
callModule(import('./update-exclude-policy'), args),
describe: async (...args) => callModule(import('./describe'), args),
help: async (...args) => callModule(import('./help'), args),
ignore: async (...args) => callModule(import('./ignore'), args),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
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);

// Ensure that this gen-driftignore command can only be runned when using `snyk iac gen-driftignore`
// Avoid `snyk gen-driftignore` direct usage
// Ensure that this update-exclude-policy command can only be runned when using `snyk iac update-exclude-policy`
// Avoid `snyk update-exclude-policy` direct usage
if (options.iac != true) {
return legacyError('gen-driftignore');
return legacyError('update-exclude-policy');
}

// Ensure that we are allowed to run that command
// by checking the entitlement
const orgPublicId = options.org ?? config.org;
const iacOrgSettings = await getIacOrgSettings(orgPublicId);
if (!iacOrgSettings.entitlements?.iacDrift) {
throw new UnsupportedEntitlementCommandError('gen-driftignore', 'iacDrift');
throw new UnsupportedEntitlementCommandError(
'update-exclude-policy',
'iacDrift',
);
}

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);
const err = new Error('Error running `iac update-exclude-policy` ' + e);
return Promise.reject(err);
}
};
2 changes: 1 addition & 1 deletion src/cli/modes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const modes: Record<string, ModeData> = {
},
},
iac: {
allowedCommands: ['test', 'gen-driftignore', 'describe'],
allowedCommands: ['test', 'update-exclude-policy', 'describe'],
config: (args): [] => {
args['iac'] = true;

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
2 changes: 1 addition & 1 deletion src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export enum SupportedCliCommands {
apps = 'apps',
drift = 'drift',
describe = 'describe',
'gen-driftignore' = 'gen-driftignore',
'update-exclude-policy' = 'update-exclude-policy',
}

export interface IacFileInDirectory {
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"
}
Loading

0 comments on commit 17ee376

Please sign in to comment.