diff --git a/README.md b/README.md index 0e6e33ab..9286b913 100644 --- a/README.md +++ b/README.md @@ -50,15 +50,8 @@ To upload results to the Security tab of your repo, run the `github/codeql-actio ## Advanced -To configure **Container Mapping** to send to **Microsoft Defender for DevOps**, include `container-mapping` as a tool: -```yaml -- uses: microsoft/security-devops-action@v1 - id: msdo - with: - includeTools: container-mapping -``` +To only run specific analyzers, use the `tools` command. This command is a comma-seperated list of tools to run. For example, to run only the `container-mapping` tool, configure this action as follows: -This will run all the analyzers defined by the configured or defaulted policy in addition to `container-mapping`. To only run this feature, define `container-mapping` as the only `tool` to run: ```yaml - uses: microsoft/security-devops-action@v1 id: msdo @@ -77,6 +70,7 @@ This will run all the analyzers defined by the configured or defaulted policy in | [Template Analyzer](https://github.com/Azure/template-analyzer) | Infrastructure-as-code (IaC), ARM templates, Bicep files | [MIT License](https://github.com/Azure/template-analyzer/blob/main/LICENSE.txt) | | [Terrascan](https://github.com/accurics/terrascan) | Infrastructure-as-code (IaC), Terraform (HCL2), Kubernetes (JSON/YAML), Helm v3, Kustomize, Dockerfiles, Cloudformation | [Apache License 2.0](https://github.com/accurics/terrascan/blob/master/LICENSE) | | [Trivy](https://github.com/aquasecurity/trivy) | container images, file systems, and git repositories | [Apache License 2.0](https://github.com/aquasecurity/trivy/blob/main/LICENSE) | +| [container-mapping](https://learn.microsoft.com/en-us/azure/defender-for-cloud/container-image-mapping) | container images and registries (only available for DevOps security enabled CSPM plans) | [MIT License](https://github.com/microsoft/security-devops-action/blob/main/LICENSE) | # More Information diff --git a/action.yml b/action.yml index 60312756..0e073371 100644 --- a/action.yml +++ b/action.yml @@ -6,13 +6,7 @@ branding: color: 'black' inputs: command: - description: The command to run. Defaults to run. - default: all - options: - - all - - run - - pre-job - - post-job + description: Deprecated, do not use. config: description: A file path to a .gdnconfig file. policy: @@ -25,7 +19,7 @@ inputs: tools: description: A comma separated list of analyzer to run. Example bandit, binskim, container-mapping, eslint, templateanalyzer, terrascan, trivy. includeTools: - description: A comma separated list of analyzers to run in addition to the default set defined by the policy. Limited to container-mapping + description: Deprecated outputs: sarifFile: description: A file path to a SARIF results file. diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index f51199cb..00000000 --- a/lib/index.js +++ /dev/null @@ -1,131 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.run = void 0; -const core = __importStar(require("@actions/core")); -const msdo_1 = require("./msdo"); -const msdo_helpers_1 = require("./msdo-helpers"); -const container_mapping_1 = require("./container-mapping"); -const common = __importStar(require("@microsoft/security-devops-actions-toolkit/msdo-common")); -function run(runnerString) { - return __awaiter(this, void 0, void 0, function* () { - let runner = runnerString; - let command = getCommandType(); - switch (runner) { - case msdo_helpers_1.RunnerType.Main: - yield _runMain(command); - break; - case msdo_helpers_1.RunnerType.Pre: - yield _runPreJob(command); - break; - case msdo_helpers_1.RunnerType.Post: - yield _runPostJob(command); - break; - default: - throw new Error(`Invalid source type for the task: ${runnerString}`); - } - }); -} -exports.run = run; -function _runPreJob(command) { - return __awaiter(this, void 0, void 0, function* () { - if (command != msdo_helpers_1.CommandType.All) { - return; - } - if (_toolIsEnabled(msdo_helpers_1.Tools.ContainerMapping)) { - yield _getExecutor(container_mapping_1.ContainerMapping).runPreJob(); - } - }); -} -function _runPostJob(command) { - return __awaiter(this, void 0, void 0, function* () { - if (command != msdo_helpers_1.CommandType.All) { - return; - } - if (_toolIsEnabled(msdo_helpers_1.Tools.ContainerMapping)) { - yield _getExecutor(container_mapping_1.ContainerMapping).runPostJob(); - } - }); -} -function _runMain(command) { - return __awaiter(this, void 0, void 0, function* () { - if (command == msdo_helpers_1.CommandType.PreJob) { - yield _runPreJob(command); - } - else if (command == msdo_helpers_1.CommandType.PostJob) { - yield _runPostJob(command); - } - else if (command == msdo_helpers_1.CommandType.All || command == msdo_helpers_1.CommandType.Run) { - if (_toolIsEnabledOnInput(msdo_helpers_1.Inputs.Tools, msdo_helpers_1.Tools.ContainerMapping, true)) { - console.log("Scanning is not enabled. Skipping..."); - } - else { - yield _getExecutor(msdo_1.MicrosoftSecurityDevOps).runMain(); - } - } - else { - throw new Error(`Invalid command type for the main task: ${command}`); - } - }); -} -function _toolIsEnabled(toolName) { - let enabled = false; - enabled = _toolIsEnabledOnInput(msdo_helpers_1.Inputs.Tools, toolName, false); - if (!enabled) { - enabled = _toolIsEnabledOnInput(msdo_helpers_1.Inputs.IncludeTools, toolName, false); - } - return enabled; -} -function _toolIsEnabledOnInput(inputName, toolName, isOnlyTool = false) { - let enabled = false; - let toolsString = core.getInput(inputName); - if (!common.isNullOrWhiteSpace(toolsString)) { - let tools = toolsString.split(','); - if (isOnlyTool && tools.length > 1) { - enabled = false; - } - else { - const toolIndex = tools.indexOf(toolName); - enabled = toolIndex > -1; - } - } - return enabled; -} -function _getExecutor(runner) { - return new runner(); -} -function getCommandType() { - const commandTypeString = core.getInput(msdo_helpers_1.Inputs.Command) || msdo_helpers_1.CommandType.Run; - return commandTypeString; -} diff --git a/lib/main.js b/lib/main.js index b2b00d1b..f857f586 100644 --- a/lib/main.js +++ b/lib/main.js @@ -33,14 +33,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", { value: true }); const core = __importStar(require("@actions/core")); -const index_1 = require("./index"); +const msdo_1 = require("./msdo"); +const msdo_interface_1 = require("./msdo-interface"); +const common = __importStar(require("@microsoft/security-devops-actions-toolkit/msdo-common")); const msdo_helpers_1 = require("./msdo-helpers"); -const runner = msdo_helpers_1.RunnerType.Main; function runMain() { return __awaiter(this, void 0, void 0, function* () { - yield (0, index_1.run)(runner); + if (shouldRunMain()) { + yield (0, msdo_interface_1.getExecutor)(msdo_1.MicrosoftSecurityDevOps).runMain(); + } + else { + console.log("Scanning is not enabled. Skipping..."); + } }); } runMain().catch(error => { core.setFailed(error); }); +function shouldRunMain() { + let toolsString = core.getInput('tools'); + if (!common.isNullOrWhiteSpace(toolsString)) { + let tools = toolsString.split(','); + if (tools.length == 1 && tools[0].trim() == msdo_helpers_1.Tools.ContainerMapping) { + return false; + } + } + return true; +} diff --git a/lib/msdo-helpers.js b/lib/msdo-helpers.js index b647de16..ead0ff5e 100644 --- a/lib/msdo-helpers.js +++ b/lib/msdo-helpers.js @@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.writeToOutStream = exports.getEncodedContent = exports.encode = exports.Constants = exports.Tools = exports.CommandType = exports.RunnerType = exports.Inputs = void 0; +exports.writeToOutStream = exports.getEncodedContent = exports.encode = exports.Constants = exports.Tools = exports.RunnerType = exports.Inputs = void 0; const os_1 = __importDefault(require("os")); var Inputs; (function (Inputs) { @@ -21,13 +21,6 @@ var RunnerType; RunnerType["Pre"] = "pre"; RunnerType["Post"] = "post"; })(RunnerType || (exports.RunnerType = RunnerType = {})); -var CommandType; -(function (CommandType) { - CommandType["All"] = "all"; - CommandType["PreJob"] = "pre-job"; - CommandType["PostJob"] = "post-job"; - CommandType["Run"] = "run"; -})(CommandType || (exports.CommandType = CommandType = {})); var Tools; (function (Tools) { Tools["Bandit"] = "bandit"; diff --git a/lib/msdo-interface.js b/lib/msdo-interface.js index c8ad2e54..ed538cfb 100644 --- a/lib/msdo-interface.js +++ b/lib/msdo-interface.js @@ -1,2 +1,7 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getExecutor = void 0; +function getExecutor(runner) { + return new runner(); +} +exports.getExecutor = getExecutor; diff --git a/lib/msdo.js b/lib/msdo.js index 5f2133ff..0d2ef306 100644 --- a/lib/msdo.js +++ b/lib/msdo.js @@ -104,23 +104,6 @@ class MicrosoftSecurityDevOps { } } } - let includeToolsString = core.getInput('includeTools'); - if (!common.isNullOrWhiteSpace(includeToolsString)) { - let includeTools = includeToolsString.split(','); - for (let i = 0; i < includeTools.length; i++) { - let includeTool = includeTools[i]; - let toolTrimmed = includeTool.trim(); - if (!common.isNullOrWhiteSpace(includeTool) - && includeTool != msdo_helpers_1.Tools.ContainerMapping - && includedTools.indexOf(toolTrimmed) == -1) { - if (includedTools.length == 0) { - args.push('--tool'); - } - args.push(toolTrimmed); - includedTools.push(toolTrimmed); - } - } - } args.push('--github'); yield client.run(args, 'microsoft/security-devops-action'); }); diff --git a/lib/post.js b/lib/post.js index a0f4d7f0..ca4f9b68 100644 --- a/lib/post.js +++ b/lib/post.js @@ -33,12 +33,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", { value: true }); const core = __importStar(require("@actions/core")); -const index_1 = require("./index"); -const msdo_helpers_1 = require("./msdo-helpers"); -const runner = msdo_helpers_1.RunnerType.Post; +const container_mapping_1 = require("./container-mapping"); +const msdo_interface_1 = require("./msdo-interface"); function runPost() { return __awaiter(this, void 0, void 0, function* () { - yield (0, index_1.run)(runner); + yield (0, msdo_interface_1.getExecutor)(container_mapping_1.ContainerMapping).runPostJob(); }); } runPost().catch((error) => { diff --git a/lib/pre.js b/lib/pre.js index caa16d78..1305f979 100644 --- a/lib/pre.js +++ b/lib/pre.js @@ -33,12 +33,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", { value: true }); const core = __importStar(require("@actions/core")); -const index_1 = require("./index"); -const msdo_helpers_1 = require("./msdo-helpers"); -const runner = msdo_helpers_1.RunnerType.Pre; +const container_mapping_1 = require("./container-mapping"); +const msdo_interface_1 = require("./msdo-interface"); function runPre() { return __awaiter(this, void 0, void 0, function* () { - yield (0, index_1.run)(runner); + yield (0, msdo_interface_1.getExecutor)(container_mapping_1.ContainerMapping).runPreJob(); }); } runPre().catch((error) => { diff --git a/src/container-mapping.ts b/src/container-mapping.ts index 22fc8347..67dc1f82 100644 --- a/src/container-mapping.ts +++ b/src/container-mapping.ts @@ -1,4 +1,3 @@ -import { CommandType, Constants, getEncodedContent, writeToOutStream } from "./msdo-helpers"; import { IMicrosoftSecurityDevOps } from "./msdo-interface"; import * as https from "https"; import * as core from '@actions/core'; diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 79946cc2..00000000 --- a/src/index.ts +++ /dev/null @@ -1,125 +0,0 @@ -import * as core from '@actions/core'; -import { MicrosoftSecurityDevOps } from './msdo'; -import { CommandType, Inputs, RunnerType, Tools } from './msdo-helpers'; -import { IMicrosoftSecurityDevOps, IMicrosoftSecurityDevOpsFactory } from './msdo-interface'; -import { ContainerMapping } from './container-mapping'; -import * as common from '@microsoft/security-devops-actions-toolkit/msdo-common'; - -/** - * Runs the action. - * @param runnerString The runner where the task is being run: main, pre, or post. - */ -export async function run(runnerString: string) { - let runner = runnerString as RunnerType; - let command: CommandType = getCommandType(); - - switch (runner) { - case RunnerType.Main: - await _runMain(command); - break; - case RunnerType.Pre: - await _runPreJob(command); - break; - case RunnerType.Post: - await _runPostJob(command); - break; - default: - throw new Error(`Invalid source type for the task: ${runnerString}`); - } -} - -async function _runPreJob(command: CommandType) { - if (command != CommandType.All) { - return; - } - // if explicit PreJob, will run in main - if (_toolIsEnabled(Tools.ContainerMapping)) { - await _getExecutor(ContainerMapping).runPreJob(); - } -} - -async function _runPostJob(command: CommandType) { - if (command != CommandType.All) { - return; - } - // if explicit PostJob, will run in main - if (_toolIsEnabled(Tools.ContainerMapping)) { - await _getExecutor(ContainerMapping).runPostJob(); - } -} - -async function _runMain(command: CommandType) { - if (command == CommandType.PreJob) { - // Explicit pre-job - await _runPreJob(command); - } else if (command == CommandType.PostJob) { - // Explicit post-job - await _runPostJob(command); - } else if (command == CommandType.All || command == CommandType.Run) { - // Run main - // If container-mapping is the only enabled tool, then skip scanning - if (_toolIsEnabledOnInput(Inputs.Tools, Tools.ContainerMapping, true)) { - console.log("Scanning is not enabled. Skipping..."); - } else { - await _getExecutor(MicrosoftSecurityDevOps).runMain(); - } - } else { - throw new Error(`Invalid command type for the main task: ${command}`); - } -} - -/** - * Returns true if the tool is enabled on either the tools or includeTools inputs. - * @param toolName - The name of the tool. - * @returns True if the tool is enabled in the inputs. - */ -function _toolIsEnabled(toolName: string): boolean { - let enabled: boolean = false; - - enabled = _toolIsEnabledOnInput(Inputs.Tools, toolName, false); - - if (!enabled) { - // See if the tool is in includeTools - enabled = _toolIsEnabledOnInput(Inputs.IncludeTools, toolName, false); - } - - return enabled; -} - -/** - * Returns true if the tool is enabled on the specified input. - * @param inputName The action input name to check for the list of tools. Values tools or includeTools. - * @param toolName The name of the tool to look for. - * @param isOnlyTool Return true only if it is the only tool. - * @returns True if the tool is enabled on the specified input. - */ -function _toolIsEnabledOnInput(inputName: string, toolName: string, isOnlyTool: boolean = false) { - let enabled: boolean = false; - let toolsString: string = core.getInput(inputName); - if (!common.isNullOrWhiteSpace(toolsString)) { - let tools = toolsString.split(','); - if (isOnlyTool && tools.length > 1) { - enabled = false; - } else { - const toolIndex = tools.indexOf(toolName); - enabled = toolIndex > -1; - } - } - return enabled; -} - -/** - * Returns an instance of IMicrosoftSecurityDevOps based on the input runner and command type. - * (This is used to enforce strong typing for the inputs for the runner). - * @param runner - The runner to use to create the instance of IMicrosoftSecurityDevOps. - * @param commandType - The input command type. - * @returns An instance of IMicrosoftSecurityDevOps. - */ -function _getExecutor(runner: IMicrosoftSecurityDevOpsFactory): IMicrosoftSecurityDevOps { - return new runner(); -} - -function getCommandType(): CommandType { - const commandTypeString: string = core.getInput(Inputs.Command) || CommandType.Run; - return commandTypeString as CommandType; -} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 778acb12..1f45f9d1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,34 @@ import * as core from '@actions/core'; -import { run } from './index'; -import { RunnerType } from './msdo-helpers'; - -const runner = RunnerType.Main; +import { MicrosoftSecurityDevOps } from './msdo'; +import { getExecutor } from './msdo-interface'; +import * as common from '@microsoft/security-devops-actions-toolkit/msdo-common'; +import { Tools } from './msdo-helpers'; async function runMain() { - await run(runner); + if (shouldRunMain()) + { + await getExecutor(MicrosoftSecurityDevOps).runMain(); + } + else { + console.log("Scanning is not enabled. Skipping..."); + } } runMain().catch(error => { core.setFailed(error); -}); \ No newline at end of file +}); + +/** + * Returns false if the 'tools' input is specified and the only tool on the list is 'container-mapping'. + * This is because the MicrosoftSecurityDevOps executer does not have a workload for the container-mapping tool. +*/ +function shouldRunMain() { + let toolsString: string = core.getInput('tools'); + if (!common.isNullOrWhiteSpace(toolsString)) { + let tools = toolsString.split(','); + if (tools.length == 1 && tools[0].trim() == Tools.ContainerMapping) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/src/msdo-helpers.ts b/src/msdo-helpers.ts index dc0a198e..d12e9e18 100644 --- a/src/msdo-helpers.ts +++ b/src/msdo-helpers.ts @@ -23,16 +23,6 @@ export enum RunnerType { Post = 'post' } -/* -* Enum for the possible values for the Inputs.CommandType (specified in action.yml) -*/ -export enum CommandType { - All = 'all', - PreJob = 'pre-job', - PostJob = 'post-job', - Run = 'run' -} - /* * Enum for the possible values for the Inputs.Tools (specified in action.yml) */ diff --git a/src/msdo-interface.ts b/src/msdo-interface.ts index cb7afd3a..af50977e 100644 --- a/src/msdo-interface.ts +++ b/src/msdo-interface.ts @@ -1,5 +1,3 @@ -import { CommandType } from "./msdo-helpers"; - /* * Interface for the MicrosoftSecurityDevOps task */ @@ -17,4 +15,15 @@ export interface IMicrosoftSecurityDevOps { */ export interface IMicrosoftSecurityDevOpsFactory { new (): IMicrosoftSecurityDevOps; +} + +/** + * Returns an instance of IMicrosoftSecurityDevOps based on the input runner and command type. + * (This is used to enforce strong typing for the inputs for the runner). + * @param runner - The runner to use to create the instance of IMicrosoftSecurityDevOps. + * @param commandType - The input command type. + * @returns An instance of IMicrosoftSecurityDevOps. + */ +export function getExecutor(runner: IMicrosoftSecurityDevOpsFactory): IMicrosoftSecurityDevOps { + return new runner(); } \ No newline at end of file diff --git a/src/msdo.ts b/src/msdo.ts index e8e886c9..bdbd0e4a 100644 --- a/src/msdo.ts +++ b/src/msdo.ts @@ -73,25 +73,7 @@ export class MicrosoftSecurityDevOps implements IMicrosoftSecurityDevOps { let tool = tools[i]; let toolTrimmed = tool.trim(); if (!common.isNullOrWhiteSpace(tool) - && tool != Tools.ContainerMapping - && includedTools.indexOf(toolTrimmed) == -1) { - if (includedTools.length == 0) { - args.push('--tool'); - } - args.push(toolTrimmed); - includedTools.push(toolTrimmed); - } - } - } - - let includeToolsString: string = core.getInput('includeTools'); - if (!common.isNullOrWhiteSpace(includeToolsString)) { - let includeTools = includeToolsString.split(','); - for (let i = 0; i < includeTools.length; i++) { - let includeTool = includeTools[i]; - let toolTrimmed = includeTool.trim(); - if (!common.isNullOrWhiteSpace(includeTool) - && includeTool != Tools.ContainerMapping + && tool != Tools.ContainerMapping // This tool is not handled by this executor && includedTools.indexOf(toolTrimmed) == -1) { if (includedTools.length == 0) { args.push('--tool'); diff --git a/src/post.ts b/src/post.ts index d3ed363a..ab75224f 100644 --- a/src/post.ts +++ b/src/post.ts @@ -1,11 +1,9 @@ import * as core from '@actions/core'; -import { run } from './index'; -import { RunnerType } from './msdo-helpers'; - -const runner = RunnerType.Post; +import { ContainerMapping } from './container-mapping'; +import { getExecutor } from './msdo-interface'; async function runPost() { - await run(runner); + await getExecutor(ContainerMapping).runPostJob(); } runPost().catch((error) => { diff --git a/src/pre.ts b/src/pre.ts index f4ffe392..f717e43a 100644 --- a/src/pre.ts +++ b/src/pre.ts @@ -1,11 +1,9 @@ import * as core from '@actions/core'; -import { run } from './index'; -import { RunnerType } from './msdo-helpers'; - -const runner = RunnerType.Pre; +import { ContainerMapping } from './container-mapping'; +import { getExecutor } from './msdo-interface'; async function runPre() { - await run(runner); + await getExecutor(ContainerMapping).runPreJob(); } runPre().catch((error) => {