mobbdev 0.0.28 → 0.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,57 +1,78 @@
1
1
  {
2
- "name": "mobbdev",
3
- "version": "0.0.28",
4
- "description": "Automated secure code remediation tool",
5
- "repository": "https://github.com/mobb-dev/bugsy",
6
- "main": "index.mjs",
7
- "scripts": {
8
- "lint": "prettier --check . && eslint **/*.mjs",
9
- "lint:fix": "prettier --write . && eslint --fix **/*.mjs",
10
- "test": "TOKEN=$(../../scripts/login_auth0.sh) NODE_OPTIONS=--experimental-vm-modules jest",
11
- "prepack": "dotenv-vault pull production .env"
12
- },
13
- "bin": {
14
- "mobbdev": "bin/cli.mjs"
15
- },
16
- "author": "",
17
- "license": "MIT",
18
- "dependencies": {
19
- "adm-zip": "0.5.10",
20
- "chalk": "5.3.0",
21
- "chalk-animation": "2.0.3",
22
- "colors": "1.4.0",
23
- "configstore": "6.0.0",
24
- "debug": "4.3.4",
25
- "dotenv": "16.0.3",
26
- "extract-zip": "2.0.1",
27
- "inquirer": "9.2.7",
28
- "globby": "13.2.2",
29
- "nanospinner": "1.1.0",
30
- "node-fetch": "3.3.1",
31
- "open": "8.4.2",
32
- "semver": "7.5.0",
33
- "snyk": "1.1118.0",
34
- "simple-git": "3.19.1",
35
- "tmp": "0.2.1",
36
- "yargs": "17.7.2",
37
- "zod": "3.21.4"
38
- },
39
- "devDependencies": {
40
- "@jest/globals": "29.5.0",
41
- "eslint": "8.36.0",
42
- "jest": "29.5.0",
43
- "prettier": "2.8.4"
44
- },
45
- "engines": {
46
- "node": ">=12.20.0"
47
- },
48
- "type": "module",
49
- "files": [
50
- "bin",
51
- "src",
52
- ".env",
53
- "README.md",
54
- "LICENSE",
55
- "package.json"
56
- ]
2
+ "name": "mobbdev",
3
+ "version": "0.0.30",
4
+ "description": "Automated secure code remediation tool",
5
+ "repository": "https://github.com/mobb-dev/bugsy",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "scripts": {
9
+ "build": "tsc && tsup-node --env.NODE_ENV production",
10
+ "build:dev": "tsup-node --env.NODE_ENV development",
11
+ "test": "TOKEN=$(../../scripts/login_auth0.sh) vitest run",
12
+ "test:watch": "TOKEN=$(../../scripts/login_auth0.sh) vitest",
13
+ "lint": "eslint --cache --max-warnings 0 --ignore-path .eslintignore --ext .ts,.tsx,.jsx .",
14
+ "lint:fix": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx .",
15
+ "lint:fix:files": "eslint --fix --cache --max-warnings 0 --ignore-path .eslintignore --ext .js,.ts,.tsx,.jsx",
16
+ "prepack": "pnpm build && dotenv-vault pull production .env"
17
+ },
18
+ "bin": {
19
+ "mobbdev": "bin/cli.mjs"
20
+ },
21
+ "author": "",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "adm-zip": "0.5.10",
25
+ "chalk-animation": "2.0.3",
26
+ "chalk": "5.3.0",
27
+ "configstore": "6.0.0",
28
+ "debug": "4.3.4",
29
+ "dotenv": "16.0.3",
30
+ "extract-zip": "2.0.1",
31
+ "globby": "13.2.2",
32
+ "graphql-request": "5.0.0",
33
+ "graphql": "16.6.0",
34
+ "inquirer": "9.2.7",
35
+ "nanospinner": "1.1.0",
36
+ "node-fetch": "3.3.1",
37
+ "octokit": "2.0.14",
38
+ "open": "8.4.2",
39
+ "semver": "7.5.0",
40
+ "simple-git": "3.19.1",
41
+ "snyk": "1.1118.0",
42
+ "supports-color": "9.4.0",
43
+ "tmp": "0.2.1",
44
+ "yargs": "17.7.2",
45
+ "zod": "3.21.4"
46
+ },
47
+ "devDependencies": {
48
+ "@octokit/plugin-rest-endpoint-methods": "7.0.1",
49
+ "@octokit/request-error": "3.0.3",
50
+ "@types/adm-zip": "0.5.0",
51
+ "@types/chalk-animation": "1.6.1",
52
+ "@types/configstore": "6.0.0",
53
+ "@types/debug": "4.1.8",
54
+ "@types/inquirer": "9.0.3",
55
+ "@types/semver": "7.5.0",
56
+ "@types/tmp": "0.2.3",
57
+ "@types/yargs": "17.0.24",
58
+ "@typescript-eslint/eslint-plugin": "5.44.0",
59
+ "@typescript-eslint/parser": "5.44.0",
60
+ "eslint-plugin-import": "2.27.5",
61
+ "eslint-plugin-prettier": "4.2.1",
62
+ "eslint-plugin-simple-import-sort": "10.0.0",
63
+ "eslint": "8.36.0",
64
+ "prettier": "2.8.4",
65
+ "tsup": "7.2.0",
66
+ "typescript": "4.9.3",
67
+ "vitest": "0.26.2"
68
+ },
69
+ "engines": {
70
+ "node": ">=12.20.0"
71
+ },
72
+ "type": "module",
73
+ "files": [
74
+ "bin",
75
+ "dist",
76
+ ".env"
77
+ ]
57
78
  }
