Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug[website-builder]: [issue#1057 correct functions and convert to ts] #6

Open
wants to merge 4 commits into
base: the-one
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/cron.stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ jobs:
- name: Unassign contributor after days of inactivity
uses: BoundfoxStudios/action-unassign-contributor-after-days-of-inactivity@v1.0.3
with:
last-activity: 14
last-activity: 14
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved

220 changes: 125 additions & 95 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,106 +1,136 @@
import express from "express";
import {exec} from "child_process";
import crypto from "crypto";
import dotenv from "dotenv";

dotenv.config();

const app = express();
const port = process.env.PORT || 3000;

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const child_process_1 = require("child_process");
const crypto_1 = __importDefault(require("crypto"));
const dotenv_1 = __importDefault(require("dotenv"));
dotenv_1.default.config();
const app = (0, express_1.default)();
const port = process.env.PORT || '3333';
const secret = process.env.GITHUB_SECRET;
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved
if (!secret) {
console.error("Error: GITHUB_SECRET environment variable is not set.");
process.exit(1);
}
;
let isDocumentationWebsiteUpdated = false;
let isMindmapUpdated = false;
let contributorsBuildRequired = false;

let documentationWebsiteBuildTime = 0;
let mindmapBuildTime = 0;
let contributorsBuildTime = 0;

app.use(express.json());

app.post("/webhook", async (req, res) => {
console.log("req receieved");
const signature = req.headers["x-hub-signature"];
const payload = JSON.stringify(req.body);

const hmac = crypto.createHmac("sha1", process.env.GITHUB_SECRET);
const calculatedSignature = `sha1=${hmac.update(payload).digest("hex")}`;

if (crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(calculatedSignature))) {
const { result, respMessage } = await getBranchStatus();
console.log("Result: ", result);
res.status(result).send(respMessage);
} else {
res.status(400).send("Invalid Github signature");
}
});

app.listen(process.env.PORT, () => {
console.log(`Server listening on port ${port}`);
// fixing TS errors when back
app.use(express_1.default.json());
app.post("/webhook", (req, res) => __awaiter(void 0, void 0, void 0, function* () {
console.log("Request received");
const signature = req.headers["x-hub-signature"];
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
const payload = JSON.stringify(req.body);
const hmac = crypto_1.default.createHmac("sha1", secret);
const calculatedSignature = `sha1=${hmac.update(payload).digest("hex")}`;
console.log("Calculated signature received", calculatedSignature); // need this to stay as number seems to change
if (crypto_1.default.timingSafeEqual(Buffer.from(signature), Buffer.from(calculatedSignature))) {
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
const { result, respMessage } = yield getBranchStatus(req.body);
console.log("Result: ", result);
res.status(200).send({ result, respMessage });
}
else {
res.status(400).send({ error: "Invalid GitHub signature. Ensure the secret is correctly configured." });
}
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
}));
;
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});

const executeCmd = async (cmd) => {
try {
const { stdout, stderr } = await exec(cmd);
return stderr + "\n" + stdout;
} catch (error) {
console.error(`exec error: ${error}`);
throw new Error(stderr + "\n" + stdout);
}
};

