Skip to content

Commit acff1b1

Browse files
author
FAREAST\amarnatv
committed
Added defender cli changes
1 parent 2aa03b3 commit acff1b1

4 files changed

Lines changed: 491 additions & 1 deletion

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@microsoft/security-devops-azdevops-task-lib",
3-
"version": "1.13.0",
3+
"version": "1.13.1",
44
"description": "Microsoft Security DevOps for Azure DevOps task library.",
55
"author": "Microsoft Corporation",
66
"license": "MIT",

src/defender-client.ts

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import * as path from 'path';
2+
import * as process from 'process';
3+
import * as fs from 'fs';
4+
import * as tl from 'azure-pipelines-task-lib/task';
5+
import { IExecOptions } from "azure-pipelines-task-lib/toolrunner";
6+
import * as common from './msdo-common';
7+
import * as installer from './defender-installer';
8+
9+
/**
10+
* The default version of Defender CLI to install if no version is specified.
11+
*/
12+
const cliVersionDefault: string = 'latest';
13+
14+
/**
15+
* Sets up the environment for the Defender CLI run.
16+
* Sets pipeline variables.
17+
* Resolves the version of Defender CLI to install.
18+
* Installs Defender CLI
19+
*
20+
* @param taskFolder The folder of the task that is using the Defender CLI
21+
*/
22+
async function setupEnvironment(): Promise<void> {
23+
24+
console.log('------------------------------------------------------------------------------');
25+
26+
// initialize the _defender directory
27+
let agentDirectory = path.join(process.env.AGENT_ROOTDIRECTORY, '_defender');
28+
tl.debug(`agentDirectory = ${agentDirectory}`);
29+
common.ensureDirectory(agentDirectory);
30+
31+
let agentPackagesDirectory = process.env.DEFENDER_PACKAGES_DIRECTORY;
32+
if (!agentPackagesDirectory) {
33+
agentPackagesDirectory = path.join(agentDirectory, 'packages');
34+
tl.debug(`agentPackagesDirectory = ${agentPackagesDirectory}`);
35+
common.ensureDirectory(agentPackagesDirectory);
36+
process.env.DEFENDER_PACKAGES_DIRECTORY = agentPackagesDirectory;
37+
}
38+
39+
if (!process.env.DEFENDER_FILEPATH) {
40+
let cliVersion = resolveCliVersion();
41+
await installer.install(cliVersion);
42+
}
43+
44+
console.log('------------------------------------------------------------------------------');
45+
}
46+
47+
/**
48+
* Resolves the version of Defender CLI to install.
49+
*
50+
* @returns The version of Defender CLI to install
51+
*/
52+
function resolveCliVersion(): string {
53+
let cliVersion = cliVersionDefault;
54+
55+
console.log(`Initial CLI version (default): ${cliVersion}`);
56+
57+
if (process.env.DEFENDER_VERSION) {
58+
cliVersion = process.env.DEFENDER_VERSION;
59+
console.log(`Using DEFENDER_VERSION: ${cliVersion}`);
60+
}
61+
62+
if (cliVersion.includes('*')) {
63+
console.log(`Version contains '*', switching to Latest`);
64+
cliVersion = cliVersionDefault;
65+
}
66+
67+
return cliVersion;
68+
}
69+
70+
/**
71+
* Gets the path to the Defender CLI
72+
*
73+
* @returns The path to the Defender CLI
74+
*/
75+
function getCliFilePath() : string {
76+
let cliFilePath: string = process.env.DEFENDER_FILEPATH;
77+
tl.debug(`cliFilePath = ${cliFilePath}`);
78+
return cliFilePath;
79+
}
80+
/**
81+
* Runs a Defender scan with the specified scan type and target
82+
* @param scanType - The type of scan to perform (e.g., "fs", "image")
83+
* @param target - The target to scan (directory path or image name)
84+
* @param policy - The policy to use for scanning (default: "mdc")
85+
* @param outputPath - The output SARIF file path
86+
* @param successfulExitCodes - The exit codes that are considered successful. Defaults to [0]. All others will throw an Error.
87+
*/
88+
async function scan(
89+
scanType: string,
90+
target: string,
91+
policy: string = 'mdc',
92+
outputPath?: string,
93+
successfulExitCodes: number[] = null
94+
): Promise<void> {
95+
96+
if (!outputPath) {
97+
outputPath = path.join(process.env.BUILD_STAGINGDIRECTORY || process.cwd(), 'defender.sarif');
98+
}
99+
100+
let args = [
101+
'scan',
102+
scanType,
103+
target,
104+
'--defender-policy', policy,
105+
'--defender-output', outputPath
106+
];
107+
108+
await runDefenderCli(args, successfulExitCodes);
109+
}
110+
111+
/**
112+
* Runs the Defender CLI with the specified arguments for directory scanning
113+
* @param directoryPath - The directory path to scan
114+
* @param policy - The policy to use for scanning (default: "mdc")
115+
* @param outputPath - The output SARIF file path
116+
* @param successfulExitCodes - The exit codes that are considered successful. Defaults to [0]. All others will throw an Error.
117+
*/
118+
export async function scanDirectory(
119+
directoryPath: string,
120+
policy: string = 'mdc',
121+
outputPath?: string,
122+
successfulExitCodes: number[] = null
123+
): Promise<void> {
124+
await scan('fs', directoryPath, policy, outputPath, successfulExitCodes);
125+
}
126+
127+
/**
128+
* Runs the Defender CLI with the specified arguments for container image scanning
129+
* @param imageName - The container image name to scan
130+
* @param policy - The policy to use for scanning (default: "mdc")
131+
* @param outputPath - The output SARIF file path
132+
* @param successfulExitCodes - The exit codes that are considered successful. Defaults to [0]. All others will throw an Error.
133+
*/
134+
export async function scanImage(
135+
imageName: string,
136+
policy: string = 'mdc',
137+
outputPath?: string,
138+
successfulExitCodes: number[] = null
139+
): Promise<void> {
140+
await scan('image', imageName, policy, outputPath, successfulExitCodes);
141+
}
142+
143+
/**
144+
* Runs the Defender CLI with the specified arguments
145+
* @param inputArgs - The CLI arguments to pass to the Defender CLI
146+
* @param successfulExitCodes - The exit codes that are considered successful. Defaults to [0]. All others will throw an Error.
147+
*/
148+
async function runDefenderCli(inputArgs: string[], successfulExitCodes: number[] = null): Promise<void> {
149+
let tool = null;
150+
151+
try {
152+
153+
if (successfulExitCodes == null) {
154+
successfulExitCodes = [0];
155+
}
156+
157+
await setupEnvironment();
158+
159+
let cliFilePath = getCliFilePath();
160+
161+
tool = tl.tool(cliFilePath);
162+
163+
if (inputArgs != null) {
164+
for (let i = 0; i < inputArgs.length; i++) {
165+
tool.arg(inputArgs[i]);
166+
}
167+
}
168+
169+
let systemDebug = tl.getVariable("system.debug");
170+
171+
if (systemDebug == 'true') {
172+
// Add verbose logging if system debug is enabled
173+
tool.arg('--verbose');
174+
}
175+
176+
} catch (error) {
177+
console.error('Exception occurred while initializing Defender CLI:');
178+
tl.setResult(tl.TaskResult.Failed, error);
179+
return;
180+
}
181+
182+
try {
183+
// let us parse the exit code
184+
let options: IExecOptions = <IExecOptions>{
185+
ignoreReturnCode: true
186+
};
187+
188+
tl.debug('Running Microsoft Defender CLI...');
189+
190+
let exitCode = await tool.exec(options);
191+
192+
let success = false;
193+
for (let i = 0; i < successfulExitCodes.length; i++) {
194+
if (exitCode == successfulExitCodes[i]) {
195+
success = true;
196+
break;
197+
}
198+
}
199+
200+
if (!success) {
201+
throw `Defender CLI exited with an error exit code: ${exitCode}`;
202+
}
203+
} catch (error) {
204+
tl.setResult(tl.TaskResult.Failed, error);
205+
}
206+
}
207+
208+
// Authentication is handled automatically by the scan commands, so no separate auth function is needed

0 commit comments

Comments
 (0)