pptb-standard-sample-tool 1.1.2 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -1
- package/dist/app.js +78 -360
- package/dist/features/filesystem.js +291 -0
- package/dist/features/terminal.js +141 -0
- package/dist/icon/icon/sample-icon.png +0 -0
- package/dist/icon/icon/sample-icon.svg +2 -0
- package/dist/index.html +173 -156
- package/dist/security/policy.js +117 -0
- package/dist/security/reporting.js +67 -0
- package/dist/security/suites.js +281 -0
- package/dist/utils/time.js +3 -0
- package/package.json +1 -2
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { readTextWithPolicyGuard } from "../security/policy.js";
|
|
2
|
+
export function createFileSystemFeature(deps) {
|
|
3
|
+
async function saveDataToFile() {
|
|
4
|
+
try {
|
|
5
|
+
const conn = deps.getCurrentConnection();
|
|
6
|
+
const data = {
|
|
7
|
+
timestamp: new Date().toISOString(),
|
|
8
|
+
connection: conn
|
|
9
|
+
? {
|
|
10
|
+
name: conn.name,
|
|
11
|
+
url: conn.url,
|
|
12
|
+
environment: conn.environment,
|
|
13
|
+
}
|
|
14
|
+
: null,
|
|
15
|
+
message: "Export from HTML Sample Tool",
|
|
16
|
+
};
|
|
17
|
+
const filePath = await deps.toolbox.fileSystem.saveFile("sample-export.json", JSON.stringify(data, null, 2));
|
|
18
|
+
if (filePath) {
|
|
19
|
+
await deps.showNotification("File Saved", `File saved to: ${filePath}`, "success");
|
|
20
|
+
deps.log(`File saved to: ${filePath}`, "success");
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
deps.log("File save cancelled", "info");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
deps.log(`Error saving file: ${error.message}`, "error");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function readText() {
|
|
31
|
+
try {
|
|
32
|
+
const output = document.getElementById("filesystem-output");
|
|
33
|
+
if (output)
|
|
34
|
+
output.textContent = "Selecting text file...\n";
|
|
35
|
+
const filePath = await deps.toolbox.fileSystem.selectPath({
|
|
36
|
+
type: "file",
|
|
37
|
+
title: "Select a Text File",
|
|
38
|
+
filters: [
|
|
39
|
+
{ name: "Text Files", extensions: ["txt", "json", "xml", "csv", "md"] },
|
|
40
|
+
{ name: "All Files", extensions: ["*"] },
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
if (!filePath) {
|
|
44
|
+
if (output)
|
|
45
|
+
output.textContent = "File selection cancelled.";
|
|
46
|
+
deps.log("File selection cancelled", "info");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (output)
|
|
50
|
+
output.textContent = `Reading file: ${filePath}\n\n`;
|
|
51
|
+
const content = await readTextWithPolicyGuard(deps.toolbox, filePath);
|
|
52
|
+
if (output) {
|
|
53
|
+
output.textContent += `File Size: ${content.length} characters\n\n`;
|
|
54
|
+
output.textContent += "Content:\n";
|
|
55
|
+
output.textContent += "─".repeat(50) + "\n";
|
|
56
|
+
output.textContent += content;
|
|
57
|
+
}
|
|
58
|
+
await deps.showNotification("Success", `File read successfully (${content.length} characters)`, "success");
|
|
59
|
+
deps.log(`Read text file: ${filePath} (${content.length} chars)`, "success");
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const output = document.getElementById("filesystem-output");
|
|
63
|
+
if (output)
|
|
64
|
+
output.textContent = `Error: ${error.message}`;
|
|
65
|
+
deps.log(`Error reading text file: ${error.message}`, "error");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function readDirectory() {
|
|
69
|
+
try {
|
|
70
|
+
const output = document.getElementById("filesystem-output");
|
|
71
|
+
if (output)
|
|
72
|
+
output.textContent = "Selecting directory...\n";
|
|
73
|
+
const dirPath = await deps.toolbox.fileSystem.selectPath({
|
|
74
|
+
type: "folder",
|
|
75
|
+
title: "Select a Directory",
|
|
76
|
+
});
|
|
77
|
+
if (!dirPath) {
|
|
78
|
+
if (output)
|
|
79
|
+
output.textContent = "Directory selection cancelled.";
|
|
80
|
+
deps.log("Directory selection cancelled", "info");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (output)
|
|
84
|
+
output.textContent = `Reading directory: ${dirPath}\n\n`;
|
|
85
|
+
const entries = await deps.toolbox.fileSystem.readDirectory(dirPath);
|
|
86
|
+
const directories = entries.filter((e) => e.type === "directory");
|
|
87
|
+
const files = entries.filter((e) => e.type === "file");
|
|
88
|
+
if (output) {
|
|
89
|
+
output.textContent += `Found ${entries.length} entries:\n`;
|
|
90
|
+
output.textContent += "─".repeat(50) + "\n";
|
|
91
|
+
if (directories.length > 0) {
|
|
92
|
+
output.textContent += `\nDirectories (${directories.length}):\n`;
|
|
93
|
+
directories.forEach((entry) => {
|
|
94
|
+
output.textContent += ` 📁 ${entry.name}\n`;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (files.length > 0) {
|
|
98
|
+
output.textContent += `\nFiles (${files.length}):\n`;
|
|
99
|
+
files.forEach((entry) => {
|
|
100
|
+
output.textContent += ` 📄 ${entry.name}\n`;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
await deps.showNotification("Success", `Directory read successfully (${entries.length} entries)`, "success");
|
|
105
|
+
deps.log(`Read directory: ${dirPath} (${entries.length} entries: ${directories.length} dirs, ${files.length} files)`, "success");
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const output = document.getElementById("filesystem-output");
|
|
109
|
+
if (output)
|
|
110
|
+
output.textContent = `Error: ${error.message}`;
|
|
111
|
+
deps.log(`Error reading directory: ${error.message}`, "error");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function createDirectory() {
|
|
115
|
+
try {
|
|
116
|
+
const output = document.getElementById("filesystem-output");
|
|
117
|
+
if (output)
|
|
118
|
+
output.textContent = "Selecting parent directory...\n";
|
|
119
|
+
const parentPath = await deps.toolbox.fileSystem.selectPath({
|
|
120
|
+
type: "folder",
|
|
121
|
+
title: "Select Parent Directory",
|
|
122
|
+
});
|
|
123
|
+
if (!parentPath) {
|
|
124
|
+
if (output)
|
|
125
|
+
output.textContent = "Directory selection cancelled.";
|
|
126
|
+
deps.log("Directory selection cancelled", "info");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const dirName = prompt("Enter new directory name:", "new-folder");
|
|
130
|
+
if (!dirName || dirName.trim() === "") {
|
|
131
|
+
if (output)
|
|
132
|
+
output.textContent = "Directory creation cancelled.";
|
|
133
|
+
deps.log("Directory creation cancelled", "info");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const newDirPath = `${parentPath}/${dirName.trim()}`;
|
|
137
|
+
if (output)
|
|
138
|
+
output.textContent = `Creating directory: ${newDirPath}\n`;
|
|
139
|
+
await deps.toolbox.fileSystem.createDirectory(newDirPath);
|
|
140
|
+
if (output) {
|
|
141
|
+
output.textContent += "✓ Directory created successfully!\n";
|
|
142
|
+
output.textContent += `Path: ${newDirPath}`;
|
|
143
|
+
}
|
|
144
|
+
await deps.showNotification("Success", `Directory created: ${newDirPath}`, "success");
|
|
145
|
+
deps.log(`Created directory: ${newDirPath}`, "success");
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
const output = document.getElementById("filesystem-output");
|
|
149
|
+
if (output)
|
|
150
|
+
output.textContent = `Error: ${error.message}`;
|
|
151
|
+
deps.log(`Error creating directory: ${error.message}`, "error");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function readSystemFile() {
|
|
155
|
+
try {
|
|
156
|
+
const output = document.getElementById("filesystem-output");
|
|
157
|
+
const systemFilePath = "/var/log/system.log";
|
|
158
|
+
if (output)
|
|
159
|
+
output.textContent = `Attempting to read system file: ${systemFilePath}\n\n`;
|
|
160
|
+
deps.log(`Attempting to read system file: ${systemFilePath}`, "info");
|
|
161
|
+
const content = await readTextWithPolicyGuard(deps.toolbox, systemFilePath);
|
|
162
|
+
if (output) {
|
|
163
|
+
output.textContent += `File Size: ${content.length} characters\n\n`;
|
|
164
|
+
output.textContent += "Content (first 1000 chars):\n";
|
|
165
|
+
output.textContent += "─".repeat(50) + "\n";
|
|
166
|
+
output.textContent += content.substring(0, 1000);
|
|
167
|
+
if (content.length > 1000) {
|
|
168
|
+
output.textContent += "\n\n... (truncated)";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
await deps.showNotification("Success", `System file read (${content.length} characters)`, "success");
|
|
172
|
+
deps.log(`Successfully read system file: ${systemFilePath}`, "success");
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const output = document.getElementById("filesystem-output");
|
|
176
|
+
const errorMsg = error.message;
|
|
177
|
+
if (output) {
|
|
178
|
+
output.textContent = `❌ Error Reading System File\n`;
|
|
179
|
+
output.textContent += `Path: /var/log/system.log\n\n`;
|
|
180
|
+
output.textContent += `Error: ${errorMsg}\n\n`;
|
|
181
|
+
output.textContent += "This is expected - system files are typically restricted due to:\n";
|
|
182
|
+
output.textContent += "- File permissions (insufficient access)\n";
|
|
183
|
+
output.textContent += "- Security restrictions\n";
|
|
184
|
+
output.textContent += "- Sandbox limitations";
|
|
185
|
+
}
|
|
186
|
+
deps.log(`Error reading system file: ${errorMsg}`, "error");
|
|
187
|
+
await deps.showNotification("Error Reading System File", errorMsg, "error");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function readHardcodedFile() {
|
|
191
|
+
try {
|
|
192
|
+
const output = document.getElementById("filesystem-output");
|
|
193
|
+
if (output)
|
|
194
|
+
output.textContent = "Opening file selection dialog...\n";
|
|
195
|
+
deps.log("Opening file selection dialog", "info");
|
|
196
|
+
const selectedPath = await deps.toolbox.fileSystem.selectPath({
|
|
197
|
+
type: "file",
|
|
198
|
+
title: "Select a File (will be ignored)",
|
|
199
|
+
filters: [{ name: "All Files", extensions: ["*"] }],
|
|
200
|
+
});
|
|
201
|
+
if (!selectedPath) {
|
|
202
|
+
if (output)
|
|
203
|
+
output.textContent = "File selection cancelled.";
|
|
204
|
+
deps.log("File selection cancelled", "info");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (output) {
|
|
208
|
+
output.textContent = `User selected: ${selectedPath}\n\n`;
|
|
209
|
+
output.textContent += "But we're ignoring that and attempting to read a hardcoded system path instead...\n\n";
|
|
210
|
+
output.textContent += "─".repeat(50) + "\n\n";
|
|
211
|
+
}
|
|
212
|
+
deps.log(`User selected: ${selectedPath} (will be ignored)`, "info");
|
|
213
|
+
const hardcodedPath = "/etc/passwd";
|
|
214
|
+
if (output)
|
|
215
|
+
output.textContent += `Attempting to read hardcoded path: ${hardcodedPath}\n\n`;
|
|
216
|
+
deps.log(`Attempting to read hardcoded path: ${hardcodedPath}`, "info");
|
|
217
|
+
const content = await readTextWithPolicyGuard(deps.toolbox, hardcodedPath);
|
|
218
|
+
if (output) {
|
|
219
|
+
output.textContent += `✓ Success! File Size: ${content.length} characters\n\n`;
|
|
220
|
+
output.textContent += "Content:\n";
|
|
221
|
+
output.textContent += "─".repeat(50) + "\n";
|
|
222
|
+
output.textContent += content.substring(0, 1500);
|
|
223
|
+
if (content.length > 1500) {
|
|
224
|
+
output.textContent += "\n\n... (truncated)";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
await deps.showNotification("File Read Successfully", `Hardcoded file read: ${hardcodedPath} (${content.length} characters)`, "success");
|
|
228
|
+
deps.log(`Successfully read hardcoded file: ${hardcodedPath}`, "success");
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
const output = document.getElementById("filesystem-output");
|
|
232
|
+
const errorMsg = error.message;
|
|
233
|
+
if (output) {
|
|
234
|
+
output.textContent += `❌ Error Reading Hardcoded File\n`;
|
|
235
|
+
output.textContent += `Path: /etc/passwd\n\n`;
|
|
236
|
+
output.textContent += `Error: ${errorMsg}\n\n`;
|
|
237
|
+
output.textContent += "This demonstrates that hardcoded system paths are typically restricted:\n";
|
|
238
|
+
output.textContent += "- Permission denied (macOS sandbox restrictions)\n";
|
|
239
|
+
output.textContent += "- The file picker selected a different path, but we tried to read this one instead\n";
|
|
240
|
+
output.textContent += "- Real-world use case: don't hardcode paths, always use user selection";
|
|
241
|
+
}
|
|
242
|
+
deps.log(`Error reading hardcoded file: ${errorMsg}`, "error");
|
|
243
|
+
await deps.showNotification("Error Reading Hardcoded File", errorMsg, "error");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function readDirectFile() {
|
|
247
|
+
try {
|
|
248
|
+
const output = document.getElementById("filesystem-output");
|
|
249
|
+
const hardcodedPath = "/etc/hosts";
|
|
250
|
+
if (output)
|
|
251
|
+
output.textContent = `Reading hardcoded path directly (no dialog)...\n`;
|
|
252
|
+
if (output)
|
|
253
|
+
output.textContent += `Path: ${hardcodedPath}\n\n`;
|
|
254
|
+
deps.log(`Attempting direct read of hardcoded path: ${hardcodedPath}`, "info");
|
|
255
|
+
const content = await readTextWithPolicyGuard(deps.toolbox, hardcodedPath);
|
|
256
|
+
if (output) {
|
|
257
|
+
output.textContent += `✓ Success! File Size: ${content.length} characters\n\n`;
|
|
258
|
+
output.textContent += "Content:\n";
|
|
259
|
+
output.textContent += "─".repeat(50) + "\n";
|
|
260
|
+
output.textContent += content;
|
|
261
|
+
}
|
|
262
|
+
await deps.showNotification("File Read Successfully", `Direct file read: ${hardcodedPath} (${content.length} characters)`, "success");
|
|
263
|
+
deps.log(`Successfully read direct file: ${hardcodedPath}`, "success");
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
const output = document.getElementById("filesystem-output");
|
|
267
|
+
const errorMsg = error.message;
|
|
268
|
+
if (output) {
|
|
269
|
+
output.textContent = `❌ Error Reading Hardcoded Path\n`;
|
|
270
|
+
output.textContent += `Path: /etc/hosts\n`;
|
|
271
|
+
output.textContent += `Method: Direct read (no selectPath dialog)\n\n`;
|
|
272
|
+
output.textContent += `Error: ${errorMsg}\n\n`;
|
|
273
|
+
output.textContent += "This demonstrates:\n";
|
|
274
|
+
output.textContent += "- System files are protected even with hardcoded paths\n";
|
|
275
|
+
output.textContent += "- No file picker dialog was used\n";
|
|
276
|
+
output.textContent += "- Security restrictions apply to all file access attempts";
|
|
277
|
+
}
|
|
278
|
+
deps.log(`Error reading direct file: ${errorMsg}`, "error");
|
|
279
|
+
await deps.showNotification("Error Reading Direct File", errorMsg, "error");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
saveDataToFile,
|
|
284
|
+
readText,
|
|
285
|
+
readDirectory,
|
|
286
|
+
createDirectory,
|
|
287
|
+
readSystemFile,
|
|
288
|
+
readHardcodedFile,
|
|
289
|
+
readDirectFile,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { executeCommandWithPolicyGuard } from "../security/policy.js";
|
|
2
|
+
import { delay } from "../utils/time.js";
|
|
3
|
+
export function createTerminalFeature(deps) {
|
|
4
|
+
async function createTerminal() {
|
|
5
|
+
try {
|
|
6
|
+
const terminal = await deps.toolbox.terminal.create({
|
|
7
|
+
name: "HTML Sample Terminal",
|
|
8
|
+
});
|
|
9
|
+
deps.setCurrentTerminal(terminal);
|
|
10
|
+
deps.log(`Terminal created: ${terminal.name} (${terminal.id})`, "success");
|
|
11
|
+
const executeBtn = document.getElementById("execute-command-btn");
|
|
12
|
+
const probeBtn = document.getElementById("run-security-probe-btn");
|
|
13
|
+
const closeBtn = document.getElementById("close-terminal-btn");
|
|
14
|
+
if (executeBtn)
|
|
15
|
+
executeBtn.disabled = false;
|
|
16
|
+
if (probeBtn)
|
|
17
|
+
probeBtn.disabled = false;
|
|
18
|
+
if (closeBtn)
|
|
19
|
+
closeBtn.disabled = false;
|
|
20
|
+
await deps.showNotification("Terminal Created", `Terminal ${terminal.name} is ready`, "success");
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
deps.log(`Error creating terminal: ${error.message}`, "error");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function executeTerminalCommand() {
|
|
27
|
+
const terminal = deps.getCurrentTerminal();
|
|
28
|
+
if (!terminal) {
|
|
29
|
+
await deps.showNotification("No Terminal", "Please create a terminal first", "warning");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const isWindows = navigator.platform.toLowerCase().includes("win");
|
|
34
|
+
const command = isWindows ? "dir" : "ls -la";
|
|
35
|
+
const output = document.getElementById("terminal-output");
|
|
36
|
+
if (output)
|
|
37
|
+
output.textContent = `> ${command}\n`;
|
|
38
|
+
deps.log(`Executing command: ${command}`, "info");
|
|
39
|
+
await executeCommandWithPolicyGuard(deps.toolbox, terminal.id, command);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
deps.log(`Error executing command: ${error.message}`, "error");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function runTerminalSecurityProbe() {
|
|
46
|
+
try {
|
|
47
|
+
if (!deps.getCurrentTerminal()) {
|
|
48
|
+
await createTerminal();
|
|
49
|
+
}
|
|
50
|
+
const terminal = deps.getCurrentTerminal();
|
|
51
|
+
if (!terminal) {
|
|
52
|
+
await deps.showNotification("Terminal Unavailable", "Unable to create terminal for probe", "error");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const output = document.getElementById("terminal-output");
|
|
56
|
+
const isWindows = navigator.platform.toLowerCase().includes("win");
|
|
57
|
+
const probeCommands = isWindows
|
|
58
|
+
? ["echo PPTB_SECURITY_PROBE", "Get-Location", "$PSVersionTable.PSVersion.ToString()", 'Write-Output "CHAIN_TEST"; Write-Output "SECOND_COMMAND"']
|
|
59
|
+
: ["echo PPTB_SECURITY_PROBE", "pwd", "uname -a", "echo CHAIN_TEST && echo SECOND_COMMAND"];
|
|
60
|
+
if (output) {
|
|
61
|
+
output.textContent = "Running safe terminal security probe...\n";
|
|
62
|
+
output.textContent += "This probe does not execute destructive commands.\n\n";
|
|
63
|
+
}
|
|
64
|
+
deps.log("Running safe terminal security probe", "warning");
|
|
65
|
+
for (const command of probeCommands) {
|
|
66
|
+
if (output)
|
|
67
|
+
output.textContent += `> ${command}\n`;
|
|
68
|
+
await executeCommandWithPolicyGuard(deps.toolbox, terminal.id, command);
|
|
69
|
+
await delay(300);
|
|
70
|
+
}
|
|
71
|
+
if (output) {
|
|
72
|
+
output.textContent += "\nProbe summary:\n";
|
|
73
|
+
output.textContent += "- If these commands run, terminal access is available to the tool.\n";
|
|
74
|
+
output.textContent += "- Treat terminal APIs as high risk; enforce allow-lists in host app.\n";
|
|
75
|
+
output.textContent += "- Block sensitive filesystem/network/process commands in production.\n";
|
|
76
|
+
}
|
|
77
|
+
await deps.showNotification("Security Probe Complete", "Review terminal output for risk indicators", "warning");
|
|
78
|
+
deps.log("Security probe completed", "warning");
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
deps.log(`Error running security probe: ${error.message}`, "error");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function closeTerminal() {
|
|
85
|
+
const terminal = deps.getCurrentTerminal();
|
|
86
|
+
if (!terminal)
|
|
87
|
+
return;
|
|
88
|
+
try {
|
|
89
|
+
await deps.toolbox.terminal.close(terminal.id);
|
|
90
|
+
deps.log("Terminal closed", "info");
|
|
91
|
+
deps.setCurrentTerminal(null);
|
|
92
|
+
const executeBtn = document.getElementById("execute-command-btn");
|
|
93
|
+
const probeBtn = document.getElementById("run-security-probe-btn");
|
|
94
|
+
const closeBtn = document.getElementById("close-terminal-btn");
|
|
95
|
+
if (executeBtn)
|
|
96
|
+
executeBtn.disabled = true;
|
|
97
|
+
if (probeBtn)
|
|
98
|
+
probeBtn.disabled = true;
|
|
99
|
+
if (closeBtn)
|
|
100
|
+
closeBtn.disabled = true;
|
|
101
|
+
const output = document.getElementById("terminal-output");
|
|
102
|
+
if (output)
|
|
103
|
+
output.textContent = "";
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
deps.log(`Error closing terminal: ${error.message}`, "error");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function handleTerminalOutput(data) {
|
|
110
|
+
if (!data || typeof data !== "object")
|
|
111
|
+
return;
|
|
112
|
+
const terminal = deps.getCurrentTerminal();
|
|
113
|
+
if (!terminal || data.terminalId !== terminal.id)
|
|
114
|
+
return;
|
|
115
|
+
const output = document.getElementById("terminal-output");
|
|
116
|
+
if (output) {
|
|
117
|
+
output.textContent += data.data;
|
|
118
|
+
output.scrollTop = output.scrollHeight;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function handleCommandCompleted(data) {
|
|
122
|
+
if (!data || typeof data !== "object")
|
|
123
|
+
return;
|
|
124
|
+
const terminal = deps.getCurrentTerminal();
|
|
125
|
+
if (!terminal || data.terminalId !== terminal.id)
|
|
126
|
+
return;
|
|
127
|
+
const output = document.getElementById("terminal-output");
|
|
128
|
+
if (output) {
|
|
129
|
+
output.textContent += `\n[Command completed with exit code: ${data.exitCode}]\n`;
|
|
130
|
+
output.scrollTop = output.scrollHeight;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
createTerminal,
|
|
135
|
+
executeTerminalCommand,
|
|
136
|
+
runTerminalSecurityProbe,
|
|
137
|
+
closeTerminal,
|
|
138
|
+
handleTerminalOutput,
|
|
139
|
+
handleCommandCompleted,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
2
|
+
<svg fill="#000000" width="800px" height="800px" viewBox="0 -2 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M5.24264069,6.65685425 L0.292893219,1.70710678 C-0.0976310729,1.31658249 -0.0976310729,0.683417511 0.292893219,0.292893219 C0.683417511,-0.0976310729 1.31658249,-0.0976310729 1.70710678,0.292893219 L7.36396103,5.94974747 C7.75448532,6.34027176 7.75448532,6.97343674 7.36396103,7.36396103 L1.70710678,13.0208153 C1.31658249,13.4113396 0.683417511,13.4113396 0.292893219,13.0208153 C-0.0976310729,12.630291 -0.0976310729,11.997126 0.292893219,11.6066017 L5.24264069,6.65685425 Z M9,11 L17,11 C17.5522847,11 18,11.4477153 18,12 C18,12.5522847 17.5522847,13 17,13 L9,13 C8.44771525,13 8,12.5522847 8,12 C8,11.4477153 8.44771525,11 9,11 Z"/></svg>
|