package/index.mjs DELETED
@@ -1,34 +0,0 @@
1
- import { analyze, scan, CliError } from './src/commands/index.mjs';
2
- import { parseArgs } from './src/yargs.mjs';
3
- import { hideBin } from 'yargs/helpers';
4
-
5
- async function run() {
6
- const args = await parseArgs(hideBin(process.argv));
7
- const [command] = args._;
8
- if (command === 'scan') {
9
- const { yes, ...restArgs } = args;
10
- await scan(restArgs, { skipPrompts: yes });
11
- }
12
- if (command === 'analyze') {
13
- const { yes, ...restArgs } = args;
14
- await analyze(restArgs, { skipPrompts: yes });
15
- }
16
- }
17
-
18
- (async () => {
19
- try {
20
- await run();
21
- process.exit(0);
22
- } catch (err) {
23
- if (err instanceof CliError) {
24
- console.error(err.message);
25
- process.exit(1);
26
- }
27
- // unexpected error - print stack trace
28
- console.error(
29
- 'Something went wrong, please try again or contact support if issue persists.'
30
- );
31
- console.error(err);
32
- process.exit(1);
33
- }
34
- })();
@@ -1,101 +0,0 @@
1
- import { z } from 'zod';
2
- import fs from 'node:fs';
3
- import chalk from 'chalk';
4
- import chalkAnimation from 'chalk-animation';
5
- import { choseScanner } from '../features/analysis/prompts.mjs';
6
- import { SCANNERS, mobbAscii } from '../constants.mjs';
7
- import { runAnalysis } from '../features/analysis/index.mjs';
8
- import { sleep } from '../utils.mjs';
9
- import path from 'path';
10
-
11
- const GITHUB_REPO_URL_PATTERN = new RegExp(
12
- 'https://github.com/[\\w-]+/[\\w-]+'
13
- );
14
- export class CliError extends Error {}
15
-
16
- const UrlZ = z
17
- .string({
18
- invalid_type_error: 'is not a valid github URL',
19
- })
20
- .regex(GITHUB_REPO_URL_PATTERN, {
21
- message: 'is not a valid github URL',
22
- });
23
-
24
- function throwRepoUrlErrorMessage({ error, repoUrl, command }) {
25
- const errorMessage = error.issues[error.issues.length - 1].message;
26
- const formattedErrorMessage = `\nError: ${chalk.bold(
27
- repoUrl
28
- )} is ${errorMessage}
29
- Example: \n\tmobbdev ${command} -r ${chalk.bold(
30
- 'https://github.com/WebGoat/WebGoat'
31
- )}`;
32
- throw new CliError(formattedErrorMessage);
33
- }
34
-
35
- export async function analyze(
36
- { repo, scanFile, ref, apiKey, ci, commitHash, srcPath },
37
- { skipPrompts = false } = {}
38
- ) {
39
- const { success, error } = UrlZ.safeParse(repo);
40
-
41
- if (!success && !srcPath) {
42
- throwRepoUrlErrorMessage({
43
- error,
44
- repoUrl: repo,
45
- command: 'analyze',
46
- });
47
- }
48
-
49
- if (!fs.existsSync(scanFile)) {
50
- throw new CliError(`\nCan't access ${chalk.bold(scanFile)}`);
51
- }
52
-
53
- if (path.extname(scanFile) !== '.json') {
54
- throw new CliError(`\n${chalk.bold(scanFile)} is not a json file`);
55
- }
56
-
57
- !ci && (await showWelcomeMessage(skipPrompts));
58
-
59
- await runAnalysis(
60
- {
61
- repo,
62
- scanFile,
63
- ref,
64
- apiKey,
65
- ci,
66
- commitHash,
67
- srcPath,
68
- },
69
- { skipPrompts }
70
- );
71
- }
72
-
73
- export async function scan(
74
- { repo, scanner, branch, apiKey, ci },
75
- { skipPrompts = false } = {}
76
- ) {
77
- const { success, error } = UrlZ.safeParse(repo);
78
- if (ci && !apiKey) {
79
- throw new CliError(
80
- '\nError: --ci flag requires --api-key to be provided as well'
81
- );
82
- }
83
-
84
- if (!success) {
85
- throwRepoUrlErrorMessage({ error, repoUrl: repo, command: 'scan' });
86
- }
87
- !ci && (await showWelcomeMessage(skipPrompts));
88
- scanner ??= scanner || (await choseScanner());
89
- if (scanner !== SCANNERS.Snyk) {
90
- throw new CliError(
91
- 'Vulnerability scanning via Bugsy is available only with Snyk at the moment. Additional scanners will follow soon.'
92
- );
93
- }
94
- await runAnalysis({ repo, scanner, branch, apiKey }, { skipPrompts });
95
- }
96
- async function showWelcomeMessage(skipPrompts = false) {
97
- console.log(mobbAscii);
98
- const welcome = chalkAnimation.rainbow('\n\t\t\tWelcome to Bugsy\n');
99
- skipPrompts ? await sleep(100) : await sleep(2000);
100
- welcome.stop();
101
- }
package/src/constants.mjs DELETED
@@ -1,60 +0,0 @@
1
- import path from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- import * as dotenv from 'dotenv';
4
- import { z } from 'zod';
5
- import Debug from 'debug';
6
-
7
- const debug = Debug('mobbdev:constants');
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
- dotenv.config({ path: path.join(__dirname, '../.env') });
10
-
11
- export const SCANNERS = {
12
- Checkmarx: 'checkmarx',
13
- Codeql: 'codeql',
14
- Fortify: 'fortify',
15
- Snyk: 'snyk',
16
- };
17
-
18
- const envVariablesSchema = z
19
- .object({
20
- WEB_LOGIN_URL: z.string(),
21
- WEB_APP_URL: z.string(),
22
- API_URL: z.string(),
23
- })
24
- .required();
25
-
26
- const envVariables = envVariablesSchema.parse(process.env);
27
- debug('config %o', envVariables);
28
-
29
- export const mobbAscii = `
30
- ..
31
- ..........
32
- .................
33
- ...........................
34
- ..............................
35
- ................................
36
- ..................................
37
- ....................................
38
- .....................................
39
- .............................................
40
- .................................................
41
- ............................... .................
42
- .................................. ............
43
- .................. ............. ..........
44
- ......... ........ ......... ......
45
- ............... ....
46
- .... ..
47
-
48
- . ...
49
- ..............
50
- ......................
51
- ...........................
52
- ................................
53
- ......................................
54
- ...............................
55
- .................
56
- `;
57
-
58
- export const WEB_LOGIN_URL = envVariables.WEB_LOGIN_URL;
59
- export const WEB_APP_URL = envVariables.WEB_APP_URL;
60
- export const API_URL = envVariables.API_URL;
@@ -1,61 +0,0 @@
1
- import Debug from 'debug';
2
- import { setTimeout, clearTimeout } from 'node:timers';
3
- import http from 'node:http';
4
- import querystring from 'node:querystring';
5
- import open from 'open';
6
-
7
- const debug = Debug('mobbdev:web-login');
8
-
9
- export async function callbackServer(url, redirectUrl) {
10
- debug('web login start');
11
-
12
- let responseResolver;
13
- let responseRejecter;
14
- const responseAwaiter = new Promise((resolve, reject) => {
15
- responseResolver = resolve;
16
- responseRejecter = reject;
17
- });
18
- const timerHandler = setTimeout(() => {
19
- debug('timeout happened');
20
- responseRejecter(new Error('No login happened in three minutes.'));
21
- }, 180000);
22
-
23
- const server = http.createServer((req, res) => {
24
- debug('incoming request');
25
- let body = '';
26
-
27
- req.on('data', (chunk) => {
28
- debug('http server get chunk %s', chunk);
29
- body += chunk;
30
- });
31
-
32
- req.on('end', () => {
33
- debug('http server end %s', body);
34
- res.writeHead(301, {
35
- Location: redirectUrl,
36
- }).end();
37
-
38
- responseResolver(querystring.parse(body));
39
- });
40
- });
41
-
42
- debug('http server starting');
43
- const port = await new Promise((resolve) => {
44
- server.listen(0, '127.0.0.1', () => {
45
- resolve(server.address().port);
46
- });
47
- });
48
- debug('http server started on port %d', port);
49
-
50
- debug('opening the browser on %s', `${url}?port=${port}`);
51
- await open(`${url}?port=${port}`);
52
-
53
- try {
54
- debug('waiting for http request');
55
- return await responseAwaiter;
56
- } finally {
57
- debug('http server close');
58
- clearTimeout(timerHandler);
59
- server.close();
60
- }
61
- }
@@ -1,50 +0,0 @@
1
- import { simpleGit } from 'simple-git';
2
- import Debug from 'debug';
3
-
4
- const debug = Debug('mobbdev:git');
5
-
6
- export async function getGitInfo(srcDirPath) {
7
- debug('getting git info for %s', srcDirPath);
8
-
9
- const git = simpleGit({
10
- baseDir: srcDirPath,
11
- // binary: 'git123',
12
- maxConcurrentProcesses: 1,
13
- trimmed: true,
14
- });
15
-
16
- let repoUrl = '';
17
- let hash = '';
18
- let reference = '';
19
-
20
- try {
21
- repoUrl = (await git.getConfig('remote.origin.url')).value || '';
22
- hash = (await git.revparse(['HEAD'])) || '';
23
- reference = (await git.revparse(['--abbrev-ref', 'HEAD'])) || '';
24
- } catch (e) {
25
- debug('failed to run git %o', e);
26
- if (e.message && e.message.includes(' spawn ')) {
27
- debug('git cli not installed');
28
- } else if (e.message && e.message.includes(' not a git repository ')) {
29
- debug('folder is not a git repo');
30
- } else {
31
- throw e;
32
- }
33
- }
34
-
35
- // Normalize git URL. We may need more generic code here, but it's not very
36
- // important at the moment.
37
- if (repoUrl.endsWith('.git')) {
38
- repoUrl = repoUrl.slice(0, -'.git'.length);
39
- }
40
-
41
- if (repoUrl.startsWith('git@github.com:')) {
42
- repoUrl = repoUrl.replace('git@github.com:', 'https://github.com/');
43
- }
44
-
45
- return {
46
- repoUrl,
47
- hash,
48
- reference,
49
- };
50
- }
@@ -1,106 +0,0 @@
1
- import fs from 'node:fs';
2
- import chalk from 'chalk';
3
- import stream from 'node:stream';
4
- import path from 'node:path';
5
- import { promisify } from 'node:util';
6
- import fetch from 'node-fetch';
7
- import extract from 'extract-zip';
8
- import Debug from 'debug';
9
- import { Spinner } from '../../utils.mjs';
10
- const pipeline = promisify(stream.pipeline);
11
- const debug = Debug('mobbdev:github');
12
-
13
- async function getRepo(slug, { token } = {}) {
14
- try {
15
- return fetch(`https://api.github.com/repos/${slug}`, {
16
- method: 'GET',
17
- headers: {
18
- Accept: 'application/vnd.github+json',
19
- 'X-GitHub-Api-Version': '2022-11-28',
20
- ...(token && { Authorization: `bearer ${token}` }),
21
- },
22
- });
23
- } catch (e) {
24
- debug(`error fetching the repo ${slug}`, e);
25
- throw e;
26
- }
27
- }
28
-
29
- function extractSlug(repoUrl) {
30
- debug('get default branch %s', repoUrl);
31
- let slug = repoUrl.replace(/https?:\/\/github\.com\//i, '');
32
-
33
- if (slug.endsWith('/')) {
34
- slug = slug.substring(0, slug.length - 1);
35
- }
36
-
37
- if (slug.endsWith('.git')) {
38
- slug = slug.substring(0, slug.length - '.git'.length);
39
- }
40
- debug('slug %s', slug);
41
- return slug;
42
- }
43
-
44
- export async function canReachRepo(repoUrl, { token } = {}) {
45
- const slug = extractSlug(repoUrl);
46
- const response = await getRepo(slug, { token });
47
- return response.ok;
48
- }
49
-
50
- export async function getDefaultBranch(repoUrl, { token } = {}) {
51
- const slug = extractSlug(repoUrl);
52
- const response = await getRepo(slug, { token });
53
- if (!response.ok) {
54
- debug('GH request failed %s %s', response.body, response.status);
55
- throw new Error(
56
- `Can't get default branch, make sure you have access to : ${repoUrl}.`
57
- );
58
- }
59
-
60
- const repoInfo = await response.json();
61
- debug('GH request ok %o', repoInfo);
62
-
63
- return repoInfo.default_branch;
64
- }
65
-
66
- export async function downloadRepo(
67
- { repoUrl, reference, dirname, ci },
68
- { token } = {}
69
- ) {
70
- const { createSpinner } = Spinner({ ci });
71
- const repoSpinner = createSpinner('💾 Downloading Repo').start();
72
- debug('download repo %s %s %s', repoUrl, reference, dirname);
73
- const zipFilePath = path.join(dirname, 'repo.zip');
74
- const response = await fetch(`${repoUrl}/zipball/${reference}`, {
75
- method: 'GET',
76
- headers: {
77
- ...(token && { Authorization: `bearer ${token}` }),
78
- },
79
- });
80
- if (!response.ok) {
81
- debug(
82
- 'GH zipball request failed %s %s',
83
- response.body,
84
- response.status
85
- );
86
- repoSpinner.error({ text: '💾 Repo download failed' });
87
- throw new Error(
88
- `Can't access the the branch ${chalk.bold(
89
- reference
90
- )} on ${chalk.bold(repoUrl)} make sure it exits.`
91
- );
92
- }
93
-
94
- const fileWriterStream = fs.createWriteStream(zipFilePath);
95
-
96
- await pipeline(response.body, fileWriterStream);
97
- await extract(zipFilePath, { dir: dirname });
98
-
99
- const repoRoot = fs
100
- .readdirSync(dirname, { withFileTypes: true })
101
- .filter((dirent) => dirent.isDirectory())
102
- .map((dirent) => dirent.name)[0];
103
- debug('repo root %s', repoRoot);
104
- repoSpinner.success({ text: '💾 Repo downloaded successfully' });
105
- return path.join(dirname, repoRoot);
106
- }