Merge remote-tracking branch 'origin/main' into dbartol/join-order

This commit is contained in:
Dave Bartolomeo
2022-04-27 17:50:50 -04:00
39 changed files with 1542 additions and 215 deletions

View File

@@ -135,7 +135,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
version: ['v2.3.3', 'v2.4.6', 'v2.5.9', 'v2.6.3', 'v2.7.6', 'v2.8.5', 'nightly']
version: ['v2.3.3', 'v2.4.6', 'v2.5.9', 'v2.6.3', 'v2.7.6', 'v2.8.5', 'v2.9.0', 'nightly']
env:
CLI_VERSION: ${{ matrix.version }}
NIGHTLY_URL: ${{ needs.find-nightly.outputs.url }}

View File

@@ -2,6 +2,8 @@
## [UNRELEASED]
## 1.6.5 - 25 April 2022
- Re-enable publishing to open-vsx. [#1285](https://github.com/github/vscode-codeql/pull/1285)
## 1.6.4 - 6 April 2022

View File

@@ -1,12 +1,12 @@
{
"name": "vscode-codeql",
"version": "1.6.5",
"version": "1.6.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "vscode-codeql",
"version": "1.6.5",
"version": "1.6.6",
"license": "MIT",
"dependencies": {
"@octokit/rest": "^18.5.6",

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code",
"author": "GitHub",
"private": true,
"version": "1.6.5",
"version": "1.6.6",
"publisher": "GitHub",
"license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -739,12 +739,12 @@
{
"command": "codeQLQueryHistory.showEvalLog",
"group": "9_qlCommands",
"when": "codeql.supportsEvalLog && (viewItem == rawResultsItem || viewItem == interpretedResultsItem || viewItem == cancelledResultsItem)"
"when": "codeql.supportsEvalLog && viewItem == rawResultsItem || codeql.supportsEvalLog && viewItem == interpretedResultsItem || codeql.supportsEvalLog && viewItem == cancelledResultsItem"
},
{
"command": "codeQLQueryHistory.showEvalLogSummary",
"group": "9_qlCommands",
"when": "codeql.supportsEvalLog && (viewItem == rawResultsItem || viewItem == interpretedResultsItem || viewItem == cancelledResultsItem)"
"when": "codeql.supportsEvalLog && viewItem == rawResultsItem || codeql.supportsEvalLog && viewItem == interpretedResultsItem || codeql.supportsEvalLog && viewItem == cancelledResultsItem"
},
{
"command": "codeQLQueryHistory.scanEvalLog",

View File

@@ -51,6 +51,7 @@ export async function promptImportInternetDatabase(
{},
databaseManager,
storagePath,
undefined,
progress,
token,
cli
@@ -98,11 +99,13 @@ export async function promptImportGithubDatabase(
throw new Error(`Invalid GitHub repository: ${githubRepo}`);
}
const databaseUrl = await convertGithubNwoToDatabaseUrl(githubRepo, credentials, progress);
if (!databaseUrl) {
const result = await convertGithubNwoToDatabaseUrl(githubRepo, credentials, progress);
if (!result) {
return;
}
const { databaseUrl, name, owner } = result;
const octokit = await credentials.getOctokit();
/**
* The 'token' property of the token object returned by `octokit.auth()`.
@@ -125,6 +128,7 @@ export async function promptImportGithubDatabase(
{ 'Accept': 'application/zip', 'Authorization': `Bearer ${octokitToken}` },
databaseManager,
storagePath,
`${owner}/${name}`,
progress,
token,
cli
@@ -173,6 +177,7 @@ export async function promptImportLgtmDatabase(
{},
databaseManager,
storagePath,
undefined,
progress,
token,
cli
@@ -220,6 +225,7 @@ export async function importArchiveDatabase(
{},
databaseManager,
storagePath,
undefined,
progress,
token,
cli
@@ -247,6 +253,7 @@ export async function importArchiveDatabase(
* @param requestHeaders Headers to send with the request
* @param databaseManager the DatabaseManager
* @param storagePath where to store the unzipped database.
* @param nameOverride a name for the database that overrides the default
* @param progress callback to send progress messages to
* @param token cancellation token
*/
@@ -255,6 +262,7 @@ async function databaseArchiveFetcher(
requestHeaders: { [key: string]: string },
databaseManager: DatabaseManager,
storagePath: string,
nameOverride: string | undefined,
progress: ProgressCallback,
token: CancellationToken,
cli?: CodeQLCliServer,
@@ -296,7 +304,7 @@ async function databaseArchiveFetcher(
});
await ensureZippedSourceLocation(dbPath);
const item = await databaseManager.openDatabase(progress, token, Uri.file(dbPath));
const item = await databaseManager.openDatabase(progress, token, Uri.file(dbPath), nameOverride);
await databaseManager.setCurrentDatabaseItem(item);
return item;
} else {
@@ -409,7 +417,6 @@ async function fetchAndUnzip(
await readAndUnzip(Uri.file(archivePath).toString(true), unzipPath, cli, progress);
// remove archivePath eagerly since these archives can be large.
await fs.remove(archivePath);
}
@@ -517,7 +524,11 @@ function convertGitHubUrlToNwo(githubUrl: string): string | undefined {
export async function convertGithubNwoToDatabaseUrl(
githubRepo: string,
credentials: Credentials,
progress: ProgressCallback): Promise<string | undefined> {
progress: ProgressCallback): Promise<{
databaseUrl: string,
owner: string,
name: string
} | undefined> {
try {
const nwo = convertGitHubUrlToNwo(githubRepo) || githubRepo;
const [owner, repo] = nwo.split('/');
@@ -532,7 +543,11 @@ export async function convertGithubNwoToDatabaseUrl(
return;
}
return `https://api.github.com/repos/${owner}/${repo}/code-scanning/codeql/databases/${language}`;
return {
databaseUrl: `https://api.github.com/repos/${owner}/${repo}/code-scanning/codeql/databases/${language}`,
owner,
name: repo
};
} catch (e) {
void logger.log(`Error: ${getErrorMessage(e)}`);

View File

@@ -148,7 +148,7 @@ export async function findSourceArchive(
}
async function resolveDatabase(
databasePath: string
databasePath: string,
): Promise<DatabaseContents> {
const name = path.basename(databasePath);
@@ -170,7 +170,9 @@ async function getDbSchemeFiles(dbDirectory: string): Promise<string[]> {
return await glob('*.dbscheme', { cwd: dbDirectory });
}
async function resolveDatabaseContents(uri: vscode.Uri): Promise<DatabaseContents> {
async function resolveDatabaseContents(
uri: vscode.Uri,
): Promise<DatabaseContents> {
if (uri.scheme !== 'file') {
throw new Error(`Database URI scheme '${uri.scheme}' not supported; only 'file' URIs are supported.`);
}
@@ -569,14 +571,15 @@ export class DatabaseManager extends DisposableObject {
progress: ProgressCallback,
token: vscode.CancellationToken,
uri: vscode.Uri,
displayName?: string
): Promise<DatabaseItem> {
const contents = await resolveDatabaseContents(uri);
// Ignore the source archive for QLTest databases by default.
const isQLTestDatabase = path.extname(uri.fsPath) === '.testproj';
const fullOptions: FullDatabaseOptions = {
ignoreSourceArchive: isQLTestDatabase,
// displayName is only set if a user explicitly renames a database
displayName: undefined,
// If a displayName is not passed in, the basename of folder containing the database is used.
displayName,
dateAdded: Date.now(),
language: await this.getPrimaryLanguage(uri.fsPath)
};

View File

@@ -76,9 +76,10 @@ export async function showAndLogWarningMessage(message: string, {
*/
export async function showAndLogInformationMessage(message: string, {
outputLogger = logger,
items = [] as string[]
items = [] as string[],
fullMessage = ''
} = {}): Promise<string | undefined> {
return internalShowAndLog(message, items, outputLogger, Window.showInformationMessage);
return internalShowAndLog(message, items, outputLogger, Window.showInformationMessage, fullMessage);
}
type ShowMessageFn = (message: string, ...items: string[]) => Thenable<string | undefined>;

View File

@@ -42,7 +42,10 @@ export async function getRemoteQueryIndex(
const artifactsUrlPath = `/repos/${owner}/${repoName}/actions/artifacts`;
const artifactList = await listWorkflowRunArtifacts(credentials, owner, repoName, workflowRunId);
const resultIndexArtifactId = getArtifactIDfromName('result-index', workflowUri, artifactList);
const resultIndexArtifactId = tryGetArtifactIDfromName('result-index', artifactList);
if (!resultIndexArtifactId) {
return undefined;
}
const resultIndex = await getResultIndex(credentials, owner, repoName, resultIndexArtifactId);
const successes = resultIndex?.successes.map(item => {
@@ -223,15 +226,29 @@ function getArtifactIDfromName(
workflowUri: string,
artifacts: Array<{ id: number, name: string }>
): number {
const artifact = artifacts.find(a => a.name === artifactName);
const artifactId = tryGetArtifactIDfromName(artifactName, artifacts);
if (!artifact) {
if (!artifactId) {
const errorMessage =
`Could not find artifact with name ${artifactName} in workflow ${workflowUri}.
Please check whether the workflow run has successfully completed.`;
throw Error(errorMessage);
}
return artifactId;
}
/**
* @param artifactName The artifact name, as a string.
* @param artifacts An array of artifact details (from the "list workflow run artifacts" API response).
* @returns The artifact ID corresponding to the given artifact name, if it exists.
*/
function tryGetArtifactIDfromName(
artifactName: string,
artifacts: Array<{ id: number, name: string }>
): number | undefined {
const artifact = artifacts.find(a => a.name === artifactName);
return artifact?.id;
}

View File

@@ -30,6 +30,7 @@ import { AnalysisResults } from './shared/analysis-result';
export class RemoteQueriesInterfaceManager {
private panel: WebviewPanel | undefined;
private panelLoaded = false;
private currentQueryId: string | undefined;
private panelLoadedCallBacks: (() => void)[] = [];
constructor(
@@ -47,6 +48,8 @@ export class RemoteQueriesInterfaceManager {
await this.waitForPanelLoaded();
const model = this.buildViewModel(query, queryResult);
this.currentQueryId = queryResult.queryId;
await this.postMessage({
t: 'setRemoteQueryResult',
queryResult: model
@@ -55,7 +58,7 @@ export class RemoteQueriesInterfaceManager {
// Ensure all pre-downloaded artifacts are loaded into memory
await this.analysesResultsManager.loadDownloadedAnalyses(model.analysisSummaries);
await this.setAnalysisResults(this.analysesResultsManager.getAnalysesResults(queryResult.queryId));
await this.setAnalysisResults(this.analysesResultsManager.getAnalysesResults(queryResult.queryId), queryResult.queryId);
}
/**
@@ -111,6 +114,7 @@ export class RemoteQueriesInterfaceManager {
this.panel.onDidDispose(
() => {
this.panel = undefined;
this.currentQueryId = undefined;
},
null,
ctx.subscriptions
@@ -212,23 +216,25 @@ export class RemoteQueriesInterfaceManager {
}
private async downloadAnalysisResults(msg: RemoteQueryDownloadAnalysisResultsMessage): Promise<void> {
const queryId = this.currentQueryId;
await this.analysesResultsManager.downloadAnalysisResults(
msg.analysisSummary,
results => this.setAnalysisResults(results));
results => this.setAnalysisResults(results, queryId));
}
private async downloadAllAnalysesResults(msg: RemoteQueryDownloadAllAnalysesResultsMessage): Promise<void> {
const queryId = this.currentQueryId;
await this.analysesResultsManager.loadAnalysesResults(
msg.analysisSummaries,
undefined,
results => this.setAnalysisResults(results));
results => this.setAnalysisResults(results, queryId));
}
public async setAnalysisResults(analysesResults: AnalysisResults[]): Promise<void> {
if (this.panel?.active) {
public async setAnalysisResults(analysesResults: AnalysisResults[], queryId: string | undefined): Promise<void> {
if (this.panel?.active && this.currentQueryId === queryId) {
await this.postMessage({
t: 'setAnalysesResults',
analysesResults: analysesResults
analysesResults
});
}
}

View File

@@ -131,33 +131,13 @@ export class RemoteQueriesManager extends DisposableObject {
const executionEndTime = Date.now();
if (queryWorkflowResult.status === 'CompletedSuccessfully') {
const resultIndex = await getRemoteQueryIndex(credentials, queryItem.remoteQuery);
queryItem.completed = true;
if (resultIndex) {
queryItem.status = QueryStatus.Completed;
const queryResult = this.mapQueryResult(executionEndTime, resultIndex, queryItem.queryId);
await this.storeJsonFile(queryItem, 'query-result.json', queryResult);
// Kick off auto-download of results in the background.
void commands.executeCommand('codeQL.autoDownloadRemoteQueryResults', queryResult);
// Ask if the user wants to open the results in the background.
void this.askToOpenResults(queryItem.remoteQuery, queryResult).then(
noop,
err => {
void showAndLogErrorMessage(err);
}
);
} else {
void showAndLogErrorMessage(`There was an issue retrieving the result for the query ${queryItem.remoteQuery.queryName}`);
queryItem.status = QueryStatus.Failed;
}
await this.downloadAvailableResults(queryItem, credentials, executionEndTime);
} else if (queryWorkflowResult.status === 'CompletedUnsuccessfully') {
if (queryWorkflowResult.error?.includes('cancelled')) {
// workflow was cancelled on the server
queryItem.failureReason = 'Cancelled';
queryItem.status = QueryStatus.Failed;
await this.downloadAvailableResults(queryItem, credentials, executionEndTime);
void showAndLogInformationMessage('Variant analysis was cancelled');
} else {
queryItem.failureReason = queryWorkflowResult.error;
@@ -167,7 +147,8 @@ export class RemoteQueriesManager extends DisposableObject {
} else if (queryWorkflowResult.status === 'Cancelled') {
queryItem.failureReason = 'Cancelled';
queryItem.status = QueryStatus.Failed;
void showAndLogErrorMessage('Variant analysis was cancelled');
await this.downloadAvailableResults(queryItem, credentials, executionEndTime);
void showAndLogInformationMessage('Variant analysis was cancelled');
} else if (queryWorkflowResult.status === 'InProgress') {
// Should not get here. Only including this to ensure `assertNever` uses proper type checking.
void showAndLogErrorMessage(`Unexpected status: ${queryWorkflowResult.status}`);
@@ -196,7 +177,7 @@ export class RemoteQueriesManager extends DisposableObject {
await this.analysesResultsManager.loadAnalysesResults(
analysesToDownload,
token,
results => this.interfaceManager.setAnalysisResults(results));
results => this.interfaceManager.setAnalysisResults(results, queryResult.queryId));
}
private mapQueryResult(executionEndTime: number, resultIndex: RemoteQueryResultIndex, queryId: string): RemoteQueryResult {
@@ -282,4 +263,42 @@ export class RemoteQueriesManager extends DisposableObject {
const filePath = path.join(this.storagePath, queryItem.queryId);
return await fs.pathExists(filePath);
}
/**
* Checks whether there's a result index artifact available for the given query.
* If so, set the query status to `Completed` and auto-download the results.
*/
private async downloadAvailableResults(
queryItem: RemoteQueryHistoryItem,
credentials: Credentials,
executionEndTime: number
): Promise<void> {
const resultIndex = await getRemoteQueryIndex(credentials, queryItem.remoteQuery);
if (resultIndex) {
queryItem.completed = true;
queryItem.status = QueryStatus.Completed;
queryItem.failureReason = undefined;
const queryResult = this.mapQueryResult(executionEndTime, resultIndex, queryItem.queryId);
await this.storeJsonFile(queryItem, 'query-result.json', queryResult);
// Kick off auto-download of results in the background.
void commands.executeCommand('codeQL.autoDownloadRemoteQueryResults', queryResult);
// Ask if the user wants to open the results in the background.
void this.askToOpenResults(queryItem.remoteQuery, queryResult).then(
noop,
err => {
void showAndLogErrorMessage(err);
}
);
} else {
const controllerRepo = `${queryItem.remoteQuery.controllerRepository.owner}/${queryItem.remoteQuery.controllerRepository.name}`;
const workflowRunUrl = `https://github.com/${controllerRepo}/actions/runs/${queryItem.remoteQuery.actionsWorkflowRunId}`;
void showAndLogErrorMessage(
`There was an issue retrieving the result for the query [${queryItem.remoteQuery.queryName}](${workflowRunUrl}).`
);
queryItem.status = QueryStatus.Failed;
}
}
}

View File

@@ -1,6 +1,7 @@
import { createRemoteFileRef } from '../pure/location-link-utils';
import { parseHighlightedLine, shouldHighlightLine } from '../pure/sarif-utils';
import { RemoteQuery } from './remote-query';
import { AnalysisAlert, AnalysisResults, FileLink } from './shared/analysis-result';
import { AnalysisAlert, AnalysisResults, CodeSnippet, FileLink, getAnalysisResultCount, HighlightedRegion } from './shared/analysis-result';
// Each array item is a line of the markdown file.
export type MarkdownFile = string[];
@@ -13,14 +14,13 @@ export function generateMarkdown(query: RemoteQuery, analysesResults: AnalysisRe
// Generate summary file with links to individual files
const summaryLines: MarkdownFile = generateMarkdownSummary(query);
for (const analysisResult of analysesResults) {
if (analysisResult.interpretedResults.length === 0) {
// TODO: We'll add support for non-interpreted results later.
const resultsCount = getAnalysisResultCount(analysisResult);
if (resultsCount === 0) {
continue;
}
// Append nwo and results count to the summary table
const nwo = analysisResult.nwo;
const resultsCount = analysisResult.interpretedResults.length;
const link = createGistRelativeLink(nwo);
summaryLines.push(`| ${nwo} | [${resultsCount} result(s)](${link}) |`);
@@ -33,6 +33,9 @@ export function generateMarkdown(query: RemoteQuery, analysesResults: AnalysisRe
const individualResult = generateMarkdownForInterpretedResult(interpretedResult, query.language);
lines.push(...individualResult);
}
if (analysisResult.rawResults) {
// TODO: Generate markdown table for raw results
}
files.push(lines);
}
return [summaryLines, ...files];
@@ -81,36 +84,75 @@ function generateMarkdownForInterpretedResult(interpretedResult: AnalysisAlert,
interpretedResult.highlightedRegion?.endLine
));
lines.push('');
const codeSnippet = interpretedResult.codeSnippet?.text;
const codeSnippet = interpretedResult.codeSnippet;
const highlightedRegion = interpretedResult.highlightedRegion;
if (codeSnippet) {
lines.push(
...generateMarkdownForCodeSnippet(codeSnippet, language),
...generateMarkdownForCodeSnippet(codeSnippet, language, highlightedRegion),
);
}
const alertMessage = buildMarkdownAlertMessage(interpretedResult);
lines.push(alertMessage);
const alertMessage = generateMarkdownForAlertMessage(interpretedResult);
lines.push(alertMessage, '');
// If available, show paths
const hasPathResults = interpretedResult.codeFlows.length > 0;
if (hasPathResults) {
const pathLines = generateMarkdownForPathResults(interpretedResult, language);
lines.push(...pathLines);
}
// Padding between results
lines.push(
'',
'----------------------------------------',
'',
);
return lines;
}
function generateMarkdownForCodeSnippet(codeSnippet: string, language: string): MarkdownFile {
function generateMarkdownForCodeSnippet(
codeSnippet: CodeSnippet,
language: string,
highlightedRegion?: HighlightedRegion
): MarkdownFile {
const lines: MarkdownFile = [];
const snippetStartLine = codeSnippet.startLine || 0;
const codeLines = codeSnippet.text
.split('\n')
.map((line, index) =>
highlightCodeLines(line, index + snippetStartLine, highlightedRegion)
);
// Make sure there are no extra newlines before or after the <code> block:
const codeLinesWrapped = [...codeLines];
codeLinesWrapped[0] = `<pre><code class="${language}">${codeLinesWrapped[0]}`;
codeLinesWrapped[codeLinesWrapped.length - 1] = `${codeLinesWrapped[codeLinesWrapped.length - 1]}</code></pre>`;
lines.push(
`\`\`\`${language}`,
...codeSnippet.split('\n'),
'```',
...codeLinesWrapped,
'',
);
lines.push('');
return lines;
}
function buildMarkdownAlertMessage(interpretedResult: AnalysisAlert): string {
function highlightCodeLines(
line: string,
lineNumber: number,
highlightedRegion?: HighlightedRegion
): string {
if (!highlightedRegion || !shouldHighlightLine(lineNumber, highlightedRegion)) {
return line;
}
const partiallyHighlightedLine = parseHighlightedLine(
line,
lineNumber,
highlightedRegion
);
return `${partiallyHighlightedLine.plainSection1}<strong>${partiallyHighlightedLine.highlightedSection}</strong>${partiallyHighlightedLine.plainSection2}`;
}
function generateMarkdownForAlertMessage(
interpretedResult: AnalysisAlert
): string {
let alertMessage = '';
for (const token of interpretedResult.message.tokens) {
if (token.t === 'text') {
@@ -120,7 +162,7 @@ function buildMarkdownAlertMessage(interpretedResult: AnalysisAlert): string {
token.location.fileLink,
token.location.highlightedRegion?.startLine,
token.location.highlightedRegion?.endLine,
token.text,
token.text
);
}
}
@@ -128,6 +170,34 @@ function buildMarkdownAlertMessage(interpretedResult: AnalysisAlert): string {
return `*${alertMessage}*`;
}
function generateMarkdownForPathResults(
interpretedResult: AnalysisAlert,
language: string
): MarkdownFile {
const pathLines: MarkdownFile = [];
for (const codeFlow of interpretedResult.codeFlows) {
const stepCount = codeFlow.threadFlows.length;
pathLines.push(`#### Path with ${stepCount} steps`);
for (let i = 0; i < stepCount; i++) {
const threadFlow = codeFlow.threadFlows[i];
const link = createMarkdownRemoteFileRef(
threadFlow.fileLink,
threadFlow.highlightedRegion?.startLine,
threadFlow.highlightedRegion?.endLine
);
const codeSnippet = generateMarkdownForCodeSnippet(
threadFlow.codeSnippet,
language,
threadFlow.highlightedRegion
);
// Indent the snippet to fit with the numbered list.
const codeSnippetIndented = codeSnippet.map((line) => ` ${line}`);
pathLines.push(`${i + 1}. ${link}`, ...codeSnippetIndented);
}
}
return buildExpandableMarkdownSection('Show paths', pathLines);
}
/**
* Creates a markdown link to a remote file.
* If the "link text" is not provided, we use the file path.

View File

@@ -1,8 +1,8 @@
import { QuickPickItem, window } from 'vscode';
import { showAndLogErrorMessage } from '../helpers';
import { logger } from '../logging';
import { getRemoteRepositoryLists } from '../config';
import { REPO_REGEX } from '../pure/helpers-pure';
import { UserCancellationException } from '../commandRunner';
export interface RepositorySelection {
repositories?: string[];
@@ -44,14 +44,14 @@ export async function getRepositorySelection(): Promise<RepositorySelection> {
} else if (quickpick?.useCustomRepository) {
const customRepo = await getCustomRepo();
if (!customRepo || !REPO_REGEX.test(customRepo)) {
void showAndLogErrorMessage('Invalid repository format. Please enter a valid repository in the format <owner>/<repo> (e.g. github/codeql)');
return {};
throw new UserCancellationException('Invalid repository format. Please enter a valid repository in the format <owner>/<repo> (e.g. github/codeql)');
}
void logger.log(`Entered repository: ${customRepo}`);
return { repositories: [customRepo] };
} else {
void showAndLogErrorMessage('No repositories selected.');
return {};
// We don't need to display a warning pop-up in this case, since the user just escaped out of the operation.
// We set 'true' to make this a silent exception.
throw new UserCancellationException('No repositories selected', true);
}
}

View File

@@ -2,6 +2,7 @@ import { CancellationToken, Uri, window } from 'vscode';
import * as path from 'path';
import * as yaml from 'js-yaml';
import * as fs from 'fs-extra';
import * as os from 'os';
import * as tmp from 'tmp-promise';
import {
askForLanguage,
@@ -33,7 +34,12 @@ export interface QlPack {
}
interface QueriesResponse {
workflow_run_id: number
workflow_run_id: number,
errors?: {
invalid_repositories?: string[],
repositories_without_database?: string[],
},
repositories_queried?: string[],
}
/**
@@ -324,14 +330,43 @@ async function runRemoteQueriesApiRequest(
data
}
);
const workflowRunId = response.data.workflow_run_id;
void showAndLogInformationMessage(`Successfully scheduled runs. [Click here to see the progress](https://github.com/${owner}/${repo}/actions/runs/${workflowRunId}).`);
return workflowRunId;
const { popupMessage, logMessage } = parseResponse(owner, repo, response.data);
void showAndLogInformationMessage(popupMessage, { fullMessage: logMessage });
return response.data.workflow_run_id;
} catch (error) {
void showAndLogErrorMessage(getErrorMessage(error));
}
}
const eol = os.EOL;
const eol2 = os.EOL + os.EOL;
// exported for testng only
export function parseResponse(owner: string, repo: string, response: QueriesResponse) {
const popupMessage = `Successfully scheduled runs. [Click here to see the progress](https://github.com/${owner}/${repo}/actions/runs/${response.workflow_run_id}).`
+ (response.errors ? `${eol2}Some repositories could not be scheduled. See extension log for details.` : '');
let logMessage = `Successfully scheduled runs. See https://github.com/${owner}/${repo}/actions/runs/${response.workflow_run_id}.`;
if (response.repositories_queried) {
logMessage += `${eol2}Repositories queried:${eol}${response.repositories_queried.join(', ')}`;
}
if (response.errors) {
logMessage += `${eol2}Some repositories could not be scheduled.`;
if (response.errors.invalid_repositories?.length) {
logMessage += `${eol2}Invalid repositories:${eol}${response.errors.invalid_repositories.join(', ')}`;
}
if (response.errors.repositories_without_database?.length) {
logMessage += `${eol2}Repositories without databases:${eol}${response.errors.repositories_without_database.join(', ')}`;
logMessage += `${eol}These repositories have been added to the database storage service and we will attempt to create a database for them next time the store is updated.`;
}
}
return {
popupMessage,
logMessage
};
}
/**
* Updates the default suite of the query pack. This is used to ensure
* only the specified query is run.

View File

@@ -78,3 +78,11 @@ export interface AnalysisMessageLocationToken {
}
export type ResultSeverity = 'Recommendation' | 'Warning' | 'Error';
/**
* Returns the number of (raw + interpreted) results for an analysis.
*/
export const getAnalysisResultCount = (analysisResults: AnalysisResults): number => {
const rawResultCount = analysisResults.rawResults?.resultSet.rows.length || 0;
return analysisResults.interpretedResults.length + rawResultCount;
};

View File

@@ -50,10 +50,10 @@ const CodePath = ({
}) => {
return <>
{codeFlow.threadFlows.map((threadFlow, index) =>
<div key={`thread-flow-${index}`}>
<div key={`thread-flow-${index}`} style={{ maxWidth: '55em' }}>
{index !== 0 && <VerticalSpace size={3} />}
<Box display="flex" justifyContent="center" alignItems="center" width="42.5em">
<Box display="flex" justifyContent="center" alignItems="center">
<Box flexGrow={1} p={0} border="none">
<SectionTitle>Step {index + 1}</SectionTitle>
</Box>

View File

@@ -13,7 +13,7 @@ import HorizontalSpace from './HorizontalSpace';
import Badge from './Badge';
import ViewTitle from './ViewTitle';
import DownloadButton from './DownloadButton';
import { AnalysisResults } from '../shared/analysis-result';
import { AnalysisResults, getAnalysisResultCount } from '../shared/analysis-result';
import DownloadSpinner from './DownloadSpinner';
import CollapsibleItem from './CollapsibleItem';
import { AlertIcon, CodeSquareIcon, FileCodeIcon, RepoIcon, TerminalIcon } from '@primer/octicons-react';
@@ -67,11 +67,6 @@ const openQueryTextVirtualFile = (queryResult: RemoteQueryResult) => {
});
};
const getAnalysisResultCount = (analysisResults: AnalysisResults): number => {
const rawResultCount = analysisResults.rawResults?.resultSet.rows.length || 0;
return analysisResults.interpretedResults.length + rawResultCount;
};
const sumAnalysesResults = (analysesResults: AnalysisResults[]) =>
analysesResults.reduce((acc, curr) => acc + getAnalysisResultCount(curr), 0);

View File

@@ -44,7 +44,7 @@ const _10MB = _1MB * 10;
// CLI version to test. Hard code the latest as default. And be sure
// to update the env if it is not otherwise set.
const CLI_VERSION = process.env.CLI_VERSION || 'v2.8.5';
const CLI_VERSION = process.env.CLI_VERSION || 'v2.9.0';
process.env.CLI_VERSION = CLI_VERSION;
// Base dir where CLIs will be downloaded into

View File

@@ -91,15 +91,17 @@ describe('databaseFetcher', function() {
mockRequest.resolves(mockApiResponse);
quickPickSpy.resolves('javascript');
const githubRepo = 'github/codeql';
const dbUrl = await mod.convertGithubNwoToDatabaseUrl(
const { databaseUrl, name, owner } = await mod.convertGithubNwoToDatabaseUrl(
githubRepo,
credentials,
progressSpy
);
expect(dbUrl).to.equal(
expect(databaseUrl).to.equal(
'https://api.github.com/repos/github/codeql/code-scanning/codeql/databases/javascript'
);
expect(name).to.equal('codeql');
expect(owner).to.equal('github');
expect(quickPickSpy.firstCall.args[0]).to.deep.equal([
'csharp',
'javascript',

View File

@@ -3,6 +3,7 @@ import * as sinon from 'sinon';
import { expect } from 'chai';
import { window } from 'vscode';
import * as pq from 'proxyquire';
import { UserCancellationException } from '../../../commandRunner';
const proxyquire = pq.noPreserveCache();
@@ -13,21 +14,16 @@ describe('repository-selection', function() {
let quickPickSpy: sinon.SinonStub;
let showInputBoxSpy: sinon.SinonStub;
let getRemoteRepositoryListsSpy: sinon.SinonStub;
let showAndLogErrorMessageSpy: sinon.SinonStub;
let mod: any;
beforeEach(() => {
sandbox = sinon.createSandbox();
quickPickSpy = sandbox.stub(window, 'showQuickPick');
showInputBoxSpy = sandbox.stub(window, 'showInputBox');
getRemoteRepositoryListsSpy = sandbox.stub();
showAndLogErrorMessageSpy = sandbox.stub();
mod = proxyquire('../../../remote-queries/repository-selection', {
'../config': {
getRemoteRepositoryLists: getRemoteRepositoryListsSpy
},
'../helpers': {
showAndLogErrorMessage: showAndLogErrorMessageSpy
},
});
});
@@ -120,11 +116,8 @@ describe('repository-selection', function() {
getRemoteRepositoryListsSpy.returns({}); // no pre-defined repo lists
showInputBoxSpy.resolves(repo);
// make the function call
await mod.getRepositorySelection();
// check that we get the right error message
expect(showAndLogErrorMessageSpy.firstCall.args[0]).to.contain('Invalid repository format');
// function call should throw a UserCancellationException
await expect(mod.getRepositorySelection()).to.be.rejectedWith(UserCancellationException, 'Invalid repository format');
});
});

View File

@@ -0,0 +1,120 @@
import { expect } from 'chai';
import * as os from 'os';
import { parseResponse } from '../../../remote-queries/run-remote-query';
describe('run-remote-query', () => {
describe('parseResponse', () => {
it('should parse a successful response', () => {
const result = parseResponse('org', 'name', {
workflow_run_id: 123,
repositories_queried: ['a/b', 'c/d'],
});
expect(result.popupMessage).to.equal('Successfully scheduled runs. [Click here to see the progress](https://github.com/org/name/actions/runs/123).');
expect(result.logMessage).to.equal(
['Successfully scheduled runs. See https://github.com/org/name/actions/runs/123.',
'',
'Repositories queried:',
'a/b, c/d'].join(os.EOL),
);
});
it('should parse a response with no repositories queried', () => {
const result = parseResponse('org', 'name', {
workflow_run_id: 123,
});
expect(result.popupMessage).to.equal('Successfully scheduled runs. [Click here to see the progress](https://github.com/org/name/actions/runs/123).');
expect(result.logMessage).to.equal(
'Successfully scheduled runs. See https://github.com/org/name/actions/runs/123.'
);
});
it('should parse a response with invalid repos', () => {
const result = parseResponse('org', 'name', {
workflow_run_id: 123,
repositories_queried: ['a/b', 'c/d'],
errors: {
invalid_repositories: ['e/f', 'g/h'],
}
});
expect(result.popupMessage).to.equal(
['Successfully scheduled runs. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
'',
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
);
expect(result.logMessage).to.equal(
['Successfully scheduled runs. See https://github.com/org/name/actions/runs/123.',
'',
'Repositories queried:',
'a/b, c/d',
'',
'Some repositories could not be scheduled.',
'',
'Invalid repositories:',
'e/f, g/h'].join(os.EOL)
);
});
it('should parse a response with repos w/o databases', () => {
const result = parseResponse('org', 'name', {
workflow_run_id: 123,
repositories_queried: ['a/b', 'c/d'],
errors: {
repositories_without_database: ['e/f', 'g/h'],
}
});
expect(result.popupMessage).to.equal(
['Successfully scheduled runs. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
'',
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
);
expect(result.logMessage).to.equal(
['Successfully scheduled runs. See https://github.com/org/name/actions/runs/123.',
'',
'Repositories queried:',
'a/b, c/d',
'',
'Some repositories could not be scheduled.',
'',
'Repositories without databases:',
'e/f, g/h',
'These repositories have been added to the database storage service and we will attempt to create a database for them next time the store is updated.'].join(os.EOL)
);
});
it('should parse a response with invalid repos and repos w/o databases', () => {
const result = parseResponse('org', 'name', {
workflow_run_id: 123,
repositories_queried: ['a/b', 'c/d'],
errors: {
invalid_repositories: ['e/f', 'g/h'],
repositories_without_database: ['i/j', 'k/l'],
}
});
expect(result.popupMessage).to.equal(
['Successfully scheduled runs. [Click here to see the progress](https://github.com/org/name/actions/runs/123).',
'',
'Some repositories could not be scheduled. See extension log for details.'].join(os.EOL)
);
expect(result.logMessage).to.equal(
['Successfully scheduled runs. See https://github.com/org/name/actions/runs/123.',
'',
'Repositories queried:',
'a/b, c/d',
'',
'Some repositories could not be scheduled.',
'',
'Invalid repositories:',
'e/f, g/h',
'',
'Repositories without databases:',
'i/j, k/l',
'These repositories have been added to the database storage service and we will attempt to create a database for them next time the store is updated.'].join(os.EOL)
);
});
});
});

View File

@@ -618,6 +618,44 @@
}
}
]
},
{
"threadFlows": [
{
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec",
"filePath": "npm-packages/meteor-installer/config.js"
},
"codeSnippet": {
"startLine": 37,
"endLine": 41,
"text": "\nconst meteorLocalFolder = '.meteor';\nconst meteorPath = path.resolve(rootPath, meteorLocalFolder);\n\nmodule.exports = {\n"
},
"highlightedRegion": {
"startLine": 39,
"startColumn": 20,
"endLine": 39,
"endColumn": 61
}
},
{
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec",
"filePath": "npm-packages/meteor-installer/install.js"
},
"codeSnippet": {
"startLine": 257,
"endLine": 261,
"text": " if (isWindows()) {\n //set for the current session and beyond\n child_process.execSync(`setx path \"${meteorPath}/;%path%`);\n return;\n }\n"
},
"highlightedRegion": {
"startLine": 259,
"startColumn": 28,
"endLine": 259,
"endColumn": 62
}
}
]
}
]
}

View File

@@ -0,0 +1,191 @@
### github/codeql
[javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L5-L5)
<pre><code class="javascript">function cleanupTemp() {
let cmd = "rm -rf " + path.join(__dirname, "temp");
cp.execSync(<strong>cmd</strong>); // BAD
}
</code></pre>
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4).*
<details>
<summary>Show paths</summary>
#### Path with 5 steps
1. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = "rm -rf " + path.join(<strong>__dirname</strong>, "temp");
cp.execSync(cmd); // BAD
}
</code></pre>
2. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = "rm -rf " + <strong>path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
3. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let cmd = <strong>"rm -rf " + path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
4. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4)
<pre><code class="javascript"> path = require("path");
function cleanupTemp() {
let <strong>cmd = "rm -rf " + path.join(__dirname, "temp")</strong>;
cp.execSync(cmd); // BAD
}
</code></pre>
5. [javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L5-L5)
<pre><code class="javascript">function cleanupTemp() {
let cmd = "rm -rf " + path.join(__dirname, "temp");
cp.execSync(<strong>cmd</strong>); // BAD
}
</code></pre>
</details>
----------------------------------------
[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6).*
<details>
<summary>Show paths</summary>
#### Path with 3 steps
1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
<pre><code class="javascript">(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
</details>
----------------------------------------
[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8).*
<details>
<summary>Show paths</summary>
#### Path with 3 steps
1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
<pre><code class="javascript"> cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
</code></pre>
</details>
----------------------------------------
[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9).*
<details>
<summary>Show paths</summary>
#### Path with 3 steps
1. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(<strong>__dirname</strong>, "temp")); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
2. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + <strong>path.join(__dirname, "temp")</strong>); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
3. [javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
<pre><code class="javascript">
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync(<strong>'rm -rf ' + path.join(__dirname, "temp")</strong>); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
</code></pre>
</details>
----------------------------------------

View File

@@ -0,0 +1,106 @@
### meteor/meteor
[npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(<strong>`setx path "${meteorPath}/;%path%`</strong>);
return;
}
</code></pre>
*This shell command depends on an uncontrolled [absolute path](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39).*
<details>
<summary>Show paths</summary>
#### Path with 7 steps
1. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39)
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const meteorPath = <strong>path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
2. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39)
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const <strong>meteorPath = path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
3. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L44-L44)
<pre><code class="javascript"> METEOR_LATEST_VERSION,
extractPath: rootPath,
<strong>meteorPath</strong>,
release: process.env.INSTALL_METEOR_VERSION || METEOR_LATEST_VERSION,
rootPath,
</code></pre>
4. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L12-L12)
<pre><code class="javascript">const os = require('os');
const {
<strong>meteorPath</strong>,
release,
startedPath,
</code></pre>
5. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L11-L23)
<pre><code class="javascript">const tmp = require('tmp');
const os = require('os');
const <strong>{</strong>
<strong> meteorPath,</strong>
<strong> release,</strong>
<strong> startedPath,</strong>
<strong> extractPath,</strong>
<strong> isWindows,</strong>
<strong> rootPath,</strong>
<strong> sudoUser,</strong>
<strong> isSudo,</strong>
<strong> isMac,</strong>
<strong> METEOR_LATEST_VERSION,</strong>
<strong> shouldSetupExecPath,</strong>
<strong>} = require('./config.js')</strong>;
const { uninstall } = require('./uninstall');
const {
</code></pre>
6. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(`setx path "${<strong>meteorPath</strong>}/;%path%`);
return;
}
</code></pre>
7. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(<strong>`setx path "${meteorPath}/;%path%`</strong>);
return;
}
</code></pre>
#### Path with 2 steps
1. [npm-packages/meteor-installer/config.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39)
<pre><code class="javascript">
const meteorLocalFolder = '.meteor';
const meteorPath = <strong>path.resolve(rootPath, meteorLocalFolder)</strong>;
module.exports = {
</code></pre>
2. [npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
<pre><code class="javascript"> if (isWindows()) {
//set for the current session and beyond
child_process.execSync(<strong>`setx path "${meteorPath}/;%path%`</strong>);
return;
}
</code></pre>
</details>
----------------------------------------

View File

@@ -0,0 +1,154 @@
[
{
"nwo": "github/codeql",
"status": "Completed",
"interpretedResults": [
{
"message": {
"tokens": [
{
"t": "text",
"text": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'."
}
]
},
"shortDescription": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'.",
"fileLink": {
"fileLinkPrefix": "https://github.com/github/codeql/blob/d094bbc06d063d0da8d0303676943c345e61de53",
"filePath": "javascript/extractor/tests/regexp/input/multipart.js"
},
"severity": "Warning",
"codeSnippet": {
"startLine": 15,
"endLine": 22,
"text": "\nvar bad95 = new RegExp(\n \"(a\" + \n \"|\" + \n \"aa)*\" + \n \"b$\"\n);\n\n"
},
"highlightedRegion": {
"startLine": 17,
"startColumn": 6,
"endLine": 20,
"endColumn": 6
},
"codeFlows": []
}
]
},
{
"nwo": "meteor/meteor",
"status": "Completed",
"interpretedResults": [
{
"message": {
"tokens": [
{
"t": "text",
"text": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '----'."
}
]
},
"shortDescription": "This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '----'.",
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12",
"filePath": "packages/deprecated/markdown/showdown.js"
},
"severity": "Warning",
"codeSnippet": {
"startLine": 413,
"endLine": 417,
"text": "\t\t/g,hashElement);\n\t*/\n\ttext = text.replace(/(\\n\\n[ ]{0,3}<!(--[^\\r]*?--\\s*)+>[ \\t]*(?=\\n{2,}))/g,hashElement);\n\n\t// PHP and ASP-style processor instructions (<?...?> and <%...%>)\n"
},
"highlightedRegion": {
"startLine": 415,
"startColumn": 41,
"endLine": 415,
"endColumn": 48
},
"codeFlows": []
},
{
"message": {
"tokens": [
{
"t": "text",
"text": "This part of the regular expression may cause exponential backtracking on strings starting with '<!--' and containing many repetitions of '----'."
}
]
},
"shortDescription": "This part of the regular expression may cause exponential backtracking on strings starting with '<!--' and containing many repetitions of '----'.",
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12",
"filePath": "packages/deprecated/markdown/showdown.js"
},
"severity": "Warning",
"codeSnippet": {
"startLine": 521,
"endLine": 525,
"text": "\t// Build a regex to find HTML tags and comments. See Friedl's\n\t// \"Mastering Regular Expressions\", 2nd Ed., pp. 200-201.\n\tvar regex = /(<[a-z\\/!$](\"[^\"]*\"|'[^']*'|[^'\">])*>|<!(--.*?--\\s*)+>)/gi;\n\n\ttext = text.replace(regex, function(wholeMatch) {\n"
},
"highlightedRegion": {
"startLine": 523,
"startColumn": 58,
"endLine": 523,
"endColumn": 61
},
"codeFlows": []
},
{
"message": {
"tokens": [
{
"t": "text",
"text": "This part of the regular expression may cause exponential backtracking on strings starting with ''' and containing many repetitions of '\\&'."
}
]
},
"shortDescription": "This part of the regular expression may cause exponential backtracking on strings starting with ''' and containing many repetitions of '\\&'.",
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12",
"filePath": "tools/tests/apps/modules/imports/links/acorn/src/parseutil.js"
},
"severity": "Warning",
"codeSnippet": {
"startLine": 7,
"endLine": 11,
"text": "// ## Parser utilities\n\nconst literal = /^(?:'((?:\\\\.|[^'])*?)'|\"((?:\\\\.|[^\"])*?)\")/\npp.strictDirective = function(start) {\n for (;;) {\n"
},
"highlightedRegion": {
"startLine": 9,
"startColumn": 24,
"endLine": 9,
"endColumn": 38
},
"codeFlows": []
},
{
"message": {
"tokens": [
{
"t": "text",
"text": "This part of the regular expression may cause exponential backtracking on strings starting with '\"' and containing many repetitions of '\\!'."
}
]
},
"shortDescription": "This part of the regular expression may cause exponential backtracking on strings starting with '\"' and containing many repetitions of '\\!'.",
"fileLink": {
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12",
"filePath": "tools/tests/apps/modules/imports/links/acorn/src/parseutil.js"
},
"severity": "Warning",
"codeSnippet": {
"startLine": 9,
"endLine": 9,
"text": "const literal = /^(?:'((?:\\\\.|[^'])*?)'|\"((?:\\\\.|[^\"])*?)\")/"
},
"highlightedRegion": {
"startLine": 9,
"startColumn": 43,
"endLine": 9,
"endColumn": 57
},
"codeFlows": []
}
]
}
]

View File

@@ -0,0 +1,12 @@
{
"queryName": "Inefficient regular expression",
"queryFilePath": "c:\\git-repo\\vscode-codeql-starter\\ql\\javascript\\ql\\src\\Performance\\ReDoS.ql",
"queryText": "/**\n * @name Inefficient regular expression\n * @description A regular expression that requires exponential time to match certain inputs\n * can be a performance bottleneck, and may be vulnerable to denial-of-service\n * attacks.\n * @kind problem\n * @problem.severity error\n * @security-severity 7.5\n * @precision high\n * @id js/redos\n * @tags security\n * external/cwe/cwe-1333\n * external/cwe/cwe-730\n * external/cwe/cwe-400\n */\n\nimport javascript\nimport semmle.javascript.security.performance.ReDoSUtil\nimport semmle.javascript.security.performance.ExponentialBackTracking\n\nfrom RegExpTerm t, string pump, State s, string prefixMsg\nwhere hasReDoSResult(t, pump, s, prefixMsg)\nselect t,\n \"This part of the regular expression may cause exponential backtracking on strings \" + prefixMsg +\n \"containing many repetitions of '\" + pump + \"'.\"\n",
"language": "javascript",
"controllerRepository": {
"owner": "dsp-testing",
"name": "qc-controller"
},
"executionStartTime": 1650464389790,
"actionsWorkflowRunId": 2196289254
}

View File

@@ -0,0 +1,17 @@
### github/codeql
[javascript/extractor/tests/regexp/input/multipart.js](https://github.com/github/codeql/blob/d094bbc06d063d0da8d0303676943c345e61de53/javascript/extractor/tests/regexp/input/multipart.js#L17-L20)
<pre><code class="javascript">
var bad95 = new RegExp(
"<strong>(a" + </strong>
<strong> "|" + </strong>
<strong> "aa)*" + </strong>
<strong> "</strong>b$"
);
</code></pre>
*This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'.*
----------------------------------------

View File

@@ -0,0 +1,48 @@
### meteor/meteor
[packages/deprecated/markdown/showdown.js](https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12/packages/deprecated/markdown/showdown.js#L415-L415)
<pre><code class="javascript"> /g,hashElement);
*/
text = text.replace(/(\n\n[ ]{0,3}<!(--<strong>[^\r]*?</strong>--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
// PHP and ASP-style processor instructions (<?...?> and <%...%>)
</code></pre>
*This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '----'.*
----------------------------------------
[packages/deprecated/markdown/showdown.js](https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12/packages/deprecated/markdown/showdown.js#L523-L523)
<pre><code class="javascript"> // Build a regex to find HTML tags and comments. See Friedl's
// "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--<strong>.*?</strong>--\s*)+>)/gi;
text = text.replace(regex, function(wholeMatch) {
</code></pre>
*This part of the regular expression may cause exponential backtracking on strings starting with '<!--' and containing many repetitions of '----'.*
----------------------------------------
[tools/tests/apps/modules/imports/links/acorn/src/parseutil.js](https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12/tools/tests/apps/modules/imports/links/acorn/src/parseutil.js#L9-L9)
<pre><code class="javascript">// ## Parser utilities
const literal = /^(?:'(<strong>(?:\\.|[^'])*?</strong>)'|"((?:\\.|[^"])*?)")/
pp.strictDirective = function(start) {
for (;;) {
</code></pre>
*This part of the regular expression may cause exponential backtracking on strings starting with ''' and containing many repetitions of '\&'.*
----------------------------------------
[tools/tests/apps/modules/imports/links/acorn/src/parseutil.js](https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12/tools/tests/apps/modules/imports/links/acorn/src/parseutil.js#L9-L9)
<pre><code class="javascript">const literal = /^(?:'((?:\\.|[^'])*?)'|"(<strong>(?:\\.|[^"])*?</strong>)")/</code></pre>
*This part of the regular expression may cause exponential backtracking on strings starting with '"' and containing many repetitions of '\!'.*
----------------------------------------

View File

@@ -0,0 +1,44 @@
### Results for "Inefficient regular expression"
<details>
<summary>Query</summary>
```ql
/**
* @name Inefficient regular expression
* @description A regular expression that requires exponential time to match certain inputs
* can be a performance bottleneck, and may be vulnerable to denial-of-service
* attacks.
* @kind problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @id js/redos
* @tags security
* external/cwe/cwe-1333
* external/cwe/cwe-730
* external/cwe/cwe-400
*/
import javascript
import semmle.javascript.security.performance.ReDoSUtil
import semmle.javascript.security.performance.ExponentialBackTracking
from RegExpTerm t, string pump, State s, string prefixMsg
where hasReDoSResult(t, pump, s, prefixMsg)
select t,
"This part of the regular expression may cause exponential backtracking on strings " + prefixMsg +
"containing many repetitions of '" + pump + "'."
```
</details>
<br />
### Summary
| Repository | Results |
| --- | --- |
| github/codeql | [1 result(s)](#file-github-codeql-md) |
| meteor/meteor | [4 result(s)](#file-meteor-meteor-md) |

View File

@@ -0,0 +1,392 @@
[
{
"nwo": "github/codeql",
"status": "Completed",
"interpretedResults": [],
"rawResults": {
"schema": {
"name": "#select",
"rows": 22,
"columns": [
{
"name": "c",
"kind": "e"
},
{
"kind": "i"
}
]
},
"resultSet": {
"schema": {
"name": "#select",
"rows": 22,
"columns": [
{
"name": "c",
"kind": "e"
},
{
"kind": "i"
}
]
},
"rows": [
[
{
"label": "functio ... ght);\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/Expressions/examples/CompareIdenticalValues.js",
"startLine": 8,
"startColumn": 32,
"endLine": 13,
"endColumn": 1
}
},
6
],
[
{
"label": "functio ... i-1);\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/LanguageFeatures/examples/ArgumentsCallerCallee.js",
"startLine": 1,
"startColumn": 2,
"endLine": 5,
"endColumn": 1
}
},
5
],
[
{
"label": "functio ... i-1);\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/LanguageFeatures/examples/ArgumentsCallerCalleeGood.js",
"startLine": 1,
"startColumn": 2,
"endLine": 5,
"endColumn": 1
}
},
5
],
[
{
"label": "functio ... n -1;\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/src/Statements/examples/UselessComparisonTest.js",
"startLine": 1,
"startColumn": 1,
"endLine": 12,
"endColumn": 1
}
},
12
],
[
{
"label": "functio ... false\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/constants.js",
"startLine": 1,
"startColumn": 1,
"endLine": 8,
"endColumn": 1
}
},
8
],
[
{
"label": "functio ... \\n }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
"startLine": 1,
"startColumn": 1,
"endLine": 12,
"endColumn": 1
}
},
12
],
[
{
"label": "functio ... e\\n }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
"startLine": 14,
"startColumn": 1,
"endLine": 22,
"endColumn": 1
}
},
9
],
[
{
"label": "functio ... K\\n }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/loop.js",
"startLine": 24,
"startColumn": 1,
"endLine": 40,
"endColumn": 1
}
},
17
],
[
{
"label": "functio ... e\\n }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
"startLine": 1,
"startColumn": 1,
"endLine": 17,
"endColumn": 1
}
},
17
],
[
{
"label": "functio ... alse \\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
"startLine": 19,
"startColumn": 1,
"endLine": 28,
"endColumn": 1
}
},
10
],
[
{
"label": "functio ... true\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/plus.js",
"startLine": 30,
"startColumn": 1,
"endLine": 33,
"endColumn": 1
}
},
4
],
[
{
"label": "functio ... K\\n }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
"startLine": 1,
"startColumn": 1,
"endLine": 15,
"endColumn": 1
}
},
15
],
[
{
"label": "functio ... e\\n }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
"startLine": 17,
"startColumn": 1,
"endLine": 31,
"endColumn": 1
}
},
15
],
[
{
"label": "functio ... false\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
"startLine": 33,
"startColumn": 1,
"endLine": 41,
"endColumn": 1
}
},
9
],
[
{
"label": "functio ... e\\n }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/library-tests/RangeAnalysis/tst.js",
"startLine": 43,
"startColumn": 1,
"endLine": 52,
"endColumn": 1
}
},
10
],
[
{
"label": "functio ... ght);\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Expressions/CompareIdenticalValues/tst.js",
"startLine": 8,
"startColumn": 32,
"endLine": 13,
"endColumn": 1
}
},
6
],
[
{
"label": "functio ... i-1);\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/LanguageFeatures/ArgumentsCallerCallee/tst.js",
"startLine": 1,
"startColumn": 2,
"endLine": 5,
"endColumn": 1
}
},
5
],
[
{
"label": "functio ... }\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Security/CWE-834/LoopBoundInjectionExitBad.js",
"startLine": 17,
"startColumn": 1,
"endLine": 29,
"endColumn": 1
}
},
13
],
[
{
"label": "functio ... true\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/constant.js",
"startLine": 1,
"startColumn": 1,
"endLine": 4,
"endColumn": 1
}
},
4
],
[
{
"label": "functio ... n -1;\\n}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/example.js",
"startLine": 1,
"startColumn": 1,
"endLine": 12,
"endColumn": 1
}
},
12
],
[
{
"label": "functio ... turn; }",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/tst.js",
"startLine": 8,
"startColumn": 3,
"endLine": 8,
"endColumn": 43
}
},
1
],
[
{
"label": "functio ... i+1); }",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/javascript/ql/test/query-tests/Statements/UselessComparisonTest/tst.js",
"startLine": 9,
"startColumn": 3,
"endLine": 9,
"endColumn": 52
}
},
1
]
]
},
"fileLinkPrefix": "https://github.com/github/codeql/blob/cbdd4927cee593b715d8469240ce1d31edaaef9b",
"capped": false
}
},
{
"nwo": "meteor/meteor",
"status": "Completed",
"interpretedResults": [],
"rawResults": {
"schema": {
"name": "#select",
"rows": 2,
"columns": [
{
"name": "c",
"kind": "e"
},
{
"kind": "i"
}
]
},
"resultSet": {
"schema": {
"name": "#select",
"rows": 2,
"columns": [
{
"name": "c",
"kind": "e"
},
{
"kind": "i"
}
]
},
"rows": [
[
{
"label": "functio ... rn H|0}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/packages/logic-solver/minisat.js",
"startLine": 7,
"startColumn": 91430,
"endLine": 7,
"endColumn": 105027
}
},
1
],
[
{
"label": "functio ... ext;\\n\\t}",
"url": {
"uri": "file:/home/runner/work/bulk-builder/bulk-builder/packages/sha/sha256.js",
"startLine": 94,
"startColumn": 2,
"endLine": 124,
"endColumn": 2
}
},
31
]
]
},
"fileLinkPrefix": "https://github.com/meteor/meteor/blob/53f3c4442d3542d3d2a012a854472a0d1bef9d12",
"capped": false
}
}
]

View File

@@ -0,0 +1,12 @@
{
"queryName": "Contradictory guard nodes",
"queryFilePath": "c:\\Users\\foo\\bar\\quick-query.ql",
"queryText": "/**\n * @name Contradictory guard nodes\n * \n * @description Snippet from \"UselessComparisonTest.ql\"\n */\n\nimport javascript\n\n/**\n * Holds if there are any contradictory guard nodes in `container`.\n *\n * We use this to restrict reachability analysis to a small set of containers.\n */\npredicate hasContradictoryGuardNodes(StmtContainer container) {\n exists(ConditionGuardNode guard |\n RangeAnalysis::isContradictoryGuardNode(guard) and\n container = guard.getContainer()\n )\n}\n\nfrom StmtContainer c\nwhere hasContradictoryGuardNodes(c)\nselect c, c.getNumLines()",
"language": "javascript",
"controllerRepository": {
"owner": "dsp-testing",
"name": "qc-controller"
},
"executionStartTime": 1650979054479,
"actionsWorkflowRunId": 2226920623
}

View File

@@ -0,0 +1,41 @@
### Results for "Contradictory guard nodes"
<details>
<summary>Query</summary>
```ql
/**
* @name Contradictory guard nodes
*
* @description Snippet from "UselessComparisonTest.ql"
*/
import javascript
/**
* Holds if there are any contradictory guard nodes in `container`.
*
* We use this to restrict reachability analysis to a small set of containers.
*/
predicate hasContradictoryGuardNodes(StmtContainer container) {
exists(ConditionGuardNode guard |
RangeAnalysis::isContradictoryGuardNode(guard) and
container = guard.getContainer()
)
}
from StmtContainer c
where hasContradictoryGuardNodes(c)
select c, c.getNumLines()
```
</details>
<br />
### Summary
| Repository | Results |
| --- | --- |
| github/codeql | [22 result(s)](#file-github-codeql-md) |
| meteor/meteor | [2 result(s)](#file-meteor-meteor-md) |

View File

@@ -1,60 +0,0 @@
### github/codeql
[javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L5-L5)
```javascript
function cleanupTemp() {
let cmd = "rm -rf " + path.join(__dirname, "temp");
cp.execSync(cmd); // BAD
}
```
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/src/Security/CWE-078/examples/shell-command-injection-from-environment.js#L4-L4).*
----------------------------------------
[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6)
```javascript
(function() {
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
```
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L6-L6).*
----------------------------------------
[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8)
```javascript
cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
```
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L8-L8).*
----------------------------------------
[javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9)
```javascript
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
const safe = "\"" + path.join(__dirname, "temp") + "\"";
```
*This shell command depends on an uncontrolled [absolute path](https://github.com/github/codeql/blob/48015e5a2e6202131f2d1062cc066dc33ed69a9b/javascript/ql/test/query-tests/Security/CWE-078/tst_shell-command-injection-from-environment.js#L9-L9).*
----------------------------------------

View File

@@ -1,16 +0,0 @@
### meteor/meteor
[npm-packages/meteor-installer/install.js](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/install.js#L259-L259)
```javascript
if (isWindows()) {
//set for the current session and beyond
child_process.execSync(`setx path "${meteorPath}/;%path%`);
return;
}
```
*This shell command depends on an uncontrolled [absolute path](https://github.com/meteor/meteor/blob/73b538fe201cbfe89dd0c709689023f9b3eab1ec/npm-packages/meteor-installer/config.js#L39-L39).*
----------------------------------------

View File

@@ -1,42 +0,0 @@
import { expect } from 'chai';
import * as path from 'path';
import * as fs from 'fs-extra';
import { generateMarkdown } from '../../../../../src/remote-queries/remote-queries-markdown-generation';
describe('markdown generation', async function() {
it('should generate markdown file for each repo with results', async function() {
const problemQuery = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/problem-query.json'), 'utf8')
);
const analysesResults = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/analyses-results.json'), 'utf8')
);
const markdownFiles = generateMarkdown(problemQuery, analysesResults);
// Check that query has results for two repositories, plus a summary file
expect(markdownFiles.length).to.equal(3);
const markdownFile0 = markdownFiles[0]; // summary file
const markdownFile1 = markdownFiles[1]; // results for github/codeql repo
const markdownFile2 = markdownFiles[2]; // results for meteor/meteor repo
const expectedSummaryFile = await readTestOutputFile('data/summary.md');
const expectedTestOutput1 = await readTestOutputFile('data/results-repo1.md');
const expectedTestOutput2 = await readTestOutputFile('data/results-repo2.md');
// Check that markdown output is correct, after making line endings consistent
expect(markdownFile0.join('\n')).to.equal(expectedSummaryFile);
expect(markdownFile1.join('\n')).to.equal(expectedTestOutput1);
expect(markdownFile2.join('\n')).to.equal(expectedTestOutput2);
});
});
/**
* Reads a test output file and returns it as a string.
* Replaces line endings with '\n' for consistency across operating systems.
*/
async function readTestOutputFile(relativePath: string): Promise<string> {
const file = await fs.readFile(path.join(__dirname, relativePath), 'utf8');
return file.replace(/\r?\n/g, '\n');
}

View File

@@ -0,0 +1,102 @@
import { expect } from 'chai';
import * as path from 'path';
import * as fs from 'fs-extra';
import { generateMarkdown } from '../../../../src/remote-queries/remote-queries-markdown-generation';
describe('markdown generation', async function() {
describe('for path-problem query', async function() {
it('should generate markdown file for each repo with results', async function() {
const pathProblemQuery = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/interpreted-results/path-problem/path-problem-query.json'), 'utf8')
);
const analysesResults = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/interpreted-results/path-problem/analyses-results.json'), 'utf8')
);
const markdownFiles = generateMarkdown(pathProblemQuery, analysesResults);
// Check that query has results for two repositories, plus a summary file
expect(markdownFiles.length).to.equal(3);
const markdownFile0 = markdownFiles[0]; // summary file
const markdownFile1 = markdownFiles[1]; // results for github/codeql repo
const markdownFile2 = markdownFiles[2]; // results for meteor/meteor repo
const expectedSummaryFile = await readTestOutputFile('data/interpreted-results/path-problem/summary.md');
const expectedTestOutput1 = await readTestOutputFile('data/interpreted-results/path-problem/results-repo1.md');
const expectedTestOutput2 = await readTestOutputFile('data/interpreted-results/path-problem/results-repo2.md');
// Check that markdown output is correct, after making line endings consistent
expect(markdownFile0.join('\n')).to.equal(expectedSummaryFile);
expect(markdownFile1.join('\n')).to.equal(expectedTestOutput1);
expect(markdownFile2.join('\n')).to.equal(expectedTestOutput2);
});
});
describe('for problem query', async function() {
it('should generate markdown file for each repo with results', async function() {
const problemQuery = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/interpreted-results/problem/problem-query.json'), 'utf8')
);
const analysesResults = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/interpreted-results/problem/analyses-results.json'), 'utf8')
);
const markdownFiles = generateMarkdown(problemQuery, analysesResults);
// Check that query has results for two repositories, plus a summary file
expect(markdownFiles.length).to.equal(3);
const markdownFile0 = markdownFiles[0]; // summary file
const markdownFile1 = markdownFiles[1]; // results for github/codeql repo
const markdownFile2 = markdownFiles[2]; // results for meteor/meteor repo
const expectedSummaryFile = await readTestOutputFile('data/interpreted-results/problem/summary.md');
const expectedTestOutput1 = await readTestOutputFile('data/interpreted-results/problem/results-repo1.md');
const expectedTestOutput2 = await readTestOutputFile('data/interpreted-results/problem/results-repo2.md');
// Check that markdown output is correct, after making line endings consistent
expect(markdownFile0.join('\n')).to.equal(expectedSummaryFile);
expect(markdownFile1.join('\n')).to.equal(expectedTestOutput1);
expect(markdownFile2.join('\n')).to.equal(expectedTestOutput2);
});
});
describe('for non-alert query', async function() {
it('should generate markdown file for each repo with results', async function() {
const query = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/raw-results/query.json'), 'utf8')
);
const analysesResults = JSON.parse(
await fs.readFile(path.join(__dirname, 'data/raw-results/analyses-results.json'), 'utf8')
);
const markdownFiles = generateMarkdown(query, analysesResults);
// Check that query has results for two repositories, plus a summary file
expect(markdownFiles.length).to.equal(3);
const markdownFile0 = markdownFiles[0]; // summary file
const markdownFile1 = markdownFiles[1]; // results for github/codeql repo
const markdownFile2 = markdownFiles[2]; // results for meteor/meteor repo
const expectedSummaryFile = await readTestOutputFile('data/raw-results/summary.md');
const expectedTestOutput1 = await readTestOutputFile('data/raw-results/results-repo1.md');
const expectedTestOutput2 = await readTestOutputFile('data/raw-results/results-repo2.md');
// Check that markdown output is correct, after making line endings consistent
expect(markdownFile0.join('\n')).to.equal(expectedSummaryFile);
expect(markdownFile1.join('\n')).to.equal(expectedTestOutput1);
expect(markdownFile2.join('\n')).to.equal(expectedTestOutput2);
});
});
});
/**
* Reads a test output file and returns it as a string.
* Replaces line endings with '\n' for consistency across operating systems.
*/
async function readTestOutputFile(relativePath: string): Promise<string> {
const file = await fs.readFile(path.join(__dirname, relativePath), 'utf8');
return file.replace(/\r?\n/g, '\n');
}