summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLeo Goetz <dev@leogtz.de>2026-05-08 15:48:37 +0200
committerLeo Goetz <dev@leogtz.de>2026-05-08 15:48:37 +0200
commitc3cb0ca317a52a740bf9625ca9df43f5c2306548 (patch)
treeaa8877dc2d651f16d23a629c4054043cae8be156 /src
inital commit
Diffstat (limited to 'src')
-rw-r--r--src/actions/checkDependencies.ts62
-rw-r--r--src/actions/outputSummary.ts15
-rw-r--r--src/actions/sendEmail.ts27
-rw-r--r--src/commands/check.ts19
-rw-r--r--src/commands/config.ts19
-rw-r--r--src/index.ts11
-rw-r--r--src/types.ts45
-rw-r--r--src/utils/config.ts48
-rw-r--r--src/utils/email.ts18
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,
+};