/
init.ts
269 lines (253 loc) · 8.2 KB
/
init.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
import * as fs from "fs";
import * as path from "path";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as safeWhich from "@chrisgavin/safe-which";
import * as analysisPaths from "./analysis-paths";
import { GitHubApiCombinedDetails, GitHubApiDetails } from "./api-client";
import { CodeQL, CODEQL_VERSION_NEW_TRACING, setupCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { FeatureFlags } from "./feature-flags";
import { Logger } from "./logging";
import { RepositoryNwo } from "./repository";
import { TracerConfig, getCombinedTracerConfig } from "./tracer-config";
import * as util from "./util";
import { codeQlVersionAbove } from "./util";
export async function initCodeQL(
codeqlURL: string | undefined,
apiDetails: GitHubApiDetails,
tempDir: string,
toolCacheDir: string,
variant: util.GitHubVariant,
logger: Logger
): Promise<{ codeql: CodeQL; toolsVersion: string }> {
logger.startGroup("Setup CodeQL tools");
const { codeql, toolsVersion } = await setupCodeQL(
codeqlURL,
apiDetails,
tempDir,
toolCacheDir,
variant,
logger,
true
);
await codeql.printVersion();
logger.endGroup();
return { codeql, toolsVersion };
}
export async function initConfig(
languagesInput: string | undefined,
queriesInput: string | undefined,
packsInput: string | undefined,
configFile: string | undefined,
dbLocation: string | undefined,
debugMode: boolean,
debugArtifactName: string,
debugDatabaseName: string,
repository: RepositoryNwo,
tempDir: string,
toolCacheDir: string,
codeQL: CodeQL,
workspacePath: string,
gitHubVersion: util.GitHubVersion,
apiDetails: GitHubApiCombinedDetails,
featureFlags: FeatureFlags,
logger: Logger
): Promise<configUtils.Config> {
logger.startGroup("Load language configuration");
const config = await configUtils.initConfig(
languagesInput,
queriesInput,
packsInput,
configFile,
dbLocation,
debugMode,
debugArtifactName,
debugDatabaseName,
repository,
tempDir,
toolCacheDir,
codeQL,
workspacePath,
gitHubVersion,
apiDetails,
featureFlags,
logger
);
analysisPaths.printPathFiltersWarning(config, logger);
logger.endGroup();
return config;
}
export async function runInit(
codeql: CodeQL,
config: configUtils.Config,
sourceRoot: string,
processName: string | undefined,
processLevel: number | undefined
): Promise<TracerConfig | undefined> {
fs.mkdirSync(config.dbLocation, { recursive: true });
try {
if (await codeQlVersionAbove(codeql, CODEQL_VERSION_NEW_TRACING)) {
// Init a database cluster
await codeql.databaseInitCluster(
config.dbLocation,
config.languages,
sourceRoot,
processName,
processLevel
);
} else {
for (const language of config.languages) {
// Init language database
await codeql.databaseInit(
util.getCodeQLDatabasePath(config, language),
language,
sourceRoot
);
}
}
} catch (e) {
// Handle the situation where init is called twice
// for the same database in the same job.
if (
e instanceof Error &&
e.message?.includes("Refusing to create databases") &&
e.message.includes("exists and is not an empty directory.")
) {
throw new Error(
`Is the "init" action called twice in the same job? ${e.message}`
);
} else {
throw e;
}
}
return await getCombinedTracerConfig(config, codeql);
}
// Runs a powershell script to inject the tracer into a parent process
// so it can tracer future processes, hopefully including the build process.
// If processName is given then injects into the nearest parent process with
// this name, otherwise uses the processLevel-th parent if defined, otherwise
// defaults to the 3rd parent as a rough guess.
export async function injectWindowsTracer(
processName: string | undefined,
processLevel: number | undefined,
config: configUtils.Config,
codeql: CodeQL,
tracerConfig: TracerConfig
) {
let script: string;
if (processName !== undefined) {
script = `
Param(
[Parameter(Position=0)]
[String]
$tracer
)
$id = $PID
while ($true) {
$p = Get-CimInstance -Class Win32_Process -Filter "ProcessId = $id"
Write-Host "Found process: $p"
if ($p -eq $null) {
throw "Could not determine ${processName} process"
}
if ($p[0].Name -eq "${processName}") {
Break
} else {
$id = $p[0].ParentProcessId
}
}
Write-Host "Final process: $p"
Invoke-Expression "&$tracer --inject=$id"`;
} else {
// If the level is not defined then guess at the 3rd parent process.
// This won't be correct in every setting but it should be enough in most settings,
// and overestimating is likely better in this situation so we definitely trace
// what we want, though this does run the risk of interfering with future CI jobs.
// Note that the default of 3 doesn't work on github actions, so we include a
// special case in the script that checks for Runner.Worker.exe so we can still work
// on actions if the runner is invoked there.
processLevel = processLevel || 3;
script = `
Param(
[Parameter(Position=0)]
[String]
$tracer
)
$id = $PID
for ($i = 0; $i -le ${processLevel}; $i++) {
$p = Get-CimInstance -Class Win32_Process -Filter "ProcessId = $id"
Write-Host "Parent process \${i}: $p"
if ($p -eq $null) {
throw "Process tree ended before reaching required level"
}
# Special case just in case the runner is used on actions
if ($p[0].Name -eq "Runner.Worker.exe") {
Write-Host "Found Runner.Worker.exe process which means we are running on GitHub Actions"
Write-Host "Aborting search early and using process: $p"
Break
} elseif ($p[0].Name -eq "Agent.Worker.exe") {
Write-Host "Found Agent.Worker.exe process which means we are running on Azure Pipelines"
Write-Host "Aborting search early and using process: $p"
Break
} else {
$id = $p[0].ParentProcessId
}
}
Write-Host "Final process: $p"
Invoke-Expression "&$tracer --inject=$id"`;
}
const injectTracerPath = path.join(config.tempDir, "inject-tracer.ps1");
fs.writeFileSync(injectTracerPath, script);
await new toolrunner.ToolRunner(
await safeWhich.safeWhich("powershell"),
[
"-ExecutionPolicy",
"Bypass",
"-file",
injectTracerPath,
path.resolve(
path.dirname(codeql.getPath()),
"tools",
"win64",
"tracer.exe"
),
],
{ env: { ODASA_TRACER_CONFIGURATION: tracerConfig.spec } }
).exec();
}
export async function installPythonDeps(codeql: CodeQL, logger: Logger) {
logger.startGroup("Setup Python dependencies");
const scriptsFolder = path.resolve(__dirname, "../python-setup");
try {
if (process.platform === "win32") {
await new toolrunner.ToolRunner(await safeWhich.safeWhich("powershell"), [
path.join(scriptsFolder, "install_tools.ps1"),
]).exec();
} else {
await new toolrunner.ToolRunner(
path.join(scriptsFolder, "install_tools.sh")
).exec();
}
const script = "auto_install_packages.py";
if (process.platform === "win32") {
await new toolrunner.ToolRunner(await safeWhich.safeWhich("py"), [
"-3",
path.join(scriptsFolder, script),
path.dirname(codeql.getPath()),
]).exec();
} else {
await new toolrunner.ToolRunner(path.join(scriptsFolder, script), [
path.dirname(codeql.getPath()),
]).exec();
}
} catch (e) {
logger.endGroup();
logger.warning(
`An error occurred while trying to automatically install Python dependencies: ${e}\n` +
"Please make sure any necessary dependencies are installed before calling the codeql-action/analyze " +
"step, and add a 'setup-python-dependencies: false' argument to this step to disable our automatic " +
"dependency installation and avoid this warning."
);
return;
}
logger.endGroup();
}