diff --git a/README.md b/README.md index 9ba0306..d334fc9 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ CodeQL Agent CLI is a tool that automates the process of using CodeQL, a semanti - Automated CodeQL from detect language, create database and scan. - Scan remote target (e.g. GitHub repository) or local target (e.g. source code folder). - Support running on Docker which prepackaged and precompiled CodeQL for running code scanning (*under development*). +- Send results to Discord webhook. ## Requirements @@ -69,30 +70,32 @@ Usage: codeql-agent scan [options] scan a source code folder or remote repository (e.g. GitHub repository) Arguments: - target source code folder or remote repository. - + target source code folder or remote repository. + Examples: - codeql-agent src/sammple + codeql-agent scan src/sammple codeql-agent scan src/sammple --use-docker codeql-agent scan https://github.com/OWASP/NodeGoat Options: - -l, --language language of source code. Supported languages: go, java, cpp, csharp, cpp, javascript, ruby. Omitting this option to auto-detect the language. - -o, --output output folder. Default: -codeql-results - -c, --command command to create database for compiled languages, omit if the only languages requested are Python and JavaScript. This specifies the build commands - needed to invoke the compiler. If you don't set this variable, CodeQL will attempt to detect the build system automatically, using a built-in autobuilder - -t, --threads number of threads to use. Pass 0 to use one threads per core on the machine. Default: 1 (default: 1) - --query CodeQL query to run. Default: -security-extended.qls - --format output format. Default: sarif-latest (default: "sarif-latest") - --overwrite overwrite existing database. - --no-download do not download missing queries before analyzing. - --remove-remote-repository remove the remote repository after cloning. - --db-output database folder path. - --remove-database remove the CodeQL database after scanning. - --create-db-only only create CodeQL database, do not scan. - --use-docker use docker to isolated run CodeQL. - -v, --verbose verbose output - -h, --help display help for command + -l, --language language of source code. Supported languages: go, java, cpp, csharp, cpp, javascript, ruby. Omitting this option to auto-detect the language. + -o, --output output folder. Default: -codeql-results + -c, --command command to create database for compiled languages, omit if the only languages requested are Python and JavaScript. This specifies the build commands needed to invoke the compiler. If + you don't set this variable, CodeQL will attempt to detect the build system automatically, using a built-in autobuilder + -t, --threads number of threads to use. Pass 0 to use one threads per core on the machine. Default: 1 (default: 1) + --query CodeQL query to run. Default: -security-extended.qls + --format output format. Default: sarif-latest (default: "sarif-latest") + --overwrite overwrite existing database. + --download download missing queries before analyzing. + --remove-remote-repository remove the remote repository after cloning. + --db-output database folder path. + --remove-database remove the CodeQL database after scanning. + --create-db-only only create CodeQL database, do not scan. + --enable-file-logging enable file logging. + --discord-webhook discord web hook to send the result to. + --use-docker use docker to isolated run CodeQL. + -v, --verbose verbose output + -h, --help display help for command ``` ## Using CodeQL Agent on VSCode diff --git a/cli.js b/cli.js index 4e9dc6b..1b1f772 100755 --- a/cli.js +++ b/cli.js @@ -35,11 +35,13 @@ program.command('scan') .option('--query ', 'CodeQL query to run. Default: -security-extended.qls') .option('--format ', 'output format. Default: sarif-latest', 'sarif-latest') .option('--overwrite', 'overwrite existing database.') - .option('--no-download', 'do not download missing queries before analyzing.') + .option('--download', 'download missing queries before analyzing.') .option('--remove-remote-repository', 'remove the remote repository after cloning.') .option('--db-output ', 'database folder path. ') .option('--remove-database', 'remove the CodeQL database after scanning.') .option('--create-db-only', 'only create CodeQL database, do not scan.') + .option('--enable-file-logging', 'enable file logging.') + .option('--discord-webhook ', 'discord web hook to send the result to.') .option('--use-docker', 'use docker to isolated run CodeQL.') .option('-v, --verbose', 'verbose output') .action(scan); diff --git a/index.js b/index.js index 381d1a0..edb0dae 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -const logger = require('./logger'); +const { defaultLogger, bugLogger } = require('./logger'); const utils = require('./utils'); const fs = require('fs'); const config = require('./config'); @@ -9,34 +9,36 @@ module.exports = { } async function scanAction(sourceTarget, options) { - if (options.verbose) { logger.setLevel('verbose') } + if (options.verbose) { defaultLogger.setLevel('verbose') } if (options.useDocker) { - await utils.isCommandExist('docker', logger); - logger.error('Docker is not supported yet.'); + await utils.isCommandExist('docker', defaultLogger); + defaultLogger.error('Docker is not supported yet.'); return; } - await utils.isCommandExist('codeql', logger); + if (options.enableFileLogging) { defaultLogger.enableFileTransport() } + if (options.discordWebhook) { bugLogger.enableDiscordTransport(options.discordWebhook) } + await utils.isCommandExist('codeql', defaultLogger); // Create Database var createDbOptions = { ...options }; createDbOptions.output = options.dbOutput; var isRemoteRepository = utils.isRemoteRepository(sourceTarget); if (isRemoteRepository) { - logger.info(`Cloning remote repository ${sourceTarget}`) - sourceFolderPath = await utils.cloneRemoteRepository(sourceTarget, logger); + defaultLogger.info(`Cloning remote repository ${sourceTarget}`) + sourceFolderPath = await utils.cloneRemoteRepository(sourceTarget, defaultLogger); } else sourceFolderPath = sourceTarget; sourceFolderPath = fs.realpathSync(sourceFolderPath); - logger.info(`Creating CodeQL database for ${sourceFolderPath}...`) - var { args: createDbArgs, databasePath } = await utils.setupCreateDatabaseCommandArgs(sourceFolderPath, createDbOptions, logger); - logger.verbose(`Options:`); + defaultLogger.info(`Creating CodeQL database for ${sourceFolderPath}...`) + var { args: createDbArgs, databasePath } = await utils.setupCreateDatabaseCommandArgs(sourceFolderPath, createDbOptions, defaultLogger); + defaultLogger.verbose(`Options:`); for (const key in options) { const element = options[key]; - logger.verbose(`[+] ${key}: ${element}`); + defaultLogger.verbose(`[+] ${key}: ${element}`); } - createDbExitCode = await utils.executeCommand('codeql', createDbArgs, 'Create CodeQL database', logger); - logger.info(`CodeQL database created at ${databasePath}.`) + createDbExitCode = await utils.executeCommand('codeql', createDbArgs, 'Create CodeQL database', defaultLogger); + defaultLogger.info(`CodeQL database created at ${databasePath}.`) if (isRemoteRepository && options.removeRemoteRepository) { - logger.info(`Removing remote repository ${sourceFolderPath}`) - await utils.removeFolder(sourceFolderPath, logger); + defaultLogger.info(`Removing remote repository ${sourceFolderPath}`) + await utils.removeFolder(sourceFolderPath, defaultLogger); } if (options.createDbOnly) { return databasePath; @@ -46,26 +48,39 @@ async function scanAction(sourceTarget, options) { if (!fs.existsSync(outputFolderPath)) { fs.mkdirSync(outputFolderPath); } - const languages = await utils.getDatabaseLanguages(databasePath, logger); + const languages = await utils.getDatabaseLanguages(databasePath, defaultLogger); + if (!languages) { + defaultLogger.error('Can not detect languages. Please specify the language using --language option'); + return; + } for (const language of languages) { options.language = language; languageDatabasePath = path.resolve(`${databasePath}${path.sep}${language}`); options.output = path.resolve(outputFolderPath, `${language}-codeql-result.sarif`) - logger.info(`Scanning ${language} code in ${databasePath}...`) - var { args: scanArgs } = await utils.setupScanCommandArgs(languageDatabasePath, options, logger); - await utils.executeCommand('codeql', scanArgs, 'Scan CodeQL database', logger); + defaultLogger.info(`Scanning ${language} code in ${databasePath}...`) + var { args: scanArgs } = await utils.setupScanCommandArgs(languageDatabasePath, options, defaultLogger); + await utils.executeCommand('codeql', scanArgs, 'Scan CodeQL database', defaultLogger); } - logger.info(`CodeQL scan results saved at ${outputFolderPath}.`) + defaultLogger.info(`CodeQL scan results saved at ${outputFolderPath}.`) const resultFiles = fs.readdirSync(outputFolderPath); + var alerts = []; for (const resultFile of resultFiles) { - const alerts = await utils.parseSarif(path.resolve(outputFolderPath, resultFile), logger); - for (const alert of alerts) { - logger.error(`[${alert.id}][${alert.level}][precision:${alert.precision}][severity:${alert.severity}] ${alert.title}: ${alert.location}`); - } + alerts = alerts.concat(await utils.parseSarif(path.resolve(outputFolderPath, resultFile), defaultLogger)); + } + for (const alert of alerts) { + defaultLogger.log({ + level: utils.castBugLevelToLogLevel(alert.level), + message: `[${alert.id}][${alert.level}][precision:${alert.precision}][severity:${alert.severity}][${alert.location}] ${alert.title}` + }); + bugLogger.log({ + level: utils.castBugLevelToLogLevel(alert.level), + message: path.basename(sourceFolderPath), + meta: alert + }); } if (options.removeDatabase) { - logger.info(`Removing database folder ${databasePath}`) - await utils.removeFolder(databasePath, logger); + defaultLogger.info(`Removing database folder ${databasePath}`) + await utils.removeFolder(databasePath, defaultLogger); } - return outputFolderPath; + return alerts; } diff --git a/logger.js b/logger.js index b38d734..b2daa2a 100644 --- a/logger.js +++ b/logger.js @@ -1,29 +1,84 @@ const winston = require('winston'); +const { createLogger, format, transports } = winston; +const Logger = winston.Logger; +const { combine, timestamp, printf, colorize } = format; +const DailyRotateFile = require('winston-daily-rotate-file'); +const { DiscordTransport } = require('winston-transport-discord'); +const { HiddenLogger } = require('winston/lib/winston/logger'); -// Default logger -const logger = winston.createLogger({ - level: 'info', - format: winston.format.json(), - transports: [ - new winston.transports.Console({ - level: 'info', - format: winston.format.combine( - winston.format.colorize(), - winston.format.simple() - ) - }) - ], +/* + * Custom logger +*/ +const defaultLogFormat = printf(({ level, message, timestamp }) => { + return `[${level}][${timestamp}]: ${message}`; }); -logger.setLevel = function (logLevel) { - this.clear() - this.add(new winston.transports.Console({ - level: logLevel, +const discordLogFormat = printf(({ level, message }) => { + return `[${level}] ${message}`; +}); + +const setLevel = function (logLevel) { + this.level = logLevel; +} +const enableConsoleTransport = function () { + this.add(new transports.Console({ format: winston.format.combine( - winston.format.colorize(), - winston.format.simple() + colorize({ all: true }), + timestamp(), + defaultLogFormat ) })) } +const enableFileTransport = function () { + this.add(new DailyRotateFile({ + format: winston.format.combine( + timestamp(), + defaultLogFormat + ), + filename: 'logs/application-%DATE%.log', + datePattern: 'YYYY-MM-DD', + zippedArchive: true, + maxSize: '20m', + maxFiles: '30d' + })) +} +const enableDiscordTransport = function (webhookUrl) { + this.add(new DiscordTransport({ + metadata: { + host: 'CodeQL Agent' + }, + discord: { + webhook: { + url: webhookUrl + } + }, + format: combine(discordLogFormat), + level: 'info' + })) +} + +/* + * There are two logger instances + * 1. Default logger: defaultLogger. This logger is used for logging to console and file + * 2. Bug logger: bugLogger. This logger is used for logging bugs found. +*/ +var defaultLogger = new createLogger({}); +var bugLogger = new createLogger({}); + +defaultLogger.setLevel = setLevel; +defaultLogger.enableConsoleTransport = enableConsoleTransport; +defaultLogger.enableFileTransport = enableFileTransport; +defaultLogger.enableDiscordTransport = enableDiscordTransport; + +bugLogger.setLevel = setLevel; +bugLogger.enableConsoleTransport = enableConsoleTransport; +bugLogger.enableFileTransport = enableFileTransport; +bugLogger.enableDiscordTransport = enableDiscordTransport; + +defaultLogger.setLevel('info') +defaultLogger.enableConsoleTransport() + +bugLogger.setLevel('info') -module.exports = logger; \ No newline at end of file +module.exports.defaultLogger = defaultLogger; +module.exports.bugLogger = bugLogger; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7125919..abd5846 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,9 @@ "linguist-js": "^2.5.4", "path": "^0.12.7", "which": "^3.0.0", - "winston": "^3.8.2" + "winston": "^3.8.2", + "winston-daily-rotate-file": "^4.7.1", + "winston-transport-discord": "^1.0.3" }, "bin": { "codeql-agent": "cli.js" @@ -57,6 +59,53 @@ "kuler": "^2.0.0" } }, + "node_modules/@discordjs/builders": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.16.0.tgz", + "integrity": "sha512-9/NCiZrLivgRub2/kBc0Vm5pMBE5AUdYbdXsLu/yg9ANgvnaJ0bZKTY8yYnLbsEc/LYUP79lEIdC73qEYhWq7A==", + "deprecated": "no longer supported", + "dependencies": { + "@sapphire/shapeshift": "^3.5.1", + "discord-api-types": "^0.36.2", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/collection": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz", + "integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==", + "deprecated": "no longer supported", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.5.0.tgz", + "integrity": "sha512-S4E1YNz1UxgUfMPpMeqzPPkCfXE877zOsvKM5WEmwIhcpz1PQV7lzqlEOuz194UuwOJLLjQFBgQELnQfCX9UfA==", + "deprecated": "no longer supported", + "dependencies": { + "@discordjs/collection": "^0.7.0", + "@sapphire/async-queue": "^1.3.1", + "@sapphire/snowflake": "^3.2.2", + "discord-api-types": "^0.33.3", + "tslib": "^2.4.0", + "undici": "^5.4.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest/node_modules/discord-api-types": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz", + "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==" + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", @@ -82,6 +131,37 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", + "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz", + "integrity": "sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz", + "integrity": "sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -109,9 +189,37 @@ "node_modules/@types/node": { "version": "18.11.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true, - "peer": true + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "dependencies": { + "@types/node": "*" + } }, "node_modules/abbrev": { "version": "1.1.1", @@ -183,6 +291,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -219,6 +332,17 @@ "node": ">=8" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -333,6 +457,17 @@ "text-hex": "1.0.x" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", @@ -375,6 +510,14 @@ "ms": "^2.1.1" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -384,11 +527,46 @@ "node": ">=0.3.1" } }, + "node_modules/discord-api-types": { + "version": "0.36.3", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.3.tgz", + "integrity": "sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==" + }, + "node_modules/discord.js": { + "version": "13.13.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.13.1.tgz", + "integrity": "sha512-4QdeSIGOIeZ25cTOxaPdVqIXz8tN56DetXFAwEkDHX5sDqbsXm9gMaNhfaOwMap3d5mgdC7I78g70I82x5i5lw==", + "dependencies": { + "@discordjs/builders": "^0.16.0", + "@discordjs/collection": "^0.7.0", + "@sapphire/async-queue": "^1.5.0", + "@types/node-fetch": "^2.6.2", + "@types/ws": "^8.5.3", + "discord-api-types": "^0.33.5", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "ws": "^8.9.0" + }, + "engines": { + "node": ">=16.6.0", + "npm": ">=7.0.0" + } + }, + "node_modules/discord.js/node_modules/discord-api-types": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.5.tgz", + "integrity": "sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==" + }, "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -402,6 +580,14 @@ "node": ">= 0.4.0" } }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "dependencies": { + "moment": "^2.29.1" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -419,6 +605,19 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -592,6 +791,11 @@ "node": "^12.20.0 || >=14" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/logform": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", @@ -610,6 +814,25 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -622,6 +845,14 @@ "node": "*" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -730,6 +961,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -871,6 +1110,14 @@ "node": "*" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -929,6 +1176,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "node_modules/ts-mixer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -972,6 +1224,11 @@ } } }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/typescript": { "version": "4.9.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", @@ -991,6 +1248,17 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "node_modules/undici": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", + "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.18" + } + }, "node_modules/util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -1059,6 +1327,23 @@ "node": ">= 12.0.0" } }, + "node_modules/winston-daily-rotate-file": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz", + "integrity": "sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==", + "dependencies": { + "file-stream-rotator": "^0.6.1", + "object-hash": "^2.0.1", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" + } + }, "node_modules/winston-transport": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", @@ -1072,6 +1357,39 @@ "node": ">= 6.4.0" } }, + "node_modules/winston-transport-discord": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/winston-transport-discord/-/winston-transport-discord-1.0.3.tgz", + "integrity": "sha512-pKF3Sie6haNk+MjyF60nkex1AdskJ6Ep2Oxym1oh1Te1Vgs162qoVg91W9Tb4doHivYBmPJZ90z7gHXarRxd2A==", + "dependencies": { + "@discordjs/rest": "^0.5.0", + "discord-api-types": "^0.36.1", + "discord.js": "^13.8.1", + "triple-beam": "^1.3.0", + "tslib": "^2.4.0", + "winston-transport": "^4.5.0" + } + }, + "node_modules/ws": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz", + "integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index d6bb643..b2c73bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeql-agent", - "version": "0.1.3", + "version": "0.2.0", "description": "A CodeQL tool to automatically execute code scanning.", "main": "index.js", "scripts": { @@ -38,7 +38,9 @@ "linguist-js": "^2.5.4", "path": "^0.12.7", "which": "^3.0.0", - "winston": "^3.8.2" + "winston": "^3.8.2", + "winston-daily-rotate-file": "^4.7.1", + "winston-transport-discord": "^1.0.3" }, "devDependencies": { "nodemon": "^2.0.20", diff --git a/utils.js b/utils.js index 4b6eaf6..31125c4 100644 --- a/utils.js +++ b/utils.js @@ -1,7 +1,7 @@ const CONFIG = require('./config.json'); module.exports = { - isCommandExist, isFolderExist, isSupportedLanguage, createIfNotExist, executeCommand, setupCreateDatabaseCommandArgs, getSourceLanguages, createCodeQLDatabase, normolizeString, isRemoteRepository, cloneRemoteRepository, removeFolder, setupScanCommandArgs, getDatabaseLanguages, parseSarif + isCommandExist, isFolderExist, isSupportedLanguage, createIfNotExist, executeCommand, setupCreateDatabaseCommandArgs, getSourceLanguages, createCodeQLDatabase, normolizeString, isRemoteRepository, cloneRemoteRepository, removeFolder, setupScanCommandArgs, getDatabaseLanguages, parseSarif, castBugLevelToLogLevel } /** @@ -220,7 +220,7 @@ async function setupScanCommandArgs(databaseFolderPath, options, logger) { args.push('database', 'analyze'); args.push(`--format=${options.format ? options.format : CONFIG.default.format}`); args.push(`--output=${outputPath}`); - options.noDownload ? null : args.push(`--download`); + options.download ? args.push(`--download`) : null; options.threads ? args.push(`--threads=${options.threads}`) : null; options.verbose ? args.push(`--verbose`) : null; @@ -331,4 +331,20 @@ async function parseSarif(sarifPath, logger) { return alert; }); return alerts; +} + +/** + * @desc cast bug level to log level + * @param {string} level - Bug level + * @return {string} - log level +*/ +function castBugLevelToLogLevel(level) { + switch (level) { + case 'error': + return 'error'; + case 'warning': + return 'warn'; + default: + return 'error'; + } } \ No newline at end of file