1
0
mirror of https://github.com/boku7/Loki synced 2026-06-06 15:24:27 +00:00
Files
2025-05-05 13:16:59 -07:00

504 lines
17 KiB
JavaScript

const { ipcRenderer } = require("electron");
const crypto = require('crypto');
const az = require('./azure');
const { log } = require('console');
let sortAscending = true;
let containerName = '';
let containerKey;
let containerBlob;
let agentObj;
let agent;
let init_cwd = 0;
let pwd = '';
let mode = '';
let currentFile = '';
global.agent = null;
function splitStringWithQuotes(str) {
const result = [];
let current = '';
let insideQuotes = false;
let quoteChar = '';
for (let i = 0; i < str.length; i++) {
const char = str[i];
if (insideQuotes) {
if (char === '\\' && (str[i + 1] === quoteChar || str[i + 1] === '\\')) {
// Handle escaped quote or backslash
current += str[i + 1];
i++; // Skip the next character
} else if (char === quoteChar) {
// End of quoted string
insideQuotes = false;
result.push(current);
current = '';
} else {
// Inside quoted string
current += char;
}
} else {
if (char === '"' || char === "'") {
// Start of quoted string
insideQuotes = true;
quoteChar = char;
} else if (char === '\\' && str[i + 1] === ' ') {
// Handle escaped space
current += ' ';
i++; // Skip the next character
} else if (char === ' ') {
// Space outside of quotes
if (current.length > 0) {
result.push(current);
current = '';
}
} else {
// Unquoted string
current += char;
}
}
}
// Add the last argument if there's any
if (current.length > 0) {
result.push(current);
}
return result;
}
function generateUUID(len) {
// Generate a random UUID
if (len > 20){len = 20};
const uuid = crypto.randomUUID();
// Remove hyphens and take the first 10 characters
const shortUUID = uuid.replace(/-/g, '').substring(0, len);
return shortUUID;
}
function doDownloadFile(argv)
{
const downloadFile = "'" + argv[1] +"'";
const downloadBlob = generateUUID(10);
const download = {
'file':downloadFile,
'blob':downloadBlob
}
return download;
}
class Task {
constructor(command) {
this.outputChannel = 'o-' + Math.random().toString(36).substring(2, 14);
this.command = command;
}
}
function sendCommand(command) {
let argv = splitStringWithQuotes(command);
log(`[EXPLORER][SENDCMD] command : ${command}`);
log(`[EXPLORER][SENDCMD] argv : ${argv}`);
let download;
const task = new Task(
command
);
if (!Array.isArray(global.agent.tasks)) {
global.agent.tasks = [];
}
global.agent.tasks.push(task);
log(`[EXPLORER][SENDCMD] task : ${JSON.stringify(global.agent.tasks[global.agent.tasks.length - 1])}`);
if (argv[0] == "download")
{
let currentPath = document.getElementById("dirPath").value.trim();
const concatenated = argv.slice(1).join(" ");
argv[1] = currentPath.endsWith("/") ? currentPath + concatenated : currentPath + "/" + concatenated;
download = doDownloadFile(argv);
global.agent.tasks[global.agent.tasks.length - 1].command = `download ${download['file']} ${download['blob']}`;
ipcRenderer.send('pull-download-file', global.agent,download['file'],download['blob']); // Send command and container name
ipcRenderer.send('upload-client-command-to-input-channel', global.agent); // Send command and container name
//
}
else{
log(`[EXPLORER][SENDCMD] hit all else handler`);
ipcRenderer.send('upload-client-command-to-input-channel', global.agent); // Send command and container name
}
}
async function initialize() {
log(`explorer.js : initialize `);
mode = "pwd";
await sendCommand("pwd");
}
async function listFiles() {
mode = "list";
sendCommand(`ls ${"'"+document.getElementById("dirPath").value.trim()+"'"}`);
}
async function format_ls_output(filesAndFolders) {
let resultBuffer = '';
resultBuffer += `Name`.padEnd(60) + `Type`.padEnd(16) + `Size (bytes)`.padEnd(15) + `Created`.padEnd(24) + `Modified`.padEnd(24) + '\n';
resultBuffer += '-'.repeat(30) + '-'.repeat(10) + '-'.repeat(15) + '-'.repeat(30) + '-'.repeat(30) + '\n';
log(`[AGENT][IPC] format_ls_output : ${filesAndFolders}`);
let entries = JSON.parse(filesAndFolders)
for (const entry of entries) {
try {
log(`[AGENT][IPC] entry : ${entry}`);
const options = {
month: '2-digit',
day: '2-digit',
year: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
};
resultBuffer += (entry.name || '').padEnd(60);
resultBuffer += (entry.type || '').padEnd(16);
resultBuffer += (entry.stats.size ? String(entry.stats.size) : '').padEnd(15);
resultBuffer += (entry.stats.ctimeMs ? new Date(entry.stats.ctimeMs).toLocaleString('en-US', options).replace(',', '') : '').padEnd(24);
resultBuffer += (entry.stats.mtimeMs ? new Date(entry.stats.mtimeMs).toLocaleString('en-US', options).replace(',', '') : '').padEnd(24);
resultBuffer += '\n';
} catch (err) {
log(`Error: ${err.message}`);
}
}
return resultBuffer;
}
async function listFiles_display(lsCommandOutput)
{
try {
// Parse the JSON string into an array of file entries
let files = JSON.parse(lsCommandOutput);
// Insert into table
const fileList = document.getElementById("fileList");
fileList.innerHTML = "";
files.forEach(file => {
const row = document.createElement("tr");
const type = file.type === "Directory" ? "Folder" : "File";
const size = file.stats.size || '-';
const options = {
month: '2-digit',
day: '2-digit',
year: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
};
const modified = file.stats.mtimeMs ? new Date(file.stats.mtimeMs).toLocaleString('en-US', options).replace(',', '') : '';
row.innerHTML = `
<td class="${type.toLowerCase()}"
onclick="${type === 'Folder' ? `navigateTo('${file.name}')` : `openFile('${file.name}')`}">
${file.name}
</td>
<td>${size}</td>
<td>${type}</td>
<td>${modified}</td>
`;
fileList.appendChild(row);
});
} catch (err) {
log(`Error in listFiles_display: ${err.message}`);
}
}
async function navigateTo(folderName) {
let currentPath = document.getElementById("dirPath").value.trim();
// Ensure the new path is correctly formatted
let newPath = currentPath.endsWith("/") ? currentPath + folderName : currentPath + "/" + folderName;
document.getElementById("dirPath").value = newPath;
listFiles();
}
async function goBack() {
let currentPath = document.getElementById("dirPath").value.trim();
if (currentPath === "/") return; // Prevent navigating beyond root "/"
// Remove trailing slash if it exists to avoid double counting
if (currentPath.endsWith("/")) {
currentPath = currentPath.slice(0, -1);
}
// Find the new last occurrence of "/"
let lastSlashIndex = currentPath.lastIndexOf("/");
// If there's a parent directory, update the path
if (lastSlashIndex > 0) {
let parentPath = currentPath.substring(0, lastSlashIndex + 1); // Keep trailing slash
document.getElementById("dirPath").value = parentPath;
listFiles();
} else {
document.getElementById("dirPath").value = "/"; // If no more parent, stay at root
listFiles();
}
}
async function openFile(filePath) {
const textExtensions = [".txt", ".json", ".pem", ".log", ".xml", ".md", ".csv", ".ini", ".conf", ".yaml", ".yml",
".js",".bak",".html",".css",".cnf"
];
// Extract file extension
log(`openFile : ${filePath}`);
let fileExt = filePath.substring(filePath.lastIndexOf(".")).toLowerCase();
if (textExtensions.includes(fileExt)) {
mode = 'cat';
log(`Reading file: ${filePath}`);
let currentPath = document.getElementById("dirPath").value.trim();
let catFilePath = currentPath.endsWith("/") ? currentPath + filePath : currentPath + "/" + filePath;
catFilePath = "'" + catFilePath + "'";
currentFile = catFilePath;
sendCommand(`cat ${catFilePath}`);
} else {
log(`Downloading file: ${filePath}`);
sendCommand(`download ${filePath}`);
}
}
function sortTable(columnIndex) {
const table = document.querySelector("tbody");
const rows = Array.from(table.querySelectorAll("tr"));
rows.sort((a, b) => {
let valA = a.children[columnIndex].innerText;
let valB = b.children[columnIndex].innerText;
if (!isNaN(valA) && !isNaN(valB)) {
valA = parseFloat(valA);
valB = parseFloat(valB);
}
return sortAscending ? (valA > valB ? 1 : -1) : (valA < valB ? 1 : -1);
});
sortAscending = !sortAscending;
table.innerHTML = "";
rows.forEach(row => table.appendChild(row));
}
document.getElementById("dirPath").addEventListener("keypress", function(event) {
if (event.key === "Enter") {
listFiles();
}
});
// document.addEventListener("keydown", function(event) {
// if (event.key === "Backspace") goBack();
// });
ipcRenderer.on('command-output', (event, output) => {
try {
if (output) {
// Convert output to string if it's an object
log(`[EXPLORER][IPC][COMMANDOUT]: output : ${JSON.stringify(output)}`);
// if(output.output === "pushedfile" || !output.output.substring(0, 3).includes('/')){
// const dirPath = document.getElementById('dirPath');
// if (!dirPath.value)
// {
// initialize();
// return;
// }else{
// // listFiles();
// }
// }
if (output.command === "pwd") {
mode = "";
output.output = output.output.trim();
output.output = output.output.replace(/\\/g, '/');
if (!output.output.endsWith("/")) {
output.output += "/";
}
document.getElementById("dirPath").value = output.output;
log(`[EXPLORER][IPC][CMDOUT] output.output : ${output.output}`);
pwd = output.output;
listFiles();
}
else if (mode === "list") {
mode = "";
listFiles_display(output.output);
}
else if (mode === "cat") {
mode = "";
openTextFileInNewWindow(output.output);
}
}
} catch (error) {
log(`[EXPLORER][IPC][CMDOUT] error : ${error.message}\r\n${error.stack}`);
}
});
function openTextFileInNewWindow(content) {
// Count number of lines in the content
const lineCount = content.split("\n").length;
const lineHeight = 18; // Approximate height per line in pixels (adjust if needed)
const minHeight = 200; // Minimum window height
const maxHeight = window.screen.height - 100; // Prevent window from being too large
let calculatedHeight = Math.min(Math.max(lineCount * lineHeight + 200, minHeight), maxHeight);
// Open new window with adjusted height
const textWindow = window.open("", "TextViewer", `width=1000,height=${calculatedHeight}`);
if (currentFile.startsWith("'") && currentFile.endsWith("'")) {
currentFile = currentFile.slice(1, -1);
}
textWindow.document.write(`
<html>
<head>
<title>${currentFile}</title>
<style>
body {
font-family: monospace;
white-space: pre-wrap;
margin: 0;
padding: 0;
background: #111;
color: #f8f9f8;
overflow: hidden;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.header {
padding: 2px;
background: #222;
color: #fff;
text-align: center;
font-size: 18px;
border-bottom: 1px solid #333;
}
.content {
flex: 1;
padding: 2px;
overflow: auto;
}
textarea {
width: 100%;
height: 100%;
background: #222;
color: #fff;
border: none;
padding: 2px;
font-size: 14px;
resize: none;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<textarea readonly>${content}</textarea>
</div>
</div>
</body>
</html>
`);
}
window.addEventListener('DOMContentLoaded', async () =>
{
ipcRenderer.on('container-data', (event, agent_object) =>
{
log(`explorer.js : container-data : ${agent_object.agentid}`);
try{
global.agent = agent_object;
document.title = `${agent_object.agentid.toUpperCase()} | ${agent_object.hostname.toUpperCase()} | ${agent_object.username.toUpperCase()} | ${agent_object.IP}`;
const dirPath = document.getElementById('dirPath');
dirPath.focus();
dirPath.selectionStart = dirPath.selectionEnd = dirPath.value.length; // Ensure cursor is at the end of input
}catch(error)
{
log(error);
}
});
const checkVariables = setInterval(() => {
if (global.agent) {
log(`explorer.js : container-data : ${global.agent}`);
clearInterval(checkVariables); // Stop the loop when all variables have values
initialize();
}
}, 100); // Check every 100ms
}
);
document.addEventListener("keydown", function(event) {
const dirPath = document.getElementById("dirPath");
if (!dirPath) return; // Ensure element exists
const isCtrlOrMeta = event.ctrlKey || event.metaKey;
const isAlt = event.altKey;
if (isCtrlOrMeta) {
switch (event.key.toLowerCase()) {
case "c": // Copy
event.preventDefault();
dirPath.select();
document.execCommand("copy");
log("Copied to clipboard:", dirPath.value);
break;
case "a": // Select all
event.preventDefault();
dirPath.select();
log("Selected all text in dirPath");
break;
case "v": // Paste
event.preventDefault();
navigator.clipboard.readText().then((clipboardText) => {
dirPath.value = clipboardText;
log("Pasted:", clipboardText);
}).catch(err => console.error("Failed to read clipboard:", err));
break;
case "x": // Cut
event.preventDefault();
dirPath.select();
document.execCommand("cut");
log("Cut text from dirPath");
break;
case "arrowleft": // Move cursor back one word
event.preventDefault();
moveCursorByWord(dirPath, "left");
log("Moved cursor back one word");
break;
case "arrowright": // Move cursor forward one word
event.preventDefault();
moveCursorByWord(dirPath, "right");
log("Moved cursor forward one word");
break;
}
}
});
// Function to move cursor by one word
function moveCursorByWord(input, direction) {
let pos = input.selectionStart;
let value = input.value;
if (direction === "left") {
while (pos > 0 && value[pos - 1] === " ") pos--; // Skip spaces
while (pos > 0 && value[pos - 1] !== " ") pos--; // Move to start of the word
} else if (direction === "right") {
while (pos < value.length && value[pos] === " ") pos++; // Skip spaces
while (pos < value.length && value[pos] !== " ") pos++; // Move to end of the word
}
input.selectionStart = input.selectionEnd = pos;
}