From 877f54ad00120fbff0b94b7208b5edf145442674 Mon Sep 17 00:00:00 2001 From: ray-1337 <33544674+ray-1337@users.noreply.github.com> Date: Sat, 3 Jun 2023 00:39:06 +0200 Subject: [PATCH] major refactor to typescript --- index.d.ts | 37 --------- index.js | 1 - src/Index.ts | 27 +++++++ src/Main.js | 31 -------- src/Typings.d.ts | 37 +++++++++ src/Util.js | 105 ------------------------- src/Util.ts | 40 ++++++++++ src/methods/Allow.ts | 74 +++++++++++++++++ src/methods/Delete.ts | 29 +++++++ src/methods/Deny.ts | 76 ++++++++++++++++++ src/methods/{disable.js => Disable.ts} | 10 +-- src/methods/{enable.js => Enable.ts} | 13 +-- src/methods/Logging.ts | 23 ++++++ src/methods/Reload.ts | 22 ++++++ src/methods/Reset.ts | 22 ++++++ src/methods/Status.ts | 45 +++++++++++ src/methods/allow.js | 92 ---------------------- src/methods/delete.js | 30 ------- src/methods/deny.js | 92 ---------------------- src/methods/logging.js | 36 --------- src/methods/reload.js | 24 ------ src/methods/reset.js | 24 ------ src/methods/status.js | 51 ------------ tsconfig.json | 26 ++++++ 24 files changed, 429 insertions(+), 538 deletions(-) delete mode 100644 index.d.ts delete mode 100644 index.js create mode 100644 src/Index.ts delete mode 100644 src/Main.js create mode 100644 src/Typings.d.ts delete mode 100644 src/Util.js create mode 100644 src/Util.ts create mode 100644 src/methods/Allow.ts create mode 100644 src/methods/Delete.ts create mode 100644 src/methods/Deny.ts rename src/methods/{disable.js => Disable.ts} (59%) rename src/methods/{enable.js => Enable.ts} (52%) create mode 100644 src/methods/Logging.ts create mode 100644 src/methods/Reload.ts create mode 100644 src/methods/Reset.ts create mode 100644 src/methods/Status.ts delete mode 100644 src/methods/allow.js delete mode 100644 src/methods/delete.js delete mode 100644 src/methods/deny.js delete mode 100644 src/methods/logging.js delete mode 100644 src/methods/reload.js delete mode 100644 src/methods/reset.js delete mode 100644 src/methods/status.js create mode 100644 tsconfig.json diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 229d56d..0000000 --- a/index.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -declare const nodeUfw: { - name: string; - - version: string; - - disable: () => Promise; - - enable: () => Promise; - - reset: () => Promise; - - reload: () => Promise; - - status: (raw?: boolean) => Promise>; - - logging: (type: LoggingType) => Promise; - - allow: { - port: (port: number, protocol?: "udp" | "tcp") => Promise; - address: (address: string, port?: number, protocol?: "udp" | "tcp") => Promise; - }; - - deny: { - port: (port: number, protocol?: "udp" | "tcp") => Promise; - address: (address: string, port?: number, protocol?: "udp" | "tcp") => Promise; - }; -}; - -type LoggingType = 'off' | 'on' | 'low' | 'medium' | 'high' | 'full'; - -interface ParsedStatus { - to: string; - action: string; - from: string; -} - -export = nodeUfw; \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index b2f2a5d..0000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("./src/Main.js"); \ No newline at end of file diff --git a/src/Index.ts b/src/Index.ts new file mode 100644 index 0000000..b4f3389 --- /dev/null +++ b/src/Index.ts @@ -0,0 +1,27 @@ +import { checkSudo, checkNodeVersion, checkPlatform, checkPlatformExact } from './Util'; + +if (!checkSudo()) { + throw new Error("You need to be root to run this package."); +}; + +if (!checkNodeVersion()) { + throw new Error(`The Node version must be at least v14 or above.`); +}; + +if (!checkPlatform()) { + throw new Error("Your platform must be at least `linux`."); +}; + +if (!checkPlatformExact()) { + throw new Error("node-ufw only supported on Ubuntu."); +}; + +export { default as allow } from "./methods/Allow"; +export { default as deny } from "./methods/Deny"; +export { default as disable } from "./methods/Disable"; +export { default as enable } from "./methods/Enable"; +export { default as status } from "./methods/Status"; +export { default as delete } from "./methods/Delete"; +export { default as reset } from "./methods/Reset"; +export { default as reload } from "./methods/Reload"; +export { default as logging } from "./methods/Logging" \ No newline at end of file diff --git a/src/Main.js b/src/Main.js deleted file mode 100644 index 1c2a05e..0000000 --- a/src/Main.js +++ /dev/null @@ -1,31 +0,0 @@ -const {checkSudo, checkNodeVersion, checkPlatform, checkPlatformExact} = require('./Util'); - -if (!checkSudo()) { - throw new Error("You need to be root to run this package."); -}; - -if (!checkNodeVersion()) { - throw new Error(`The Node version must be at least v14 or above.`); -}; - -if (!checkPlatform()) { - throw new Error("Your platform must be at least `linux`."); -}; - -if (!checkPlatformExact()) { - throw new Error("node-ufw only supported on Ubuntu."); -}; - -module.exports = { - name: require("../package.json").name, - version: require("../package.json").version, - allow: require("./methods/allow"), - deny: require("./methods/deny"), - disable: require("./methods/disable"), - enable: require("./methods/enable"), - status: require("./methods/status"), - delete: require("./methods/delete"), - reset: require("./methods/reset"), - reload: require("./methods/reload"), - logging: require("./methods/logging") -}; \ No newline at end of file diff --git a/src/Typings.d.ts b/src/Typings.d.ts new file mode 100644 index 0000000..599d710 --- /dev/null +++ b/src/Typings.d.ts @@ -0,0 +1,37 @@ +interface ParsedStatus { + to: string; + action: string; + from: string; +} + +type LoggingType = 'off' | 'on' | 'low' | 'medium' | 'high' | 'full'; + +type PortProtocol = "udp" | "tcp"; + +export declare const nodeUfw: { + name: string; + + version: string; + + disable: () => Promise; + + enable: () => Promise; + + reset: () => Promise; + + reload: () => Promise; + + status: (raw?: boolean) => Promise; + + logging: (type: LoggingType) => Promise; + + allow: { + port: (port: number, protocol?: PortProtocol) => Promise; + address: (address: string, port?: number, protocol?: PortProtocol) => Promise; + }; + + deny: { + port: (port: number, protocol?: PortProtocol) => Promise; + address: (address: string, port?: number, protocol?: PortProtocol) => Promise; + }; +}; \ No newline at end of file diff --git a/src/Util.js b/src/Util.js deleted file mode 100644 index 1fac861..0000000 --- a/src/Util.js +++ /dev/null @@ -1,105 +0,0 @@ -const {execSync} = require("child_process"); -// const {promisify} = require("util"); -// const promisifiedExec = promisify(exec); -const {getuid, versions, platform} = require("process"); - -module.exports.checkSudo = function () { - return getuid && getuid() == 0 ? true : false; -}; - -module.exports.checkNodeVersion = function () { - const currentApropriateVersion = 14, nodeVersion = versions.node.split('.'); - - return +nodeVersion[0] > currentApropriateVersion ? true : false; -}; - -module.exports.checkPlatform = function () { - // https://nodejs.org/api/process.html#process_process_platform - return platform == "linux" ? true : false; -}; - -// module.exports.getDistroInfo = async function () { -// let current = { -// distributorID: null, distributorVersion: null -// }; - -// try { -// // check version -// let {stdout, stderr} = await promisifiedExec('lsb_release -i -r'); - -// if (stderr) { -// throw new Error(`Error while checking Linux distribution information: ${stderr}`); -// }; - -// if (stdout) { -// // sanitize -// let parsed = stdout -// .split(/\r|\n/gi) // remove break lines -// .map(x => x.replace(/\s/gi, "")) // remove spaces, only remains [e.g. DistributorID:Ubuntu] -// .filter(x => x); // filter empty string - -// let findDistributorID = parsed.find(val => val.match(/^(DistributorID)/gi)); -// let findReleaseVersion = parsed.find(val => val.match(/^(Release)/gi)); - -// if (findDistributorID) { -// let splitViaColon = findDistributorID.split(/(:)/gi); -// if (splitViaColon) current.distributorID = splitViaColon.pop().toLowerCase(); -// }; - -// if (findReleaseVersion) { -// let splitViaColon = findReleaseVersion.split(/(:)/gi); -// if (splitViaColon) current.distributorVersion = splitViaColon.pop().toLowerCase(); -// }; -// }; -// } catch {}; - -// return current; -// }; - -module.exports.checkPlatformExact = function () { - // let distroInfo = await this.getDistroInfo(); - // if (!distroInfo?.distributorID || !distroInfo?.distributorVersion) { - // throw new Error("Missing distro platform information."); - // }; - - // // https://en.wikipedia.org/wiki/Uncomplicated_Firewall - // let platform = distroInfo.distributorID; - // if (String(platform).toLowerCase() !== "ubuntu") { - // throw new Error("node-ufw only supported on Ubuntu."); - // }; - - // return true; - - try { - const check = execSync("cat /etc/*release | grep -E ^NAME"); - return check?.toString("utf-8").match("Ubuntu") ? true : false; - } catch (e) { - console.error(e); - return false; - }; -}; - -module.exports.checkAppropriatePort = function(port) { - const portLimit = Math.round(2 ** 16 - 1); - - if (typeof port !== "number") throw new Error("The port must be type of number."); - - return (port > 0 || port <= portLimit) ? true : false; -}; - -module.exports.checkAppropriateIP = function(address) { - if (typeof address !== "string") throw new Error("The address must be type of string."); - - // https://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ - // also support subnet/net mask - let regex = new RegExp(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(3[0-2]|[1-2][0-9]|[0-9]))?$/gi); - return address.match(regex) !== null ? true : false; -}; - -// soon to be continued -// module.exports.checkPlatformVersion = function () { -// let distroInfo = this.getDistroInfo(); -// if (!distroInfo?.distributorID || !distroInfo?.distributorVersion) { -// throw new Error("Missing distro platform information."); -// }; -// }; \ No newline at end of file diff --git a/src/Util.ts b/src/Util.ts new file mode 100644 index 0000000..4485400 --- /dev/null +++ b/src/Util.ts @@ -0,0 +1,40 @@ +import { execSync } from "node:child_process"; +import { getuid, versions, platform } from "node:process"; + +export function checkSudo() { + return getuid && getuid() == 0 ? true : false; +}; + +export function checkNodeVersion() { + const currentApropriateVersion = 14, nodeVersion = versions.node.split('.'); + + return +nodeVersion[0] > currentApropriateVersion ? true : false; +}; + +export function checkPlatform() { + // https://nodejs.org/api/process.html#process_process_platform + return platform == "linux" ? true : false; +}; + +export function checkPlatformExact() { + try { + const check = execSync("cat /etc/*release | grep -E ^NAME"); + return check?.toString("utf-8").match("Ubuntu") ? true : false; + } catch (e) { + console.error(e); + return false; + }; +}; + +export function checkAppropriatePort(port: number) { + const portLimit = Math.round(2 ** 16 - 1); + + return port > 0 || port <= portLimit; +}; + +export function checkAppropriateIP(address: string) { + // https://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ + + let regex = new RegExp(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(3[0-2]|[1-2][0-9]|[0-9]))?$/gi); + return address.match(regex) !== null ? true : false; +}; \ No newline at end of file diff --git a/src/methods/Allow.ts b/src/methods/Allow.ts new file mode 100644 index 0000000..22514f0 --- /dev/null +++ b/src/methods/Allow.ts @@ -0,0 +1,74 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +import { checkAppropriatePort, checkAppropriateIP } from "../Util"; +import type { PortProtocol } from "../Typings"; + +const promisifiedExec = promisify(exec); + +/** + * Allow incoming requests through specific port. (root/sudo access is mandatory) +*/ +async function port(port: number, protocol?: PortProtocol) { + try { + // check port range + let checkPort = checkAppropriatePort(port); + if (!checkPort) return false; + + let res = await promisifiedExec(`echo "y" | sudo ufw allow ${port}${protocol ? `/${protocol}` : ""}`); + if (res.stderr) throw new Error(res.stderr); + + if (res.stdout) { + // will find a better way to parse this + if (res.stdout.toLowerCase().match(/(added)/gi)) { + return true; + } else { + console.log(res.stdout); + return false; + }; + } else { + console.log(res.stdout); + return false; + }; + } catch (err) { + throw err; + }; +} + +/** + * Allow incoming requests through specific (IP) address. (root/sudo access is mandatory) +*/ +async function address(address: string, port: number, protocol?: PortProtocol) { + try { + // address validation + let checkAddress = checkAppropriateIP(address); + if (!checkAddress) return false; + + // port validation + if (port) { + let checkPort = checkAppropriatePort(port); + if (!checkPort) return false; + }; + + let res = await promisifiedExec(`echo "y" | sudo ufw allow from ${address} ${port ? `to any port ${port}` : ""} ${protocol ? `proto ${protocol}` : ""}`); + if (res.stderr) throw new Error(res.stderr); + + if (res.stdout) { + // will find a better way to parse this + if (res.stdout.toLowerCase().match(/(added)/gi)) { + return true; + } else { + console.log(res.stdout); + return false; + }; + } else { + console.log(res.stdout); + return false; + }; + } catch (err) { + throw err; + }; +} + +export default { + address, port +}; \ No newline at end of file diff --git a/src/methods/Delete.ts b/src/methods/Delete.ts new file mode 100644 index 0000000..51ab4d2 --- /dev/null +++ b/src/methods/Delete.ts @@ -0,0 +1,29 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +const promisifiedExec = promisify(exec); + +/** + * Delete ufw rule(s). (root/sudo access is mandatory) +*/ +export default async function(num: number) { + try { + if (num === 0) { + num = 1; + }; + + let res = await promisifiedExec(`echo "y" | sudo ufw delete ${num}`); + + if (res.stderr) { + throw new Error(res.stderr); + }; + + if (res.stdout) { + return true; + } else { + console.log(res.stdout); + return false; + }; + } catch (err) { + throw err; + }; +}; \ No newline at end of file diff --git a/src/methods/Deny.ts b/src/methods/Deny.ts new file mode 100644 index 0000000..0bc812a --- /dev/null +++ b/src/methods/Deny.ts @@ -0,0 +1,76 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +import { checkAppropriatePort, checkAppropriateIP } from "../Util"; +import type { PortProtocol } from "../Typings"; + +const promisifiedExec = promisify(exec); + +/** + * Deny incoming requests through specific port. (root/sudo access is mandatory) +*/ +async function port(port: number, protocol?: PortProtocol) { + try { + // check port range + let checkPort = checkAppropriatePort(port); + if (!checkPort) return false; + + let res = await promisifiedExec(`echo "y" | sudo ufw deny ${port}${protocol ? `/${protocol}` : ""}`); + if (res.stderr) { + throw new Error(res.stderr); + }; + + if (res.stdout) { + // will find a better way to parse this + if (res.stdout.toLowerCase().match(/(added)/gi)) { + return true; + } else { + console.log(res.stdout); + return false; + }; + } else { + console.log(res.stdout); + return false; + }; + } catch (err) { + throw err; + }; +}; + +/** + * Deny incoming requests through specific (IP) address. (root/sudo access is mandatory) +*/ +async function address(address: string, port: number, protocol?: PortProtocol) { + try { + // address validation + let checkAddress = checkAppropriateIP(address); + if (!checkAddress) return false; + + // port validation + if (port) { + let checkPort = checkAppropriatePort(port); + if (!checkPort) return false; + }; + + let res = await promisifiedExec(`echo "y" | sudo ufw deny from ${address} ${port ? `to any port ${port}` : ""} ${protocol ? `proto ${protocol}` : ""}`); + if (res.stderr) throw new Error(res.stderr); + + if (res.stdout) { + // will find a better way to parse this + if (res.stdout.toLowerCase().match(/(added)/gi)) { + return true; + } else { + console.log(res.stdout); + return false; + }; + } else { + console.log(res.stdout); + return false; + }; + } catch (err) { + throw err; + }; +}; + +export default { + address, port +}; \ No newline at end of file diff --git a/src/methods/disable.js b/src/methods/Disable.ts similarity index 59% rename from src/methods/disable.js rename to src/methods/Disable.ts index 59e714d..b192dcd 100644 --- a/src/methods/disable.js +++ b/src/methods/Disable.ts @@ -1,13 +1,11 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); +import { exec } from "node:child_process"; +import { promisify } from "node:util"; const promisifiedExec = promisify(exec); -const util = require("../Util"); /** * Disable ufw. (root/sudo access is mandatory) - * @returns {Promise} Returns a boolean. */ -module.exports = async function() { +export default async function() { try { let res = await promisifiedExec(`echo "y" | sudo ufw disable`); @@ -19,6 +17,6 @@ module.exports = async function() { return false; }; } catch (err) { - throw new Error(err); + throw err; }; }; \ No newline at end of file diff --git a/src/methods/enable.js b/src/methods/Enable.ts similarity index 52% rename from src/methods/enable.js rename to src/methods/Enable.ts index 81c3ca5..208e16a 100644 --- a/src/methods/enable.js +++ b/src/methods/Enable.ts @@ -1,13 +1,8 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); +import { exec } from "node:child_process"; +import { promisify } from "node:util"; const promisifiedExec = promisify(exec); -const util = require("../Util"); -/** - * Enable ufw. (root/sudo access is mandatory) - * @returns {Promise} Returns a boolean. -*/ -module.exports = async function() { +export default async function() { try { // https://serverfault.com/a/790150 let res = await promisifiedExec(`echo "y" | sudo ufw enable`); @@ -20,6 +15,6 @@ module.exports = async function() { return false; }; } catch (err) { - throw new Error(err); + throw err; }; }; \ No newline at end of file diff --git a/src/methods/Logging.ts b/src/methods/Logging.ts new file mode 100644 index 0000000..3a9e178 --- /dev/null +++ b/src/methods/Logging.ts @@ -0,0 +1,23 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +import type { LoggingType } from "../Typings"; +const promisifiedExec = promisify(exec); + +/** + * Set/toggle UFW logging. (root/sudo access is mandatory) +*/ +export default async function(type: LoggingType) { + try { + let res = await promisifiedExec(`sudo ufw logging ${type}`); + + if (res.stderr) throw new Error(res.stderr); + + if (res.stdout) { + return true; + } else { + return false; + }; + } catch (err) { + throw err; + }; +}; \ No newline at end of file diff --git a/src/methods/Reload.ts b/src/methods/Reload.ts new file mode 100644 index 0000000..4f7a7db --- /dev/null +++ b/src/methods/Reload.ts @@ -0,0 +1,22 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +const promisifiedExec = promisify(exec); + +/** + * Reloads firewall. (root/sudo access is mandatory) +*/ +export default async function() { + try { + let res = await promisifiedExec("sudo ufw reload"); + + if (res.stderr) throw new Error(res.stderr); + + if (res.stdout) { + return true; + } else { + return false; + }; + } catch (err) { + throw err; + }; +}; \ No newline at end of file diff --git a/src/methods/Reset.ts b/src/methods/Reset.ts new file mode 100644 index 0000000..fa7cf20 --- /dev/null +++ b/src/methods/Reset.ts @@ -0,0 +1,22 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +const promisifiedExec = promisify(exec); + +/** + * Disables and resets firewall to installation defaults. No prompt. Use this wisely. (root/sudo access is mandatory)= +*/ +export default async function() { + try { + let res = await promisifiedExec("sudo ufw --force reset"); + + if (res.stderr) throw new Error(res.stderr); + + if (res.stdout) { + return true; + } else { + return false; + }; + } catch (err) { + throw err; + }; +}; \ No newline at end of file diff --git a/src/methods/Status.ts b/src/methods/Status.ts new file mode 100644 index 0000000..a8eb1ea --- /dev/null +++ b/src/methods/Status.ts @@ -0,0 +1,45 @@ +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +import type { ParsedStatus } from "../Typings"; +const promisifiedExec = promisify(exec); + +/** + * List of currently activated ufw. (root/sudo access is mandatory) +*/ +export default async function(raw?: boolean): Promise { + try { + let res = await promisifiedExec("sudo ufw status"); + + if (res.stderr) throw new Error(res.stderr); + + if (res.stdout) { + if (raw) return res.stdout; + + let list = []; + + // parsing + let parsedStatus = res.stdout.replace(/\r|\n/gi, " ").split(/\s{2,}/gi).filter(x => x.length); + if (!parsedStatus.length) return []; + + let findAfterFrom = parsedStatus.findIndex(x => x == "----"); + let afterSlice = parsedStatus.slice(findAfterFrom + 1, parsedStatus.length); + if (!afterSlice.length) return []; + + for (let i = 0; i < afterSlice.length; i++) { + if (i % 3 == 0) { + let to = afterSlice[i]; + let action = afterSlice[i + 1]; + let from = afterSlice[i + 2]; + + list.push({to, action, from}); + }; + }; + + return list; + }; + + return null; + } catch (err) { + throw err; + }; +}; \ No newline at end of file diff --git a/src/methods/allow.js b/src/methods/allow.js deleted file mode 100644 index 403ba6e..0000000 --- a/src/methods/allow.js +++ /dev/null @@ -1,92 +0,0 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); -const promisifiedExec = promisify(exec); -const util = require("../Util"); - -/** - * Allow incoming requests through specific port. (root/sudo access is mandatory) - * @param {number} port The connection pange. From range 1 - 65535. - * @param {"udp" | "tcp" | undefined} protocol The protocol. - * @returns {Promise} Returns a boolean. -*/ -module.exports.port = async function (port, protocol) { - try { - if (!port) throw new Error("Missing port input."); - if (typeof port !== "number") throw new Error("The port must be type of number."); - - // check port range - let checkPort = util.checkAppropriatePort(port); - if (!checkPort) return false; - - if (protocol) { - if (typeof protocol !== "string") throw new Error("The protocol must be type of string."); - if (protocol !== "tcp" || protocol !== "udp") throw new Error('The protocol must be either "tcp" or "udp"'); - }; - - let res = await promisifiedExec(`echo "y" | sudo ufw allow ${port}${protocol ? `/${protocol}` : ""}`); - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - // will find a better way to parse this - if (res.stdout.toLowerCase().match(/(added)/gi)) { - return true; - } else { - console.log(res.stdout); - return false; - }; - } else { - console.log(res.stdout); - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; - -/** - * Allow incoming requests through specific (IP) address. (root/sudo access is mandatory) - * @param {string} address IP address, supported by subnet/net mask. From range 0.0.0.0 to 255.255.255.255 - * @param {number | undefined} port The connection pange. From range 1 - 65535. - * @param {"udp" | "tcp" | undefined} protocol The protocol. - * @returns {Promise} Returns a boolean. -*/ -module.exports.address = async function (address, port, protocol) { - try { - // address validation - if (!address) throw new Error("Missing address input."); - if (typeof address !== "string") throw new Error("The address must be type of string."); - let checkAddress = util.checkAppropriateIP(address); - if (!checkAddress) return false; - - // port validation - if (port) { - if (typeof port !== "number") throw new Error("The port must be type of number."); - let checkPort = util.checkAppropriatePort(port); - if (!checkPort) return false; - }; - - // protocol (tcp/udp) validation - if (protocol) { - if (typeof protocol !== "string") throw new Error("The protocol must be type of string."); - if (protocol !== "tcp" || protocol !== "udp") throw new Error('The protocol must be either "tcp" or "udp"'); - }; - - let res = await promisifiedExec(`echo "y" | sudo ufw allow from ${address} ${port ? `to any port ${port}` : ""} ${protocol ? `proto ${protocol}` : ""}`); - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - // will find a better way to parse this - if (res.stdout.toLowerCase().match(/(added)/gi)) { - return true; - } else { - console.log(res.stdout); - return false; - }; - } else { - console.log(res.stdout); - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; \ No newline at end of file diff --git a/src/methods/delete.js b/src/methods/delete.js deleted file mode 100644 index 7dfa06e..0000000 --- a/src/methods/delete.js +++ /dev/null @@ -1,30 +0,0 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); -const promisifiedExec = promisify(exec); -const util = require("../Util"); - -/** - * Delete ufw rule(s). (root/sudo access is mandatory) - * @param {number} num Number of the rules list. The first rule starts from number 1. - * @returns {Promise} Returns a boolean. -*/ -module.exports = async function(num) { - try { - if (!num) throw new Error("Missing num input."); - if (typeof num !== "number") throw new Error("The num must be type of number."); - if (num === 0) num = 1; - - let res = await promisifiedExec(`echo "y" | sudo ufw delete ${num}`); - - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - return true; - } else { - console.log(res.stdout); - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; \ No newline at end of file diff --git a/src/methods/deny.js b/src/methods/deny.js deleted file mode 100644 index 3cee983..0000000 --- a/src/methods/deny.js +++ /dev/null @@ -1,92 +0,0 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); -const promisifiedExec = promisify(exec); -const util = require("../Util"); - -/** - * Deny incoming requests through specific port. (root/sudo access is mandatory) - * @param {number} port The connection pange. From range 1 - 65535. - * @param {"udp" | "tcp" | undefined} protocol The protocol. - * @returns {Promise} Returns a boolean. -*/ -module.exports.port = async function (port, protocol) { - try { - if (!port) throw new Error("Missing port input."); - if (typeof port !== "number") throw new Error("The port must be type of number."); - - // check port range - let checkPort = util.checkAppropriatePort(port); - if (!checkPort) return false; - - if (protocol) { - if (typeof protocol !== "string") throw new Error("The protocol must be type of string."); - if (protocol !== "tcp" || protocol !== "udp") throw new Error('The protocol must be either "tcp" or "udp"'); - }; - - let res = await promisifiedExec(`echo "y" | sudo ufw deny ${port}${protocol ? `/${protocol}` : ""}`); - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - // will find a better way to parse this - if (res.stdout.toLowerCase().match(/(added)/gi)) { - return true; - } else { - console.log(res.stdout); - return false; - }; - } else { - console.log(res.stdout); - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; - -/** - * Deny incoming requests through specific (IP) address. (root/sudo access is mandatory) - * @param {string} address IP address, supported by subnet/net mask. From range 0.0.0.0 to 255.255.255.255 - * @param {number | undefined} port The connection pange. From range 1 - 65535. - * @param {"udp" | "tcp" | undefined} protocol The protocol. - * @returns {Promise} Returns a boolean. -*/ -module.exports.address = async function (address, port, protocol) { - try { - // address validation - if (!address) throw new Error("Missing address input."); - if (typeof address !== "string") throw new Error("The address must be type of string."); - let checkAddress = util.checkAppropriateIP(address); - if (!checkAddress) return false; - - // port validation - if (port) { - if (typeof port !== "number") throw new Error("The port must be type of number."); - let checkPort = util.checkAppropriatePort(port); - if (!checkPort) return false; - }; - - // protocol (tcp/udp) validation - if (protocol) { - if (typeof protocol !== "string") throw new Error("The protocol must be type of string."); - if (protocol !== "tcp" || protocol !== "udp") throw new Error('The protocol must be either "tcp" or "udp"'); - }; - - let res = await promisifiedExec(`echo "y" | sudo ufw deny from ${address} ${port ? `to any port ${port}` : ""} ${protocol ? `proto ${protocol}` : ""}`); - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - // will find a better way to parse this - if (res.stdout.toLowerCase().match(/(added)/gi)) { - return true; - } else { - console.log(res.stdout); - return false; - }; - } else { - console.log(res.stdout); - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; \ No newline at end of file diff --git a/src/methods/logging.js b/src/methods/logging.js deleted file mode 100644 index 3122dc4..0000000 --- a/src/methods/logging.js +++ /dev/null @@ -1,36 +0,0 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); -const promisifiedExec = promisify(exec); -const util = require("../Util"); - -/** - * Set/toggle UFW logging. (root/sudo access is mandatory) - * @param {'off' | 'on' | 'low' | 'medium' | 'high' | 'full'} type A type of UFWlogging. - * @returns {Promise} Returns a boolean. -*/ -module.exports = async function(type) { - try { - if (!type) throw new Error("Missing type input."); - if (typeof type !== "string") throw new Error("The type must be type of string."); - - type = type.toLowerCase(); - - // https://manpages.ubuntu.com/manpages/focal/en/man8/ufw.8.html#logging - let appropriateType = ['off', 'on', 'low', 'medium', 'high', 'full']; - if (!appropriateType.includes(type)) { - throw new Error(`Invalid type. Currently supported: ${appropriateType.join(", ")}`); - }; - - let res = await promisifiedExec(`sudo ufw logging ${type}`); - - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - return true; - } else { - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; \ No newline at end of file diff --git a/src/methods/reload.js b/src/methods/reload.js deleted file mode 100644 index 189b81d..0000000 --- a/src/methods/reload.js +++ /dev/null @@ -1,24 +0,0 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); -const promisifiedExec = promisify(exec); -const util = require("../Util"); - -/** - * Reloads firewall. (root/sudo access is mandatory) - * @returns {Promise} Returns a boolean. -*/ -module.exports = async function() { - try { - let res = await promisifiedExec(`sudo ufw reload`); - - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - return true; - } else { - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; \ No newline at end of file diff --git a/src/methods/reset.js b/src/methods/reset.js deleted file mode 100644 index 14ade7e..0000000 --- a/src/methods/reset.js +++ /dev/null @@ -1,24 +0,0 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); -const promisifiedExec = promisify(exec); -const util = require("../Util"); - -/** - * Disables and resets firewall to installation defaults. No prompt. Use this wisely. (root/sudo access is mandatory) - * @returns {Promise} Returns a boolean. -*/ -module.exports = async function() { - try { - let res = await promisifiedExec(`sudo ufw --force reset`); - - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - return true; - } else { - return false; - }; - } catch (err) { - throw new Error(err); - }; -}; \ No newline at end of file diff --git a/src/methods/status.js b/src/methods/status.js deleted file mode 100644 index 3debe53..0000000 --- a/src/methods/status.js +++ /dev/null @@ -1,51 +0,0 @@ -const {exec} = require("child_process"); -const {promisify} = require("util"); -const promisifiedExec = promisify(exec); -const util = require("../Util"); - -/** - * List of currently activated ufw. (root/sudo access is mandatory) - * @param {boolean} [raw=false] A raw version of "ufw status" - * @returns {Promise} Returns a string if "raw" param is included, otherwise a list of array with to/action/from. -*/ -module.exports = async function(raw) { - if (raw && typeof raw !== "boolean") { - throw new Error("The raw must be type of boolean."); - }; - - try { - let res = await promisifiedExec(`sudo ufw status`); - - if (res.stderr) throw new Error(res.stderr); - - if (res.stdout) { - if (!raw) { - let list = []; - - // parsing - let parsedStatus = res.stdout.replace(/\r|\n/gi, " ").split(/\s{2,}/gi).filter(x => x.length); - if (!parsedStatus.length) return []; - - let findAfterFrom = parsedStatus.findIndex(x => x == "----"); - let afterSlice = parsedStatus.slice(findAfterFrom + 1, parsedStatus.length); - if (!afterSlice.length) return []; - - for (let i = 0; i < afterSlice.length; i++) { - if (i % 3 == 0) { - let to = afterSlice[i]; - let action = afterSlice[i + 1]; - let from = afterSlice[i + 2]; - - list.push({to, action, from}); - }; - }; - - return list; - } else { - return res.stdout; - }; - }; - } catch (err) { - throw new Error(err); - }; -}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..80aa935 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "outDir": "dist", + "module": "CommonJS", + "target": "ESNext", + "inlineSourceMap": true, + "importHelpers": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": false, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "allowUnusedLabels": false, + "allowUnreachableCode": false, + "strictPropertyInitialization": true, + "strictBindCallApply": true, + "declaration": true, + "noEmitOnError": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts"], + "exclude": ["node_modules", "dist", "test"] +} \ No newline at end of file