remove virus

This commit is contained in:
nin0dev 2025-03-08 23:24:08 -05:00
parent ec4e8ff39a
commit 39750cb2b5
Signed by: nin0
SSH key fingerprint: SHA256:NOoDnFVvZNFvqfXCIhzr6oCTDImZAbTTuyAysZ8Ufk8
6 changed files with 253 additions and 318 deletions

6
.prettierrc.json Normal file
View file

@ -0,0 +1,6 @@
{
"useTabs": false,
"tabWidth": 4,
"semi": true,
"singleQuote": false
}

View file

@ -24,10 +24,21 @@ export default function UserpluginInstallButton({ props }: any) {
}}
onClick={async () => {
try {
await Native.initPluginInstall(gitLink[0], gitLink[1], gitLink[2], gitLink[3]);
const pluginToEnable = await Native.initPluginInstall(gitLink[0], gitLink[1], gitLink[2], gitLink[3]);
Alerts.show({
title: "Done!",
body: `${pluginToEnable} has been successfully installed. What now?`,
confirmText: "Enable & refresh",
cancelText: "Refresh",
secondaryConfirmText: "I'll refresh later",
onConfirm() {
!Vencord.Plugins.plugins[pluginToEnable] ? Vencord.Settings.plugins[pluginToEnable] = { enabled: true } : Vencord.Settings.plugins[pluginToEnable].enabled = true;
window.location.reload();
},
onCancel: window.location.reload
});
}
catch (e) {
catch (e: any) {
if (e.toString().includes("silentStop")) return;
Alerts.show({
title: "Install error",

View file

@ -1,80 +0,0 @@
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { PluginCard } from "@components/PluginSettings";
import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal, closeModal } from "@utils/modal";
import { Alerts, Button, Card, Parser, Text, Toasts } from "@webpack/common";
import { Native } from ".";
const cl = classNameFactory("vc-userplugininstaller-");
export function showInstallModal(meta: any, pluginPath: string) {
console.log(meta);
Alerts.show({
title: <>
<div className={cl("title")}>
Review userplugin
</div>
</>,
body: <>
<div className={cl("body")}>
<Text className={cl("subheader")}>
Plugin info
</Text>
<Card className={cl(["card", "lead-card"])}>
<Text>
<Text className={cl("name")}>{meta.name}</Text>
<Text className={cl("desc")}>{meta.description}</Text>
</Text>
</Card>
<Text className={cl("subheader")}>
Notes & warnings
</Text>
{
meta.usesPreSend && <Card className={cl(["card", "warning-card"])}>
<Text>
<Text className={cl("name")}>Has pre-send listeners</Text>
<Text className={cl("desc")}>This plugin is able to intercept and edit messages before sending them.</Text>
</Text>
</Card>
}
<Card className={cl(["card"])}>
<Text>
<Text className={cl("name")}>Refresh and enable required</Text>
<Text className={cl("desc")}>After the install is completed, Discord will be reloaded and you will need to enable the plugin yourself.</Text>
</Text>
</Card>
<Text style={{ color: "var(--text-danger) !important", margin: "3px 0" }}>Make sure that the plugin author is trustworthy before installing {meta.name}.</Text>
</div>
</>,
cancelText: "Cancel install",
confirmText: `Install plugin`,
onCancel() {
Native.deleteFolder(pluginPath);
},
onCloseCallback() {
setTimeout(() => {
Native.deleteFolder(pluginPath);
}, 20000);
},
async onConfirm() {
Toasts.pop();
Toasts.show({
message: "Rebuilding Vencord, please wait...",
id: Toasts.genId(),
type: Toasts.Type.MESSAGE
});
try {
await Native.build(pluginPath);
window.location.reload();
}
catch {
Toasts.pop();
return Toasts.show({
message: "Something bad has happened while building Vencord.",
id: Toasts.genId(),
type: Toasts.Type.FAILURE
});
}
},
});
}

View file

@ -8,7 +8,6 @@ import "./style.css";
import { Devs } from "@utils/constants";
import definePlugin, { PluginNative } from "@utils/types";
import { Alerts, TextInput } from "@webpack/common";
import UserpluginInstallButton from "./UserpluginInstallButton";
@ -23,27 +22,7 @@ export default definePlugin({
name: "UserpluginInstaller",
description: "Install userplugins with a simple button click",
authors: [Devs.nin0dev],
renderMessageAccessory: (props) => {
renderMessageAccessory: props => {
return <UserpluginInstallButton props={props} />;
},
toolboxActions: {
"Install Plugin": () => {
let gitUrl = "";
Alerts.show({
title: "Install plugin",
body: <>
<TextInput onChange={v => { gitUrl = v; }} placeholder="Git link (https://github.com/...)" />
</>,
confirmText: "Install",
async onConfirm() {
const gitLink = gitUrl.match(CLONE_LINK_REGEX)!;
await Native.initPluginInstall(gitLink[0], gitLink[1], gitLink[2], gitLink[3]);
window.location.reload();
}
});
},
"Uninstall Plugin": () => {
}
}
});

287
native.ts
View file

@ -10,88 +10,88 @@ import { existsSync, readdirSync, readFileSync } from "fs";
import { rm } from "fs/promises";
import { join } from "path";
// @ts-ignore fuck off
import pluginValidateContent from "./pluginValidate.txt"; // i would use HTML but esbuild is being whiny
const PLUGIN_META_REGEX = /export default definePlugin\((?:\s|\/(?:\/|\*).*)*{\s*(?:\s|\/(?:\/|\*).*)*name:\s*(?:"|'|`)(.*)(?:"|'|`)(?:\s|\/(?:\/|\*).*)*,(?:\s|\/(?:\/|\*).*)*(?:\s|\/(?:\/|\*).*)*description:\s*(?:"|'|`)(.*)(?:"|'|`)(?:\s|\/(?:\/|\*).*)*/;
const CLONE_LINK_REGEX = /https:\/\/((?:git(?:hub|lab)\.com|git\.(?:[a-zA-Z0-9]|\.)+|codeberg\.org))\/(?!user-attachments)((?:[a-zA-Z0-9]|-)+)\/((?:[a-zA-Z0-9]|-)+)(?:\.git)?(?:\/)?/;
export async function initPluginInstall(_, link: string, source: string, owner: string, repo: string) {
const verifiedRegex = link.match(CLONE_LINK_REGEX)!;
if (verifiedRegex.length !== 4 || verifiedRegex[0] !== link || verifiedRegex[1] !== source || verifiedRegex[2] !== owner || verifiedRegex[3] !== repo) return;
export async function initPluginInstall(_, link: string, source: string, owner: string, repo: string): Promise<string> {
// eslint-disable-next-line
return new Promise(async (resolve, reject) => {
const verifiedRegex = link.match(CLONE_LINK_REGEX)!;
if (verifiedRegex.length !== 4 || verifiedRegex[0] !== link || verifiedRegex[1] !== source || verifiedRegex[2] !== owner || verifiedRegex[3] !== repo) return reject("Invalid link");
// Ask for clone
const cloneDialog = await dialog.showMessageBox({
title: "Clone userplugin",
message: `You are about to clone a userplugin from ${source}.`,
type: "question",
detail: `The repository name is "${repo}" and it is owned by "${owner}".\nThe repository URL is ${link}\n\n(If you did not request this intentionally, choose Cancel)`,
buttons: ["Cancel", "Clone repository and continue install", "Open repository in browser"]
});
switch (cloneDialog.response) {
case 0: {
throw "Rejected by user";
}
case 1: {
await cloneRepo(link, repo);
break;
}
case 2: {
await shell.openExternal(link);
throw "silentStop";
}
}
// Get plugin meta
const meta = await getPluginMeta(join(__dirname, "..", "src", "userplugins", repo));
// Review plugin
const win = new BrowserWindow({
maximizable: false,
minimizable: false,
width: 560,
height: meta.usesNative || meta.usesPreSend ? 650 : 360,
resizable: false,
webPreferences: {
devTools: true
},
title: "Review userplugin",
modal: true,
parent: BrowserWindow.getAllWindows()[0],
show: false,
autoHideMenuBar: true
});
const reView /* haha got it */ = new BrowserView({
webPreferences: {
devTools: true,
nodeIntegration: true
}
});
win.setBrowserView(reView);
win.addBrowserView(reView);
win.setTopBrowserView(reView);
win.loadURL(generateReviewPluginContent(meta));
win.on("page-title-updated", async e => {
switch (win.webContents.getTitle() as "abortInstall" | "reviewCode" | "install") {
case "abortInstall": {
win.close();
await rm(join(__dirname, "..", "src", "userplugins", repo), {
recursive: true
});
throw "Rejected by user";
// Ask for clone
const cloneDialog = await dialog.showMessageBox({
title: "Clone userplugin",
message: `You are about to clone a userplugin from ${source}.`,
type: "question",
detail: `The repository name is "${repo}" and it is owned by "${owner}".\nThe repository URL is ${link}\n\n(If you did not request this intentionally, choose Cancel)`,
buttons: ["Cancel", "Clone repository and continue install", "Open repository in browser"]
});
switch (cloneDialog.response) {
case 0: {
return reject("Rejected by user");
}
case 1: {
await cloneRepo(link, repo);
break;
}
case "reviewCode": {
win.close();
shell.openPath(join(__dirname, "..", "src", "userplugins", repo));
throw "A file explorer window should've been opened with your plugin";
break;
}
case "install": {
win.close();
await build();
break;
case 2: {
await shell.openExternal(link);
return reject("silentStop");
}
}
// Get plugin meta
const meta = await getPluginMeta(join(__dirname, "..", "src", "userplugins", repo));
// Review plugin
const win = new BrowserWindow({
maximizable: false,
minimizable: false,
width: 560,
height: meta.usesNative || meta.usesPreSend ? 650 : 360,
resizable: false,
webPreferences: {
devTools: true
},
title: "Review userplugin",
modal: true,
parent: BrowserWindow.getAllWindows()[0],
show: false,
autoHideMenuBar: true
});
const reView /* haha got it */ = new BrowserView({
webPreferences: {
devTools: true,
nodeIntegration: true
}
});
win.setBrowserView(reView);
win.addBrowserView(reView);
win.setTopBrowserView(reView);
win.loadURL(generateReviewPluginContent(meta));
win.on("page-title-updated", async e => {
switch (win.webContents.getTitle() as "abortInstall" | "reviewCode" | "install") {
case "abortInstall": {
win.close();
await rm(join(__dirname, "..", "src", "userplugins", repo), {
recursive: true
});
return reject("Rejected by user");
}
case "install": {
win.close();
await build();
resolve(meta.name);
break;
}
}
});
win.show();
});
win.show();
}
async function build(): Promise<any> {
@ -169,146 +169,7 @@ function generateReviewPluginContent(meta: {
usesPreSend: boolean;
usesNative: boolean;
}): string {
const template = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Review userplugin</title>
<script>
document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#abort").addEventListener("click", () => {
document.title = "abortInstall";
})
document.querySelector("#review").addEventListener("click", () => {
document.title = "reviewCode";
})
document.querySelector("#install").addEventListener("click", () => {
if(!document.querySelector("style").innerHTML.includes("#native-ts-warning { display: none !important; }") && !document.querySelector("input[type='checkbox']").checked) return alert("Make sure to acknowledge all warnings before installing.");
document.title = "install";
})
})
</script>
<style>
* {
font-family: sans-serif;
color: white;
}
body {
background-color: #202020;
padding: 10px;
display: flex;
flex-direction: column;
}
.card {
padding: 10px 12px;
border: 1px solid grey;
border-radius: 10px;
margin-bottom: 10px; /* Added margin for better spacing */
}
.warn-card {
border-color: #ffde00;
background-color: #ffde0011;
}
.danger-card {
border-color: #ff0000;
background-color: #ff000022;
h3 {
color: #ffaaaa;
}
}
.card * {
margin: 0;
}
.card h3 {
margin-bottom: 5px;
}
input[type="checkbox"] {
margin-right: 5px;
}
#validate {
color: #ffaaaa;
}
#btn-row {
display: flex;
flex: 1;
}
#btn-row * {
flex:1;
margin: 5px;
padding: 10px 5px;
border: 1px solid white;
border-radius: 5px;
}
#abort {
background-color: #aa3030;
}
#review {
background-color: #303030;
}
#install {
background-color: #002000;
}
#abort:hover {
background-color: #bb3030;
font-weight: bold;
}
#review:hover {
background-color: #404040;
font-weight: bold;
}
#install:hover {
background-color: #003000;
font-weight: bold;
}
%WARNINGHIDER%
%NATIVETSHIDER%
%PRESENDHIDER%
</style>
</head>
<body>
<h2>Plugin info</h2>
<div class="card" id="plugin-info-card">
<h3>%PLUGINNAME%</h3>
<p>%PLUGINDESC%</p>
</div>
<h2 data-useless="warning">Warnings</h2>
<div data-useless="warning" class="card danger-card" id="native-ts-warning">
<h3>Uses a native.ts file</h3>
<p>
Use of this file allows the plugin to escape the browser sandbox and
potentially do anything to your system and data.
<b
>ONLY INSTALL THIS PLUGIN AFTER REVIEWING THE CODE AND BEING 100% SURE
THAT NOTHING BAD CAN BE DONE!</b
>
</p>
<br />
<input type="checkbox" /> Acknowledge warning (required to allow install)
</div>
<div data-useless="warning" class="card warn-card" id="pre-send-warning">
<h3>Has pre-send listeners</h3>
<p>This allows the plugin to edit your messages before they are sent.</p>
</div>
<p id="validate">
Reminder: installing a userplugin can be a destructive action. Make sure that you know and trust the developer before installing any.
</p>
<div id="btn-row">
<button id="abort">
Cancel installation
</button>
<button id="review">
Review source code
</button>
<button id="install">
Install plugin
</button>
</div>
</body>
</html>
`.replace("%PLUGINNAME%", meta.name.replaceAll("<", "&lt;")).replace("%PLUGINDESC%", meta.description.replaceAll("<", "&lt;")).replace("%WARNINGHIDER%", !meta.usesNative && !meta.usesPreSend ? "[data-useless=\"warning\"] { display: none !important; }" : "").replace("%NATIVETSHIDER%", meta.usesNative ? "" : "#native-ts-warning { display: none !important; }").replace("%PRESENDHIDER%", meta.usesPreSend ? "" : "#pre-send-warning { display: none !important; }");
const template = pluginValidateContent.replace("%PLUGINNAME%", meta.name.replaceAll("<", "&lt;")).replace("%PLUGINDESC%", meta.description.replaceAll("<", "&lt;")).replace("%WARNINGHIDER%", !meta.usesNative && !meta.usesPreSend ? "[data-useless=\"warning\"] { display: none !important; }" : "").replace("%NATIVETSHIDER%", meta.usesNative ? "" : "#native-ts-warning { display: none !important; }").replace("%PRESENDHIDER%", meta.usesPreSend ? "" : "#pre-send-warning { display: none !important; }");
const buf = Buffer.from(template).toString("base64");
return `data:text/html;base64,${buf}`;
}

158
pluginValidate.txt Normal file
View file

@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Review userplugin</title>
<script>
document.addEventListener("DOMContentLoaded", () => {
document
.querySelector("#abort")
.addEventListener("click", () => {
document.title = "abortInstall";
});
document
.querySelector("#install")
.addEventListener("click", () => {
if (
!document
.querySelector("style")
.innerHTML.includes(
"#native-ts-warning { display: none !important; }"
) &&
!document.querySelector("input[type='checkbox']")
.checked
)
return alert(
"Make sure to acknowledge all warnings before installing."
);
document.title = "install";
});
document.querySelector("style").innerHTML +=
document.querySelector("addAtRuntimeStyle").innerHTML;
});
</script>
<style>
addAtRuntimeStyle {
display: none !important;
}
* {
font-family: sans-serif;
color: #cdd6f4;
}
body {
background-color: #1e1e2e;
padding: 10px;
display: flex;
flex-direction: column;
}
.card {
padding: 10px 12px;
border: 1px solid #9399b2;
border-radius: 10px;
margin-bottom: 10px; /* Added margin for better spacing */
}
.warn-card {
border-color: #f38ba8;
background-color: #313244;
}
.danger-card {
border-color: #f38ba8;
background-color: #313244;
h3 {
color: #cdd6f4;
}
}
.card * {
margin: 0;
}
.card h3 {
margin-bottom: 5px;
}
input[type="checkbox"] {
margin-right: 5px;
}
#validate {
color: #f38ba8;
}
#btn-row {
display: flex;
flex: 1;
}
#btn-row * {
flex: 1;
margin: 5px;
padding: 10px 5px;
border: 1px solid #cdd6f4;
border-radius: 5px;
}
#abort {
background-color: #f38ba8;
color: #11111b;
}
#install {
color: #a6e3a1;
border-color: #a6e3a1;
background-color: #00000000;
}
#abort:hover {
background-color: #ec9393;
font-weight: bold;
}
#install:hover {
background-color: #425040;
font-weight: bold;
}
</style>
<addAtRuntimeStyle>
%WARNINGHIDER% %NATIVETSHIDER% %PRESENDHIDER%
</addAtRuntimeStyle>
</head>
<body>
<h2>Plugin info</h2>
<div class="card" id="plugin-info-card">
<h3>%PLUGINNAME%</h3>
<p>%PLUGINDESC%</p>
</div>
<h2 data-useless="warning">Warnings</h2>
<div
data-useless="warning"
class="card danger-card"
id="native-ts-warning"
>
<h3>Uses a native.ts file</h3>
<p>
Use of this file allows the plugin to escape the browser sandbox
and potentially do anything to your system and data.
<b
>ONLY INSTALL THIS PLUGIN AFTER REVIEWING THE CODE AND BEING
100% SURE THAT NOTHING BAD CAN BE DONE!</b
>
</p>
<br />
<input type="checkbox" /> Acknowledge warning (required to allow
install)
</div>
<div
data-useless="warning"
class="card warn-card"
id="pre-send-warning"
>
<h3>Has pre-send listeners</h3>
<p>
This allows the plugin to edit your messages before they are
sent.
</p>
</div>
<p id="validate">
Reminder: installing a userplugin can be a destructive action. Make
sure that you know and trust the developer before installing any.
</p>
<div id="btn-row">
<button id="abort">Cancel installation</button>
<button id="install">Install plugin</button>
</div>
</body>
</html>