diff options
| author | Leo Goetz <dev@leogtz.de> | 2026-05-08 15:48:37 +0200 |
|---|---|---|
| committer | Leo Goetz <dev@leogtz.de> | 2026-05-08 15:48:37 +0200 |
| commit | c3cb0ca317a52a740bf9625ca9df43f5c2306548 (patch) | |
| tree | aa8877dc2d651f16d23a629c4054043cae8be156 /src | |
inital commit
Diffstat (limited to 'src')
| -rw-r--r-- | src/actions/checkDependencies.ts | 62 | ||||
| -rw-r--r-- | src/actions/outputSummary.ts | 15 | ||||
| -rw-r--r-- | src/actions/sendEmail.ts | 27 | ||||
| -rw-r--r-- | src/commands/check.ts | 19 | ||||
| -rw-r--r-- | src/commands/config.ts | 19 | ||||
| -rw-r--r-- | src/index.ts | 11 | ||||
| -rw-r--r-- | src/types.ts | 45 | ||||
| -rw-r--r-- | src/utils/config.ts | 48 | ||||
| -rw-r--r-- | src/utils/email.ts | 18 |
9 files changed, 264 insertions, 0 deletions
diff --git a/src/actions/checkDependencies.ts b/src/actions/checkDependencies.ts new file mode 100644 index 0000000..4746bfd --- /dev/null +++ b/src/actions/checkDependencies.ts @@ -0,0 +1,62 @@ +import fs from "fs/promises"; +import ora from "ora"; +import type { Config, Project } from "../types.js"; +import { getConfig } from "../utils/config.js"; +import { exec } from "child_process"; + +export async function checkDependencies() { + const config: Config = await getConfig(); + let projectsOutputs: Project[] = []; + let projects: Promise<Project>[] = []; + + const spinner = ora("Getting all Project Data").start(); + + try { + const dirs = await fs.readdir(config.path); + + for (let dir of dirs) { + let dirFullPath = `${config.path}${dir}`; + const projectDir = await fs.readdir(dirFullPath); + if (projectDir.includes("package.json")) { + projects.push(getProjectPromise(dirFullPath, dir, spinner)); + } + } + + projectsOutputs = await Promise.all(projects); + + spinner.succeed("Got the Data successfully"); + } catch (error) { + spinner.fail("Ups and Error :("); + console.log(error); + } + + return projectsOutputs; +} + +function getProjectPromise( + path: string, + dirname: string, + spinner: any, +): Promise<Project> { + return new Promise(async (resolve, _) => { + spinner.text = "pulling latest"; + await promiseExec(`cd "${path}" && git pull `, () => {}); + spinner.text = "getting audit"; + promiseExec( + `cd "${path}" && npm audit --json`, + (_: any, stdout: string) => { + let output = JSON.parse(stdout); + let project: Project = { projectName: dirname, ...output }; + resolve(project); + }, + ); + }); +} + +function promiseExec(cmd: string, callback: any) { + return new Promise((resolve, _) => { + exec(cmd, (_, stdout, __) => { + resolve(callback(_, stdout, __)); + }); + }); +} diff --git a/src/actions/outputSummary.ts b/src/actions/outputSummary.ts new file mode 100644 index 0000000..5a376cb --- /dev/null +++ b/src/actions/outputSummary.ts @@ -0,0 +1,15 @@ +import chalk from "chalk"; +import type { Project } from "../types.js"; + +export function outputSummary(projects: Project[]) { + const text = ` + This is what i found: + ${projects.map((project) => { + let projectVulnerabilities = project.metadata.vulnerabilities.total; + return ` + ${project.projectName} has ${projectVulnerabilities > 0 ? chalk.bold.red(projectVulnerabilities) : chalk.bold.green(projectVulnerabilities)} Security Issues`; + })} + `; + + console.log(text); +} diff --git a/src/actions/sendEmail.ts b/src/actions/sendEmail.ts new file mode 100644 index 0000000..50394c5 --- /dev/null +++ b/src/actions/sendEmail.ts @@ -0,0 +1,27 @@ +import nodemailer from "nodemailer"; +import { emailConfig } from "../utils/email.js"; +import type { Project } from "../types.js"; + +const transporter = nodemailer.createTransport(emailConfig); + +export const sendAuditEmail = async (projects: Project[]) => { + const text = emailContent(projects); + const email = await transporter.sendMail({ + from: `"${emailConfig.senderName}" <${emailConfig.senderEmail}>`, + to: `${emailConfig.reciever}`, + subject: emailConfig.subject ?? "Dependency Audit!", + text: text, + }); + + return email; +}; + +const emailContent = (projects: Project[]): string => { + return `Here is your Report: +${projects.map((project) => { + let projectVulnerabilities = project.metadata.vulnerabilities.total; + return ` +${project.projectName} has ${projectVulnerabilities} Security Issues`; +})} +`; +}; diff --git a/src/commands/check.ts b/src/commands/check.ts new file mode 100644 index 0000000..cfa3cb1 --- /dev/null +++ b/src/commands/check.ts @@ -0,0 +1,19 @@ +import type { Command } from "commander"; +import { checkDependencies } from "../actions/checkDependencies.js"; +import { outputSummary } from "../actions/outputSummary.js"; +import { sendAuditEmail } from "../actions/sendEmail.js"; + +export const check = (program: Command) => { + program + .command("check") + .description("Checks given repos for security updates and logs output.") + .option("-e, --email", "Send Email Report via the configured Email.") + .action(async (cmd) => { + const data = await checkDependencies(); + if (cmd.email) { + sendAuditEmail(data); + } else { + outputSummary(data); + } + }); +}; diff --git a/src/commands/config.ts b/src/commands/config.ts new file mode 100644 index 0000000..c888766 --- /dev/null +++ b/src/commands/config.ts @@ -0,0 +1,19 @@ +import type { Command } from "commander"; +import { initConfig } from "../utils/config.js"; + +export const config = (program: Command) => { + program + .command("config") + .argument("<action>") + .action(async (action: string) => { + switch (action) { + case "init": + await initConfig(); + break; + default: + console.log( + "No valid action. These are valid: init. More comming soon...", + ); + } + }); +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3a6b348 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,11 @@ +#!/usr/bin/env node +import { Command } from "commander"; +import { check } from "./commands/check.js"; +import { config } from "./commands/config.js"; + +const program = new Command(); + +check(program); +config(program); + +program.parse(process.argv); diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..db84517 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,45 @@ +export interface EmailConfig { + host: string; + port: number; + secure: boolean; + auth: { + user: string; + pass: string; + }; + senderEmail: string; + senderName: string; + subject: string; + reciever: string; +} + +export interface Config { + path: string; + email?: EmailConfig; +} + +export interface Output { + auditReportVersion: number; + vulnerabilities: object; + metadata: { + vulnerabilities: { + info: number; + low: number; + moderate: number; + high: number; + critical: number; + total: number; + }; + dependencies: { + prod: number; + dev: number; + optional: number; + peer: number; + peerOptional: number; + total: number; + }; + }; +} + +export interface Project extends Output { + projectName: string; +} diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 0000000..62080f7 --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,48 @@ +import os from "os"; +import fs from "fs/promises"; +import type { Config } from "../types.js"; + +const configPath: string = new URL( + `${os.homedir()}/.config/dephelp/`, + import.meta.url, +).pathname; + +const configFile: string = `${configPath}/config.json`; + +const sampleConfig: Config = { + path: "/your/projects/ (absolute path!)", + email: { + host: "mail.example.com", + port: 587, + secure: true, + auth: { + user: "example", + pass: "example-password", + }, + senderEmail: "audit@example.com", + senderName: "Dependency Reporter", + subject: "Your Project(s) are insecure!", + reciever: "your@beautiful.email", + }, +}; + +export const getConfig = async (): Promise<Config> => { + const content = await fs.readFile(configFile, { encoding: "utf8" }); + const config: Config = JSON.parse(content); + return config; +}; + +export const initConfig = async () => { + try { + await fs.access(configPath); + } catch { + await fs.mkdir(configPath); + } + + try { + await fs.writeFile(`${configFile}`, JSON.stringify(sampleConfig, null, 2)); + console.log(`Config got generated in /.config/dephelp/config.json`); + } catch (error) { + console.error(error); + } +}; diff --git a/src/utils/email.ts b/src/utils/email.ts new file mode 100644 index 0000000..828603b --- /dev/null +++ b/src/utils/email.ts @@ -0,0 +1,18 @@ +import type { Config, EmailConfig } from "../types.js"; +import { getConfig } from "./config.js"; + +const config: Config = await getConfig(); + +export const emailConfig: EmailConfig = { + host: config.email!.host, + port: config.email!.port, + secure: config.email!.secure, + auth: { + user: config.email!.auth.user, + pass: config.email!.auth.pass, + }, + senderEmail: config.email!.senderEmail, + senderName: config.email!.senderName, + subject: config.email!.subject, + reciever: config.email!.reciever, +}; |
