@pixelbyte-software/pixcode 1.53.28 → 1.54.1
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/dist/assets/{index-BBDYzJ7B.js → index-CLO8YURv.js} +148 -148
- package/dist/index.html +1 -1
- package/dist-server/server/claude-sdk.js +6 -0
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cli.js +67 -0
- package/dist-server/server/cli.js.map +1 -1
- package/dist-server/server/index.js +278 -54
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/middleware/account-lockout.js +101 -0
- package/dist-server/server/middleware/account-lockout.js.map +1 -0
- package/dist-server/server/middleware/auth.js +71 -9
- package/dist-server/server/middleware/auth.js.map +1 -1
- package/dist-server/server/middleware/rate-limiter.js +62 -0
- package/dist-server/server/middleware/rate-limiter.js.map +1 -0
- package/dist-server/server/routes/agent.js +3 -4
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/auth.js +75 -13
- package/dist-server/server/routes/auth.js.map +1 -1
- package/dist-server/server/routes/codex.js +1 -1
- package/dist-server/server/routes/codex.js.map +1 -1
- package/dist-server/server/routes/diagnostics.js +6 -3
- package/dist-server/server/routes/diagnostics.js.map +1 -1
- package/dist-server/server/routes/gemini.js +1 -1
- package/dist-server/server/routes/gemini.js.map +1 -1
- package/dist-server/server/routes/git.js +9 -9
- package/dist-server/server/routes/git.js.map +1 -1
- package/dist-server/server/routes/live-view.js +2 -2
- package/dist-server/server/routes/live-view.js.map +1 -1
- package/dist-server/server/routes/plugins.js +12 -12
- package/dist-server/server/routes/plugins.js.map +1 -1
- package/dist-server/server/routes/projects.js +2 -2
- package/dist-server/server/routes/projects.js.map +1 -1
- package/dist-server/server/routes/qwen.js +1 -1
- package/dist-server/server/routes/qwen.js.map +1 -1
- package/dist-server/server/routes/remote.js +1 -1
- package/dist-server/server/routes/remote.js.map +1 -1
- package/dist-server/server/routes/webhooks.js +1 -1
- package/dist-server/server/routes/webhooks.js.map +1 -1
- package/dist-server/server/services/startup-update.js +31 -6
- package/dist-server/server/services/startup-update.js.map +1 -1
- package/dist-server/server/services/telegram/bot.js +6 -4
- package/dist-server/server/services/telegram/bot.js.map +1 -1
- package/dist-server/server/setup-wizard.js +246 -0
- package/dist-server/server/setup-wizard.js.map +1 -0
- package/dist-server/server/utils/plugin-loader.js +3 -0
- package/dist-server/server/utils/plugin-loader.js.map +1 -1
- package/dist-server/server/utils/port-access.js +25 -8
- package/dist-server/server/utils/port-access.js.map +1 -1
- package/dist-server/server/utils/security-log.js +89 -0
- package/dist-server/server/utils/security-log.js.map +1 -0
- package/package.json +1 -1
- package/server/claude-sdk.js +7 -0
- package/server/cli.js +67 -0
- package/server/index.js +287 -55
- package/server/middleware/account-lockout.js +112 -0
- package/server/middleware/auth.js +72 -9
- package/server/middleware/rate-limiter.js +82 -0
- package/server/routes/agent.js +3 -4
- package/server/routes/auth.js +86 -13
- package/server/routes/codex.js +1 -1
- package/server/routes/diagnostics.js +6 -3
- package/server/routes/gemini.js +1 -1
- package/server/routes/git.js +9 -9
- package/server/routes/live-view.js +2 -2
- package/server/routes/plugins.js +12 -12
- package/server/routes/projects.js +2 -2
- package/server/routes/qwen.js +1 -1
- package/server/routes/remote.js +1 -1
- package/server/routes/webhooks.js +1 -1
- package/server/services/startup-update.js +31 -6
- package/server/services/telegram/bot.js +6 -4
- package/server/setup-wizard.js +263 -0
- package/server/utils/plugin-loader.js +4 -0
- package/server/utils/port-access.js +26 -8
- package/server/utils/security-log.js +84 -0
|
@@ -2,7 +2,7 @@ import os from 'os';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import readline from 'readline';
|
|
5
|
-
import { execSync } from 'child_process';
|
|
5
|
+
import { execSync, spawnSync } from 'child_process';
|
|
6
6
|
|
|
7
7
|
import { c } from './colors.js';
|
|
8
8
|
|
|
@@ -65,7 +65,7 @@ function tryUfw(port) {
|
|
|
65
65
|
const status = execSync('ufw status', { stdio: 'pipe', encoding: 'utf8' });
|
|
66
66
|
// UFW inactive = no rule needed; the port is already reachable.
|
|
67
67
|
if (!/active/i.test(status)) return 'inactive';
|
|
68
|
-
execSync(
|
|
68
|
+
execSync('sudo -n ufw allow ' + String(port) + '/tcp', { stdio: 'pipe' });
|
|
69
69
|
return 'applied';
|
|
70
70
|
} catch {
|
|
71
71
|
return null;
|
|
@@ -77,7 +77,7 @@ function tryFirewalld(port) {
|
|
|
77
77
|
execSync('command -v firewall-cmd', { stdio: 'pipe' });
|
|
78
78
|
const state = execSync('firewall-cmd --state', { stdio: 'pipe', encoding: 'utf8' });
|
|
79
79
|
if (!/running/i.test(state)) return 'inactive';
|
|
80
|
-
execSync(
|
|
80
|
+
execSync('sudo -n firewall-cmd --add-port=' + String(port) + '/tcp --permanent', { stdio: 'pipe' });
|
|
81
81
|
execSync('sudo -n firewall-cmd --reload', { stdio: 'pipe' });
|
|
82
82
|
return 'applied';
|
|
83
83
|
} catch {
|
|
@@ -89,12 +89,19 @@ function tryFirewalld(port) {
|
|
|
89
89
|
|
|
90
90
|
function applyWindowsRule(port) {
|
|
91
91
|
const ruleName = `Pixcode-${port}`;
|
|
92
|
-
|
|
92
|
+
// Use spawnSync with an argument array instead of execSync with a shell
|
|
93
|
+
// string to prevent command injection. Port is validated as an integer
|
|
94
|
+
// at the entry point, but this is defense-in-depth.
|
|
93
95
|
try {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
spawnSync('netsh', [
|
|
97
|
+
'advfirewall', 'firewall', 'add', 'rule',
|
|
98
|
+
`name=${ruleName}`,
|
|
99
|
+
'dir=in', 'action=allow', 'protocol=TCP',
|
|
100
|
+
`localport=${port}`,
|
|
101
|
+
], { stdio: 'pipe', shell: false });
|
|
102
|
+
return { ok: true, ruleName };
|
|
96
103
|
} catch (error) {
|
|
97
|
-
return { ok: false,
|
|
104
|
+
return { ok: false, ruleName, error: error.message };
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
107
|
|
|
@@ -125,6 +132,16 @@ function macFirewallHint(port) {
|
|
|
125
132
|
* without any firewall change.
|
|
126
133
|
*/
|
|
127
134
|
export async function ensurePortOpen(port) {
|
|
135
|
+
// Validate port is a safe integer to prevent command injection via the
|
|
136
|
+
// firewall commands below. The caller passes SERVER_PORT which comes from
|
|
137
|
+
// env, so this is defense-in-depth against a tampered environment.
|
|
138
|
+
const safePort = Number.parseInt(port, 10);
|
|
139
|
+
if (!Number.isInteger(safePort) || safePort < 1 || safePort > 65535) {
|
|
140
|
+
console.log(`${c.dim('[INFO]')} Skipping firewall setup: invalid port value.`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
port = safePort;
|
|
144
|
+
|
|
128
145
|
const state = loadState();
|
|
129
146
|
const key = `port:${port}`;
|
|
130
147
|
const entry = state[key];
|
|
@@ -185,8 +202,9 @@ export async function ensurePortOpen(port) {
|
|
|
185
202
|
console.log(`${c.ok('[OK]')} Firewall rule "${result.ruleName}" added.`);
|
|
186
203
|
saveState({ ...state, [key]: { decision: 'allow', status: 'applied', via: 'windows' } });
|
|
187
204
|
} else {
|
|
205
|
+
const manualCmd = `netsh advfirewall firewall add rule name="Pixcode-${port}" dir=in action=allow protocol=TCP localport=${port}`;
|
|
188
206
|
console.log(`${c.tip('[TIP]')} Adding the rule needs Administrator. Run this in an elevated PowerShell:`);
|
|
189
|
-
console.log(` ${c.bright(
|
|
207
|
+
console.log(` ${c.bright(manualCmd)}`);
|
|
190
208
|
saveState({ ...state, [key]: { decision: 'allow', status: 'manual' } });
|
|
191
209
|
}
|
|
192
210
|
return;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
|
|
5
|
+
const LOG_DIR = path.join(os.homedir(), '.pixcode', 'logs');
|
|
6
|
+
const SECURITY_LOG_PATH = path.join(LOG_DIR, 'security.log');
|
|
7
|
+
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
8
|
+
const MAX_LOG_FILES = 5;
|
|
9
|
+
|
|
10
|
+
function ensureLogDir() {
|
|
11
|
+
try {
|
|
12
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
13
|
+
fs.mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
// Non-fatal: if we can't create the log dir, we fall back to console
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function sanitizeForLog(value) {
|
|
21
|
+
if (typeof value !== 'string') {
|
|
22
|
+
try {
|
|
23
|
+
value = String(value ?? '');
|
|
24
|
+
} catch {
|
|
25
|
+
return '[unserializable]';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return value
|
|
29
|
+
.replace(/[\r\n\t]/g, ' ')
|
|
30
|
+
.replace(/\x1b\[[0-9;]*m/g, '')
|
|
31
|
+
.slice(0, 500);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function rotateLogIfNeeded() {
|
|
35
|
+
try {
|
|
36
|
+
if (!fs.existsSync(SECURITY_LOG_PATH)) return;
|
|
37
|
+
const stats = fs.statSync(SECURITY_LOG_PATH);
|
|
38
|
+
if (stats.size < MAX_LOG_SIZE) return;
|
|
39
|
+
for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
|
|
40
|
+
const oldPath = `${SECURITY_LOG_PATH}.${i}`;
|
|
41
|
+
const newPath = `${SECURITY_LOG_PATH}.${i + 1}`;
|
|
42
|
+
if (fs.existsSync(oldPath)) {
|
|
43
|
+
try { fs.renameSync(oldPath, newPath); } catch { /* noop */ }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
try { fs.renameSync(SECURITY_LOG_PATH, `${SECURITY_LOG_PATH}.1`); } catch { /* noop */ }
|
|
47
|
+
} catch {
|
|
48
|
+
// Non-fatal
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function securityLog(event, details = {}) {
|
|
53
|
+
const entry = {
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
event,
|
|
56
|
+
ip: sanitizeForLog(details.ip),
|
|
57
|
+
userId: details.userId != null ? String(details.userId) : null,
|
|
58
|
+
username: details.username ? sanitizeForLog(details.username) : null,
|
|
59
|
+
endpoint: details.endpoint ? sanitizeForLog(details.endpoint) : null,
|
|
60
|
+
method: details.method ? sanitizeForLog(details.method) : null,
|
|
61
|
+
statusCode: details.statusCode || null,
|
|
62
|
+
reason: details.reason ? sanitizeForLog(details.reason) : null,
|
|
63
|
+
userAgent: details.userAgent ? sanitizeForLog(details.userAgent) : null,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const line = JSON.stringify(entry);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
ensureLogDir();
|
|
70
|
+
rotateLogIfNeeded();
|
|
71
|
+
fs.appendFileSync(SECURITY_LOG_PATH, line + '\n', { mode: 0o600 });
|
|
72
|
+
} catch {
|
|
73
|
+
// Fall back to console if file logging fails
|
|
74
|
+
console.warn('[SECURITY]', line);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getClientIp(req) {
|
|
79
|
+
const forwarded = req?.headers?.['x-forwarded-for'];
|
|
80
|
+
if (typeof forwarded === 'string' && forwarded.trim()) {
|
|
81
|
+
return forwarded.split(',')[0].trim();
|
|
82
|
+
}
|
|
83
|
+
return req?.socket?.remoteAddress || req?.ip || 'unknown';
|
|
84
|
+
}
|