const getBranchStatus = async (req) => {
console.log("Webhook received successfully");

const branchName = req.body?.ref?.split("/").pop();
if (!branchName) {
return 400, "Branch name not found in the request.";
}
return branchName === process.env.BRANCH_NAME ? await buildProject() : 202, "Build not required.";
const sanitize = (cmd) => {
const sanitized = cmd.replace(/[;&|`<>$(){}[\]]/g, '');
if (/[\r\n\t]/.test(cmd)) {
throw new Error("Invalid characters in command");
}
return sanitized;
};
Idrinth marked this conversation as resolved.
Show resolved Hide resolved

const executeCmd = (cmd) => __awaiter(void 0, void 0, void 0, function* () {
try {
const { stdout, stderr } = yield (0, child_process_1.exec)(sanitize(cmd));
return stderr + "\n" + stdout;
}
catch (error) {
console.error(`exec error: ${error}`);
throw new Error("Command execution failed. Check logs for details.");
}
;
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
});
const getBranchStatus = (req) => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b;
console.log("Webhook received successfully");
const branchName = (_b = (_a = req.body) === null || _a === void 0 ? void 0 : _a.ref) === null || _b === void 0 ? void 0 : _b.split("/").pop();
if (!branchName) {
return { result: 400, respMessage: "Branch name not found in the request." };
}
if (branchName === process.env.BRANCH_NAME) {
const { status, message } = yield buildProject();
return { result: status, respMessage: message };
}
return { result: 200, respMessage: "Build not required." };
});
const isUpdateRequired = () => {
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
const currentTime = Date.now();
isMindmapUpdated = (currentTime - mindmapBuildTime) / 1000 / 60 > process.env.MINDMAP_UPDATE_TIME_INTERVAL ? true : false;
isDocumentationWebsiteUpdated = (currentTime - documentationWebsiteBuildTime) / 1000 / 60 > process.env.DOCUMENTATION_WEBSITE_UPDATE_TIME_INTERVAL ? true : false;
return isMindmapUpdated || isDocumentationWebsiteUpdated;
var _a, _b;
const currentTime = Date.now();
const mindMapUpdateInterval = Number.parseInt((_a = process.env.MINDMAP_UPDATE_TIME_INTERVAL) !== null && _a !== void 0 ? _a : "10000");
const documentationWebsiteUpdateInterval = Number.parseInt((_b = process.env.DOCUMENTATION_WEBSITE_UPDATE_TIME_INTERVAL) !== null && _b !== void 0 ? _b : "10000");
isMindmapUpdated = (currentTime - mindmapBuildTime) / 1000 / 60 > mindMapUpdateInterval;
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
isDocumentationWebsiteUpdated = (currentTime - documentationWebsiteBuildTime) / 1000 / 60 > documentationWebsiteUpdateInterval;
return isMindmapUpdated || isDocumentationWebsiteUpdated;
};

const buildProject = async () => {
const currentTime = Date.now();
if (!isUpdateRequired()) {
if (contributorsBuildRequired || (currentTime - contributorsBuildTime) / 1000 / 60 > process.env.CONTRIBUTORS_UPDATE_TIME_INTERVAL) {
console.log("No update required, updating the contributors only");
await initiateBuild("npm run contributor-build", process.env.DOCUMENTATION_WEBSITE_PATH, process.env.DOCUMENTATION_WEBSITE_DEST_PATH);
contributorsBuildTime = currentTime;
contributorsBuildRequired = false;
return 200;
} else {
contributorsBuildRequired = true;
return 202, "Contributors build will be done after the next build.";
const buildProject = () => __awaiter(void 0, void 0, void 0, function* () {
var _a;
const currentTime = Date.now();
const contributionUpdateTimeInterval = Number.parseInt((_a = process.env.CONTRIBUTORS_UPDATE_TIME_INTERVAL) !== null && _a !== void 0 ? _a : "10000");
if (!process.env.DOCUMENTATION_WEBSITE_PATH) {
console.log('error');
}
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
}
if (isMindmapUpdated) {
console.log("Building Mindmap");
await initiateBuild("npm run build", process.env.MINDMAP_PATH, process.env.MINDMAP_DEST_PATH);
mindmapBuildTime = currentTime;
isMindmapUpdated = false;
}

if (isDocumentationWebsiteUpdated) {
console.log("Building Documentation Website");
await initiateBuild("npm run build", process.env.DOCUMENTATION_WEBSITE_PATH, process.env.DOCUMENTATION_WEBSITE_DEST_PATH);
documentationWebsiteBuildTime = currentTime;
contributorsBuildTime = currentTime;
isDocumentationWebsiteUpdated = false;
}

return 200, "Build has been created.";
};

const initiateBuild = async (command, projectPath, destPath) => {
await executeCmd(`cd ${projectPath}/ && git pull`);
await executeCmd(`cd ${projectPath}/ && npm ci`);
await executeCmd(`cd ${projectPath}/ && ${command}`);
await executeCmd(`cp -r ${projectPath}/dist/ ${destPath}/`);
};
if (!isUpdateRequired()) {
if (contributorsBuildRequired || (currentTime - contributorsBuildTime) / 1000 / 60 > contributionUpdateTimeInterval) {
console.log("No update required, updating the contributors only");
yield initiateBuild("npm run contributor-build", process.env.DOCUMENTATION_WEBSITE_PATH, process.env.DOCUMENTATION_WEBSITE_DEST_PATH);
contributorsBuildTime = currentTime;
contributorsBuildRequired = false;
return { status: 200, message: "Contributors build has been created." };
}
else {
contributorsBuildRequired = true;
return { status: 202, message: "Contributors build will be done after the next build." };
}
}
if (isMindmapUpdated) {
console.log("Building Mindmap");
yield initiateBuild("npm run build", process.env.MINDMAP_PATH, process.env.MINDMAP_DEST_PATH);
mindmapBuildTime = currentTime;
isMindmapUpdated = false;
}
if (isDocumentationWebsiteUpdated) {
console.log("Building Documentation Website");
yield initiateBuild("npm run build", process.env.DOCUMENTATION_WEBSITE_PATH, process.env.DOCUMENTATION_WEBSITE_DEST_PATH);
documentationWebsiteBuildTime = currentTime;
contributorsBuildTime = currentTime;
isDocumentationWebsiteUpdated = false;
}
return { status: 200, message: "Contributors build will be done after the next build." };
});
const initiateBuild = (command, projectPath, destPath) => __awaiter(void 0, void 0, void 0, function* () {
yield executeCmd(`cd ${sanitize(projectPath)}/ && git pull`);
yield executeCmd(`cd ${sanitize(projectPath)}/ && npm ci`);
yield executeCmd(`cd ${sanitize(projectPath)}/ && ${sanitize(command)}`);
yield executeCmd(`cp -r ${sanitize(projectPath)}/dist/ ${sanitize(destPath)}/`);
});
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
142 changes: 142 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import express, {Request, Response} from "express";
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
import {exec} from "child_process";
import crypto from "crypto";
import dotenv from "dotenv";

dotenv.config();

const app = express();
const port = process.env.PORT || '3333';
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved
const secret = process.env.GITHUB_SECRET;
if(!secret) {
console.error("Error: GITHUB_SECRET environment variable is not set.");
process.exit(1);
};
Idrinth marked this conversation as resolved.
Show resolved Hide resolved

let isDocumentationWebsiteUpdated = false;
let isMindmapUpdated = false;
let contributorsBuildRequired = false;

let documentationWebsiteBuildTime = 0;
let mindmapBuildTime = 0;
let contributorsBuildTime = 0;
// fixing TS errors when back
app.use(express.json());

app.post("/webhook", async (req: Request, res: Response) => {
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
console.log("Request received");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sanitize user-provided values before logging.

Logging user-provided values directly can lead to log injection attacks.

- console.log("Request received");
+ console.log("Request received");

Committable suggestion was skipped due to low confidence.

const signature = req.headers["x-hub-signature"] as string;
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved

const payload = JSON.stringify(req.body);
const hmac = crypto.createHmac("sha1", secret);

const calculatedSignature = `sha1=${hmac.update(payload).digest("hex")}`;
console.log("Calculated signature received", calculatedSignature) // need this to stay as number seems to change
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved
Idrinth marked this conversation as resolved.
Show resolved Hide resolved

if (crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(calculatedSignature))) {
const {result, respMessage} = await getBranchStatus(req.body);
console.log("Result: ", result);
res.status(200).send({ result, respMessage });
} else {
res.status(400).send({ error: "Invalid GitHub signature. Ensure the secret is correctly configured." });
}
});

interface BranchStatus {
result: number | string;
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved
respMessage: string ;
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved
};

Idrinth marked this conversation as resolved.
Show resolved Hide resolved
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});

const sanitize = (cmd: string) => {
const sanitized = cmd.replace(/[;&|`<>$(){}[\]]/g, '');

if(/[\r\n\t]/.test(cmd)) {
throw new Error("Invalid characters in command");
}

return sanitized;
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
};

const executeCmd = async (cmd: string) => {
try {
const {stdout, stderr} = await exec(sanitize(cmd));
Fixed Show fixed Hide fixed
return stderr + "\n" + stdout;
} catch (error: any) {
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved
console.error(`exec error: ${error}`);
throw new Error("Command execution failed. Check logs for details.");
};
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
};

const getBranchStatus = async (req: Request): Promise<BranchStatus> => {
console.log("Webhook received successfully");

const branchName = req.body?.ref?.split("/").pop();

if (!branchName) {
return { result: 400, respMessage: "Branch name not found in the request." };
}

if (branchName === process.env.BRANCH_NAME) {
const { status, message } = await buildProject();
return { result: status, respMessage: message };
}

return { result: 200, respMessage: "Build not required." };

};

Idrinth marked this conversation as resolved.
Show resolved Hide resolved
const isUpdateRequired = () => {
const currentTime = Date.now();
const mindMapUpdateInterval = Number.parseInt(process.env.MINDMAP_UPDATE_TIME_INTERVAL ?? "10000");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this never changes during the run time, please only calculate once.

const documentationWebsiteUpdateInterval = Number.parseInt(process.env.DOCUMENTATION_WEBSITE_UPDATE_TIME_INTERVAL ?? "10000");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is static during a run, please only calculate once.

isMindmapUpdated = (currentTime - mindmapBuildTime) / 1000 / 60 > mindMapUpdateInterval;
isDocumentationWebsiteUpdated = (currentTime - documentationWebsiteBuildTime) / 1000 / 60 > documentationWebsiteUpdateInterval;
return isMindmapUpdated || isDocumentationWebsiteUpdated;
};
aceppaluni marked this conversation as resolved.
Show resolved Hide resolved

const buildProject = async (): Promise<{ status: number; message: string }> => {
Idrinth marked this conversation as resolved.
Show resolved Hide resolved
const currentTime = Date.now();
const contributionUpdateTimeInterval = Number.parseInt(process.env.CONTRIBUTORS_UPDATE_TIME_INTERVAL ?? "10000");
if (!process.env.DOCUMENTATION_WEBSITE_PATH) {
console.log('error')
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved
}
if (!isUpdateRequired()) {
if (contributorsBuildRequired || (currentTime - contributorsBuildTime) / 1000 / 60 > contributionUpdateTimeInterval) {
console.log("No update required, updating the contributors only");
await initiateBuild("npm run contributor-build", process.env.DOCUMENTATION_WEBSITE_PATH!, process.env.DOCUMENTATION_WEBSITE_DEST_PATH!);
contributorsBuildTime = currentTime;
contributorsBuildRequired = false;
return { status: 200, message: "Contributors build has been created." };
} else {
contributorsBuildRequired = true;
return { status: 202, message: "Contributors build will be done after the next build." };
}
}
if (isMindmapUpdated) {
console.log("Building Mindmap");
await initiateBuild("npm run build", process.env.MINDMAP_PATH!, process.env.MINDMAP_DEST_PATH!);
mindmapBuildTime = currentTime;
isMindmapUpdated = false;
}

if (isDocumentationWebsiteUpdated) {
console.log("Building Documentation Website");
await initiateBuild("npm run build", process.env.DOCUMENTATION_WEBSITE_PATH!, process.env.DOCUMENTATION_WEBSITE_DEST_PATH!);
documentationWebsiteBuildTime = currentTime;
contributorsBuildTime = currentTime;
isDocumentationWebsiteUpdated = false;
}

return {status: 200, message: "Contributors build will be done after the next build."};
};
Idrinth marked this conversation as resolved.
Show resolved Hide resolved

const initiateBuild = async (command: string, projectPath: string, destPath: string) => {
await executeCmd(`cd ${sanitize(projectPath)}/ && git pull`);
await executeCmd(`cd ${sanitize(projectPath)}/ && npm ci`);
await executeCmd(`cd ${sanitize(projectPath)}/ && ${sanitize(command)}`);
await executeCmd(`cp -r ${sanitize(projectPath)}/dist/ ${sanitize(destPath)}/`);
};
Loading
Loading