Skip to content

Commit a50dd3d

Browse files
authored
Merge pull request #101 from microsoft/main
Merge main into latest branch
2 parents 7e3060a + 52f2370 commit a50dd3d

26 files changed

+343
-396
lines changed

README.md

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ steps:
3333
- uses: actions/checkout@v3
3434

3535
- name: Run Microsoft Security DevOps
36-
uses: microsoft/security-devops-action@v1
36+
uses: microsoft/security-devops-action@latest
3737
id: msdo
3838
```
3939
@@ -50,17 +50,10 @@ To upload results to the Security tab of your repo, run the `github/codeql-actio
5050

5151
## Advanced
5252

53-
To configure **Container Mapping** to send to **Microsoft Defender for DevOps**, include `container-mapping` as a tool:
54-
```yaml
55-
- uses: microsoft/security-devops-action@v1
56-
id: msdo
57-
with:
58-
includeTools: container-mapping
59-
```
53+
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:
6054

61-
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:
6255
```yaml
63-
- uses: microsoft/security-devops-action@v1
56+
- uses: microsoft/security-devops-action@latest
6457
id: msdo
6558
with:
6659
tools: container-mapping
@@ -77,6 +70,7 @@ This will run all the analyzers defined by the configured or defaulted policy in
7770
| [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) |
7871
| [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) |
7972
| [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) |
73+
| [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) |
8074

8175
# More Information
8276

action.yml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@ branding:
66
color: 'black'
77
inputs:
88
command:
9-
description: The command to run. Defaults to run.
10-
default: all
11-
options:
12-
- all
13-
- run
14-
- pre-job
15-
- post-job
9+
description: Deprecated, do not use.
1610
config:
1711
description: A file path to a .gdnconfig file.
1812
policy:
@@ -25,7 +19,7 @@ inputs:
2519
tools:
2620
description: A comma separated list of analyzer to run. Example bandit, binskim, container-mapping, eslint, templateanalyzer, terrascan, trivy.
2721
includeTools:
28-
description: A comma separated list of analyzers to run in addition to the default set defined by the policy. Limited to container-mapping
22+
description: Deprecated
2923
outputs:
3024
sarifFile:
3125
description: A file path to a SARIF results file.

lib/container-mapping.js

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const core = __importStar(require("@actions/core"));
3838
const exec = __importStar(require("@actions/exec"));
3939
const os = __importStar(require("os"));
4040
const sendReportRetryCount = 1;
41+
const GetScanContextURL = "https://dfdinfra-afdendpoint-prod-d5fqbucbg7fue0cf.z01.azurefd.net/github/v1/auth-push/GetScanContext?context=authOnly";
42+
const ContainerMappingURL = "https://dfdinfra-afdendpoint-prod-d5fqbucbg7fue0cf.z01.azurefd.net/github/v1/container-mappings";
4143
class ContainerMapping {
4244
constructor() {
4345
this.succeedOnError = true;
@@ -90,6 +92,20 @@ class ContainerMapping {
9092
dockerEvents: [],
9193
dockerImages: []
9294
};
95+
let bearerToken = yield core.getIDToken()
96+
.then((token) => { return token; })
97+
.catch((error) => {
98+
throw new Error("Unable to get token: " + error);
99+
});
100+
if (!bearerToken) {
101+
throw new Error("Empty OIDC token received");
102+
}
103+
var callerIsOnboarded = yield this.checkCallerIsCustomer(bearerToken, sendReportRetryCount);
104+
if (!callerIsOnboarded) {
105+
core.info("Client is not onboarded to Defender for DevOps. Skipping container mapping workload.");
106+
return;
107+
}
108+
core.info("Client is onboarded for container mapping.");
93109
let dockerVersionOutput = yield exec.getExecOutput('docker --version');
94110
if (dockerVersionOutput.exitCode != 0) {
95111
core.info(`Unable to get docker version: ${dockerVersionOutput}`);
@@ -106,14 +122,6 @@ class ContainerMapping {
106122
throw new Error("Unable to get docker images: " + error);
107123
});
108124
core.debug("Finished data collection, starting API calls.");
109-
let bearerToken = yield core.getIDToken()
110-
.then((token) => { return token; })
111-
.catch((error) => {
112-
throw new Error("Unable to get token: " + error);
113-
});
114-
if (!bearerToken) {
115-
throw new Error("Empty OIDC token received");
116-
}
117125
var reportSent = yield this.sendReport(JSON.stringify(reportData), bearerToken, sendReportRetryCount);
118126
if (!reportSent) {
119127
throw new Error("Unable to send report to backend service");
@@ -160,7 +168,6 @@ class ContainerMapping {
160168
return __awaiter(this, void 0, void 0, function* () {
161169
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
162170
let apiTime = new Date().getMilliseconds();
163-
let url = "https://dfdinfra-afdendpoint-prod-d5fqbucbg7fue0cf.z01.azurefd.net/github/v1/container-mappings";
164171
let options = {
165172
method: 'POST',
166173
timeout: 2500,
@@ -170,8 +177,8 @@ class ContainerMapping {
170177
'Content-Length': data.length
171178
}
172179
};
173-
core.debug(`${options['method'].toUpperCase()} ${url}`);
174-
const req = https.request(url, options, (res) => {
180+
core.debug(`${options['method'].toUpperCase()} ${ContainerMappingURL}`);
181+
const req = https.request(ContainerMappingURL, options, (res) => {
175182
let resData = '';
176183
res.on('data', (chunk) => {
177184
resData += chunk.toString();
@@ -197,5 +204,65 @@ class ContainerMapping {
197204
}));
198205
});
199206
}
207+
checkCallerIsCustomer(bearerToken, retryCount = 0) {
208+
return __awaiter(this, void 0, void 0, function* () {
209+
return yield this._checkCallerIsCustomer(bearerToken)
210+
.then((statusCode) => __awaiter(this, void 0, void 0, function* () {
211+
if (statusCode == 200) {
212+
return true;
213+
}
214+
else if (statusCode == 403) {
215+
return false;
216+
}
217+
else {
218+
core.debug(`Unexpected status code: ${statusCode}`);
219+
return yield this.retryCall(bearerToken, retryCount);
220+
}
221+
}))
222+
.catch((error) => __awaiter(this, void 0, void 0, function* () {
223+
core.info(`Unexpected error: ${error}.`);
224+
return yield this.retryCall(bearerToken, retryCount);
225+
}));
226+
});
227+
}
228+
retryCall(bearerToken, retryCount) {
229+
return __awaiter(this, void 0, void 0, function* () {
230+
if (retryCount == 0) {
231+
core.info(`All retries failed.`);
232+
return false;
233+
}
234+
else {
235+
core.info(`Retrying checkCallerIsCustomer.\nRetry count: ${retryCount}`);
236+
retryCount--;
237+
return yield this.checkCallerIsCustomer(bearerToken, retryCount);
238+
}
239+
});
240+
}
241+
_checkCallerIsCustomer(bearerToken) {
242+
return __awaiter(this, void 0, void 0, function* () {
243+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
244+
let options = {
245+
method: 'GET',
246+
timeout: 2500,
247+
headers: {
248+
'Content-Type': 'application/json',
249+
'Authorization': 'Bearer ' + bearerToken,
250+
}
251+
};
252+
core.debug(`${options['method'].toUpperCase()} ${GetScanContextURL}`);
253+
const req = https.request(GetScanContextURL, options, (res) => {
254+
res.on('end', () => {
255+
resolve(res.statusCode);
256+
});
257+
res.on('data', function (d) {
258+
});
259+
});
260+
req.on('error', (error) => {
261+
reject(new Error(`Error calling url: ${error}`));
262+
});
263+
req.end();
264+
}));
265+
});
266+
}
200267
}
201268
exports.ContainerMapping = ContainerMapping;

lib/index.js

Lines changed: 0 additions & 131 deletions
This file was deleted.

lib/main.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
3333
};
3434
Object.defineProperty(exports, "__esModule", { value: true });
3535
const core = __importStar(require("@actions/core"));
36-
const index_1 = require("./index");
36+
const msdo_1 = require("./msdo");
37+
const msdo_interface_1 = require("./msdo-interface");
38+
const common = __importStar(require("@microsoft/security-devops-actions-toolkit/msdo-common"));
3739
const msdo_helpers_1 = require("./msdo-helpers");
38-
const runner = msdo_helpers_1.RunnerType.Main;
3940
function runMain() {
4041
return __awaiter(this, void 0, void 0, function* () {
41-
yield (0, index_1.run)(runner);
42+
if (shouldRunMain()) {
43+
yield (0, msdo_interface_1.getExecutor)(msdo_1.MicrosoftSecurityDevOps).runMain();
44+
}
45+
else {
46+
console.log("Scanning is not enabled. Skipping...");
47+
}
4248
});
4349
}
4450
runMain().catch(error => {
4551
core.setFailed(error);
4652
});
53+
function shouldRunMain() {
54+
let toolsString = core.getInput('tools');
55+
if (!common.isNullOrWhiteSpace(toolsString)) {
56+
let tools = toolsString.split(',');
57+
if (tools.length == 1 && tools[0].trim() == msdo_helpers_1.Tools.ContainerMapping) {
58+
return false;
59+
}
60+
}
61+
return true;
62+
}

lib/msdo-helpers.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
return (mod && mod.__esModule) ? mod : { "default": mod };
44
};
55
Object.defineProperty(exports, "__esModule", { value: true });
6-
exports.writeToOutStream = exports.getEncodedContent = exports.encode = exports.Constants = exports.Tools = exports.CommandType = exports.RunnerType = exports.Inputs = void 0;
6+
exports.writeToOutStream = exports.getEncodedContent = exports.encode = exports.Constants = exports.Tools = exports.RunnerType = exports.Inputs = void 0;
77
const os_1 = __importDefault(require("os"));
88
var Inputs;
99
(function (Inputs) {
@@ -21,13 +21,6 @@ var RunnerType;
2121
RunnerType["Pre"] = "pre";
2222
RunnerType["Post"] = "post";
2323
})(RunnerType || (exports.RunnerType = RunnerType = {}));
24-
var CommandType;
25-
(function (CommandType) {
26-
CommandType["All"] = "all";
27-
CommandType["PreJob"] = "pre-job";
28-
CommandType["PostJob"] = "post-job";
29-
CommandType["Run"] = "run";
30-
})(CommandType || (exports.CommandType = CommandType = {}));
3124
var Tools;
3225
(function (Tools) {
3326
Tools["Bandit"] = "bandit";

lib/msdo-interface.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
"use strict";
22
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.getExecutor = void 0;
4+
function getExecutor(runner) {
5+
return new runner();
6+
}
7+
exports.getExecutor = getExecutor;

0 commit comments

Comments
 (0)