craftclose 0.1.0
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/LICENSE +21 -0
- package/README.md +176 -0
- package/SKILL.md +139 -0
- package/craftclose.example.yml +84 -0
- package/dist/ai-analyzer.d.ts +96 -0
- package/dist/ai-analyzer.d.ts.map +1 -0
- package/dist/ai-analyzer.js +275 -0
- package/dist/ai-analyzer.js.map +1 -0
- package/dist/alerts/discord.d.ts +23 -0
- package/dist/alerts/discord.d.ts.map +1 -0
- package/dist/alerts/discord.js +95 -0
- package/dist/alerts/discord.js.map +1 -0
- package/dist/alerts/index.d.ts +5 -0
- package/dist/alerts/index.d.ts.map +1 -0
- package/dist/alerts/index.js +8 -0
- package/dist/alerts/index.js.map +1 -0
- package/dist/alerts/telegram.d.ts +25 -0
- package/dist/alerts/telegram.d.ts.map +1 -0
- package/dist/alerts/telegram.js +72 -0
- package/dist/alerts/telegram.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +410 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/connectors/index.d.ts +8 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +14 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/parsers.d.ts +24 -0
- package/dist/connectors/parsers.d.ts.map +1 -0
- package/dist/connectors/parsers.js +64 -0
- package/dist/connectors/parsers.js.map +1 -0
- package/dist/connectors/pterodactyl.d.ts +90 -0
- package/dist/connectors/pterodactyl.d.ts.map +1 -0
- package/dist/connectors/pterodactyl.js +221 -0
- package/dist/connectors/pterodactyl.js.map +1 -0
- package/dist/connectors/rcon.d.ts +51 -0
- package/dist/connectors/rcon.d.ts.map +1 -0
- package/dist/connectors/rcon.js +95 -0
- package/dist/connectors/rcon.js.map +1 -0
- package/dist/connectors/ssh.d.ts +65 -0
- package/dist/connectors/ssh.d.ts.map +1 -0
- package/dist/connectors/ssh.js +273 -0
- package/dist/connectors/ssh.js.map +1 -0
- package/dist/craft-close-skill.d.ts +106 -0
- package/dist/craft-close-skill.d.ts.map +1 -0
- package/dist/craft-close-skill.js +604 -0
- package/dist/craft-close-skill.js.map +1 -0
- package/dist/history.d.ts +32 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +194 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/openclaw-entry.d.ts +29 -0
- package/dist/openclaw-entry.d.ts.map +1 -0
- package/dist/openclaw-entry.js +45 -0
- package/dist/openclaw-entry.js.map +1 -0
- package/dist/patterns/index.d.ts +32 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +248 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/security/action-allowlist.d.ts +21 -0
- package/dist/security/action-allowlist.d.ts.map +1 -0
- package/dist/security/action-allowlist.js +54 -0
- package/dist/security/action-allowlist.js.map +1 -0
- package/dist/security/audit-log.d.ts +22 -0
- package/dist/security/audit-log.d.ts.map +1 -0
- package/dist/security/audit-log.js +49 -0
- package/dist/security/audit-log.js.map +1 -0
- package/dist/security/index.d.ts +10 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +20 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/input-sanitizer.d.ts +32 -0
- package/dist/security/input-sanitizer.d.ts.map +1 -0
- package/dist/security/input-sanitizer.js +81 -0
- package/dist/security/input-sanitizer.js.map +1 -0
- package/dist/security/path-jail.d.ts +25 -0
- package/dist/security/path-jail.d.ts.map +1 -0
- package/dist/security/path-jail.js +73 -0
- package/dist/security/path-jail.js.map +1 -0
- package/dist/security/rcon-filter.d.ts +24 -0
- package/dist/security/rcon-filter.d.ts.map +1 -0
- package/dist/security/rcon-filter.js +76 -0
- package/dist/security/rcon-filter.js.map +1 -0
- package/dist/types.d.ts +154 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action Allowlist — Controls which config files can be read/written
|
|
3
|
+
* and enforces confirmation for destructive operations.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ActionAllowlist {
|
|
6
|
+
private readonly allowedFiles;
|
|
7
|
+
private readonly confirmDestructive;
|
|
8
|
+
constructor(opts?: {
|
|
9
|
+
allowedFiles?: string[];
|
|
10
|
+
confirmDestructive?: boolean;
|
|
11
|
+
});
|
|
12
|
+
/** Check if a config file is on the allowlist. */
|
|
13
|
+
isFileAllowed(filename: string): boolean;
|
|
14
|
+
/** Check if an action is destructive and requires confirmation. */
|
|
15
|
+
isDestructive(action: string): boolean;
|
|
16
|
+
/** Whether destructive actions need confirmation. */
|
|
17
|
+
requiresConfirmation(action: string): boolean;
|
|
18
|
+
/** Get the full set of allowed files. */
|
|
19
|
+
getAllowedFiles(): ReadonlySet<string>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=action-allowlist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-allowlist.d.ts","sourceRoot":"","sources":["../../src/security/action-allowlist.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwBH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;gBAEjC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAE;IAK5E,kDAAkD;IAClD,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKxC,mEAAmE;IACnE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAItC,qDAAqD;IACrD,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI7C,yCAAyC;IACzC,eAAe,IAAI,WAAW,CAAC,MAAM,CAAC;CAGvC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Action Allowlist — Controls which config files can be read/written
|
|
4
|
+
* and enforces confirmation for destructive operations.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ActionAllowlist = void 0;
|
|
8
|
+
/** Default config files that are safe to read/edit */
|
|
9
|
+
const DEFAULT_ALLOWED_FILES = new Set([
|
|
10
|
+
"server.properties",
|
|
11
|
+
"bukkit.yml",
|
|
12
|
+
"spigot.yml",
|
|
13
|
+
"paper-global.yml",
|
|
14
|
+
"paper-world-defaults.yml",
|
|
15
|
+
"purpur.yml",
|
|
16
|
+
"config/paper-global.yml",
|
|
17
|
+
"config/paper-world-defaults.yml",
|
|
18
|
+
]);
|
|
19
|
+
/** Actions considered destructive and requiring confirmation */
|
|
20
|
+
const DESTRUCTIVE_ACTIONS = new Set([
|
|
21
|
+
"restart",
|
|
22
|
+
"stop",
|
|
23
|
+
"delete_file",
|
|
24
|
+
"write_config",
|
|
25
|
+
"restore_backup",
|
|
26
|
+
"clear_world",
|
|
27
|
+
]);
|
|
28
|
+
class ActionAllowlist {
|
|
29
|
+
allowedFiles;
|
|
30
|
+
confirmDestructive;
|
|
31
|
+
constructor(opts) {
|
|
32
|
+
this.allowedFiles = new Set(opts?.allowedFiles ?? DEFAULT_ALLOWED_FILES);
|
|
33
|
+
this.confirmDestructive = opts?.confirmDestructive ?? true;
|
|
34
|
+
}
|
|
35
|
+
/** Check if a config file is on the allowlist. */
|
|
36
|
+
isFileAllowed(filename) {
|
|
37
|
+
const normalized = filename.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
38
|
+
return this.allowedFiles.has(normalized);
|
|
39
|
+
}
|
|
40
|
+
/** Check if an action is destructive and requires confirmation. */
|
|
41
|
+
isDestructive(action) {
|
|
42
|
+
return DESTRUCTIVE_ACTIONS.has(action);
|
|
43
|
+
}
|
|
44
|
+
/** Whether destructive actions need confirmation. */
|
|
45
|
+
requiresConfirmation(action) {
|
|
46
|
+
return this.confirmDestructive && this.isDestructive(action);
|
|
47
|
+
}
|
|
48
|
+
/** Get the full set of allowed files. */
|
|
49
|
+
getAllowedFiles() {
|
|
50
|
+
return this.allowedFiles;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.ActionAllowlist = ActionAllowlist;
|
|
54
|
+
//# sourceMappingURL=action-allowlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-allowlist.js","sourceRoot":"","sources":["../../src/security/action-allowlist.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,sDAAsD;AACtD,MAAM,qBAAqB,GAAwB,IAAI,GAAG,CAAC;IACzD,mBAAmB;IACnB,YAAY;IACZ,YAAY;IACZ,kBAAkB;IAClB,0BAA0B;IAC1B,YAAY;IACZ,yBAAyB;IACzB,iCAAiC;CAClC,CAAC,CAAC;AAEH,gEAAgE;AAChE,MAAM,mBAAmB,GAAwB,IAAI,GAAG,CAAC;IACvD,SAAS;IACT,MAAM;IACN,aAAa;IACb,cAAc;IACd,gBAAgB;IAChB,aAAa;CACd,CAAC,CAAC;AAEH,MAAa,eAAe;IACT,YAAY,CAAc;IAC1B,kBAAkB,CAAU;IAE7C,YAAY,IAAgE;QAC1E,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,YAAY,IAAI,qBAAqB,CAAC,CAAC;QACzE,IAAI,CAAC,kBAAkB,GAAG,IAAI,EAAE,kBAAkB,IAAI,IAAI,CAAC;IAC7D,CAAC;IAED,kDAAkD;IAClD,aAAa,CAAC,QAAgB;QAC5B,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3C,CAAC;IAED,mEAAmE;IACnE,aAAa,CAAC,MAAc;QAC1B,OAAO,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,qDAAqD;IACrD,oBAAoB,CAAC,MAAc;QACjC,OAAO,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IAED,yCAAyC;IACzC,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;CACF;AA7BD,0CA6BC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Log — Records security-relevant actions for review.
|
|
3
|
+
* Writes structured JSON entries to an append-only log file.
|
|
4
|
+
*/
|
|
5
|
+
export interface AuditEntry {
|
|
6
|
+
timestamp: string;
|
|
7
|
+
action: string;
|
|
8
|
+
server?: string;
|
|
9
|
+
detail: string;
|
|
10
|
+
allowed: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class AuditLog {
|
|
13
|
+
private readonly logPath;
|
|
14
|
+
private readonly entries;
|
|
15
|
+
constructor(logPath?: string);
|
|
16
|
+
/** Record an action to the audit log */
|
|
17
|
+
record(action: string, detail: string, allowed: boolean, server?: string): void;
|
|
18
|
+
/** Get recent in-memory entries. */
|
|
19
|
+
getRecent(limit?: number): AuditEntry[];
|
|
20
|
+
private flush;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=audit-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.d.ts","sourceRoot":"","sources":["../../src/security/audit-log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAEhC,OAAO,GAAE,MAAmC;IAUxD,wCAAwC;IACxC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAY/E,oCAAoC;IACpC,SAAS,CAAC,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAI3C,OAAO,CAAC,KAAK;CAOd"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Audit Log — Records security-relevant actions for review.
|
|
4
|
+
* Writes structured JSON entries to an append-only log file.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AuditLog = void 0;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
class AuditLog {
|
|
11
|
+
logPath;
|
|
12
|
+
entries = [];
|
|
13
|
+
constructor(logPath = "./craftclose-audit.jsonl") {
|
|
14
|
+
this.logPath = logPath;
|
|
15
|
+
// Ensure directory exists
|
|
16
|
+
try {
|
|
17
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.logPath), { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Directory already exists or is current dir
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Record an action to the audit log */
|
|
24
|
+
record(action, detail, allowed, server) {
|
|
25
|
+
const entry = {
|
|
26
|
+
timestamp: new Date().toISOString(),
|
|
27
|
+
action,
|
|
28
|
+
server,
|
|
29
|
+
detail,
|
|
30
|
+
allowed,
|
|
31
|
+
};
|
|
32
|
+
this.entries.push(entry);
|
|
33
|
+
this.flush(entry);
|
|
34
|
+
}
|
|
35
|
+
/** Get recent in-memory entries. */
|
|
36
|
+
getRecent(limit = 50) {
|
|
37
|
+
return this.entries.slice(-limit);
|
|
38
|
+
}
|
|
39
|
+
flush(entry) {
|
|
40
|
+
try {
|
|
41
|
+
(0, node_fs_1.appendFileSync)(this.logPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Best-effort — don't crash if log write fails
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.AuditLog = AuditLog;
|
|
49
|
+
//# sourceMappingURL=audit-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.js","sourceRoot":"","sources":["../../src/security/audit-log.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,qCAAoD;AACpD,yCAAoC;AAUpC,MAAa,QAAQ;IACF,OAAO,CAAS;IAChB,OAAO,GAAiB,EAAE,CAAC;IAE5C,YAAY,UAAkB,0BAA0B;QACtD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,0BAA0B;QAC1B,IAAI,CAAC;YACH,IAAA,mBAAS,EAAC,IAAA,mBAAO,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,6CAA6C;QAC/C,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,MAAM,CAAC,MAAc,EAAE,MAAc,EAAE,OAAgB,EAAE,MAAe;QACtE,MAAM,KAAK,GAAe;YACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,MAAM;YACN,MAAM;YACN,MAAM;YACN,OAAO;SACR,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAED,oCAAoC;IACpC,SAAS,CAAC,QAAgB,EAAE;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAEO,KAAK,CAAC,KAAiB;QAC7B,IAAI,CAAC;YACH,IAAA,wBAAc,EAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;IACH,CAAC;CACF;AAvCD,4BAuCC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security module barrel export.
|
|
3
|
+
*/
|
|
4
|
+
export { PathJail, PathJailError } from "./path-jail.js";
|
|
5
|
+
export { RconFilter, RconFilterError } from "./rcon-filter.js";
|
|
6
|
+
export { InputSanitizer, SanitizeError } from "./input-sanitizer.js";
|
|
7
|
+
export { AuditLog } from "./audit-log.js";
|
|
8
|
+
export type { AuditEntry } from "./audit-log.js";
|
|
9
|
+
export { ActionAllowlist } from "./action-allowlist.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security module barrel export.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ActionAllowlist = exports.AuditLog = exports.SanitizeError = exports.InputSanitizer = exports.RconFilterError = exports.RconFilter = exports.PathJailError = exports.PathJail = void 0;
|
|
7
|
+
var path_jail_js_1 = require("./path-jail.js");
|
|
8
|
+
Object.defineProperty(exports, "PathJail", { enumerable: true, get: function () { return path_jail_js_1.PathJail; } });
|
|
9
|
+
Object.defineProperty(exports, "PathJailError", { enumerable: true, get: function () { return path_jail_js_1.PathJailError; } });
|
|
10
|
+
var rcon_filter_js_1 = require("./rcon-filter.js");
|
|
11
|
+
Object.defineProperty(exports, "RconFilter", { enumerable: true, get: function () { return rcon_filter_js_1.RconFilter; } });
|
|
12
|
+
Object.defineProperty(exports, "RconFilterError", { enumerable: true, get: function () { return rcon_filter_js_1.RconFilterError; } });
|
|
13
|
+
var input_sanitizer_js_1 = require("./input-sanitizer.js");
|
|
14
|
+
Object.defineProperty(exports, "InputSanitizer", { enumerable: true, get: function () { return input_sanitizer_js_1.InputSanitizer; } });
|
|
15
|
+
Object.defineProperty(exports, "SanitizeError", { enumerable: true, get: function () { return input_sanitizer_js_1.SanitizeError; } });
|
|
16
|
+
var audit_log_js_1 = require("./audit-log.js");
|
|
17
|
+
Object.defineProperty(exports, "AuditLog", { enumerable: true, get: function () { return audit_log_js_1.AuditLog; } });
|
|
18
|
+
var action_allowlist_js_1 = require("./action-allowlist.js");
|
|
19
|
+
Object.defineProperty(exports, "ActionAllowlist", { enumerable: true, get: function () { return action_allowlist_js_1.ActionAllowlist; } });
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,+CAAyD;AAAhD,wGAAA,QAAQ,OAAA;AAAE,6GAAA,aAAa,OAAA;AAChC,mDAA+D;AAAtD,4GAAA,UAAU,OAAA;AAAE,iHAAA,eAAe,OAAA;AACpC,2DAAqE;AAA5D,oHAAA,cAAc,OAAA;AAAE,mHAAA,aAAa,OAAA;AACtC,+CAA0C;AAAjC,wGAAA,QAAQ,OAAA;AAEjB,6DAAwD;AAA/C,sHAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Sanitizer — Cleans user-provided strings before use in
|
|
3
|
+
* shell commands, RCON commands, or API calls.
|
|
4
|
+
*/
|
|
5
|
+
export declare class InputSanitizer {
|
|
6
|
+
/**
|
|
7
|
+
* Sanitize a string for use as an RCON command argument.
|
|
8
|
+
* Strips dangerous characters and enforces length limit.
|
|
9
|
+
*/
|
|
10
|
+
static rcon(input: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize a string for use in a shell command argument.
|
|
13
|
+
* Strips dangerous characters and enforces length limit.
|
|
14
|
+
*/
|
|
15
|
+
static shell(input: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Sanitize a player name. Minecraft names are 3-16 alphanumeric + underscore.
|
|
18
|
+
*/
|
|
19
|
+
static playerName(input: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Sanitize a server name. Alphanumeric, hyphens, underscores, dots.
|
|
22
|
+
*/
|
|
23
|
+
static serverName(input: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Validate a string contains only safe log-query characters.
|
|
26
|
+
*/
|
|
27
|
+
static logQuery(input: string): string;
|
|
28
|
+
}
|
|
29
|
+
export declare class SanitizeError extends Error {
|
|
30
|
+
constructor(message: string);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=input-sanitizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-sanitizer.d.ts","sourceRoot":"","sources":["../../src/security/input-sanitizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,qBAAa,cAAc;IACzB;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQlC;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQnC;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAWxC;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAWxC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAMvC;AAED,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Input Sanitizer — Cleans user-provided strings before use in
|
|
4
|
+
* shell commands, RCON commands, or API calls.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SanitizeError = exports.InputSanitizer = void 0;
|
|
8
|
+
/** Max length for any single input string */
|
|
9
|
+
const MAX_INPUT_LENGTH = 1024;
|
|
10
|
+
/** Characters that must never appear in RCON commands */
|
|
11
|
+
const RCON_DANGEROUS = /[;&|`$(){}[\]<>!\\]/g;
|
|
12
|
+
/** Characters that must never appear in shell arguments */
|
|
13
|
+
const SHELL_DANGEROUS = /[;&|`$(){}[\]<>!\\'"\n\r\t]/g;
|
|
14
|
+
class InputSanitizer {
|
|
15
|
+
/**
|
|
16
|
+
* Sanitize a string for use as an RCON command argument.
|
|
17
|
+
* Strips dangerous characters and enforces length limit.
|
|
18
|
+
*/
|
|
19
|
+
static rcon(input) {
|
|
20
|
+
if (typeof input !== "string") {
|
|
21
|
+
throw new SanitizeError("Input must be a string");
|
|
22
|
+
}
|
|
23
|
+
const trimmed = input.trim().slice(0, MAX_INPUT_LENGTH);
|
|
24
|
+
return trimmed.replace(RCON_DANGEROUS, "");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Sanitize a string for use in a shell command argument.
|
|
28
|
+
* Strips dangerous characters and enforces length limit.
|
|
29
|
+
*/
|
|
30
|
+
static shell(input) {
|
|
31
|
+
if (typeof input !== "string") {
|
|
32
|
+
throw new SanitizeError("Input must be a string");
|
|
33
|
+
}
|
|
34
|
+
const trimmed = input.trim().slice(0, MAX_INPUT_LENGTH);
|
|
35
|
+
return trimmed.replace(SHELL_DANGEROUS, "");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Sanitize a player name. Minecraft names are 3-16 alphanumeric + underscore.
|
|
39
|
+
*/
|
|
40
|
+
static playerName(input) {
|
|
41
|
+
if (typeof input !== "string") {
|
|
42
|
+
throw new SanitizeError("Input must be a string");
|
|
43
|
+
}
|
|
44
|
+
const cleaned = input.trim().replace(/[^a-zA-Z0-9_]/g, "");
|
|
45
|
+
if (cleaned.length < 3 || cleaned.length > 16) {
|
|
46
|
+
throw new SanitizeError(`Invalid player name: ${input}`);
|
|
47
|
+
}
|
|
48
|
+
return cleaned;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Sanitize a server name. Alphanumeric, hyphens, underscores, dots.
|
|
52
|
+
*/
|
|
53
|
+
static serverName(input) {
|
|
54
|
+
if (typeof input !== "string") {
|
|
55
|
+
throw new SanitizeError("Input must be a string");
|
|
56
|
+
}
|
|
57
|
+
const cleaned = input.trim().replace(/[^a-zA-Z0-9._-]/g, "");
|
|
58
|
+
if (cleaned.length === 0 || cleaned.length > 64) {
|
|
59
|
+
throw new SanitizeError(`Invalid server name: ${input}`);
|
|
60
|
+
}
|
|
61
|
+
return cleaned;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate a string contains only safe log-query characters.
|
|
65
|
+
*/
|
|
66
|
+
static logQuery(input) {
|
|
67
|
+
if (typeof input !== "string") {
|
|
68
|
+
throw new SanitizeError("Input must be a string");
|
|
69
|
+
}
|
|
70
|
+
return input.trim().slice(0, MAX_INPUT_LENGTH);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.InputSanitizer = InputSanitizer;
|
|
74
|
+
class SanitizeError extends Error {
|
|
75
|
+
constructor(message) {
|
|
76
|
+
super(message);
|
|
77
|
+
this.name = "SanitizeError";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.SanitizeError = SanitizeError;
|
|
81
|
+
//# sourceMappingURL=input-sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-sanitizer.js","sourceRoot":"","sources":["../../src/security/input-sanitizer.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6CAA6C;AAC7C,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,yDAAyD;AACzD,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAE9C,2DAA2D;AAC3D,MAAM,eAAe,GAAG,8BAA8B,CAAC;AAEvD,MAAa,cAAc;IACzB;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,KAAa;QACvB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,aAAa,CAAC,wBAAwB,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACxD,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,KAAa;QACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,aAAa,CAAC,wBAAwB,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACxD,OAAO,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,KAAa;QAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,aAAa,CAAC,wBAAwB,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;QAC3D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC9C,MAAM,IAAI,aAAa,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,KAAa;QAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,aAAa,CAAC,wBAAwB,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAC7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAChD,MAAM,IAAI,aAAa,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,KAAa;QAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,aAAa,CAAC,wBAAwB,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACjD,CAAC;CACF;AA9DD,wCA8DC;AAED,MAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AALD,sCAKC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Jail — Restricts file access to the Minecraft server directory.
|
|
3
|
+
* Prevents path traversal attacks via .. or symlinks.
|
|
4
|
+
*/
|
|
5
|
+
export declare class PathJail {
|
|
6
|
+
private readonly root;
|
|
7
|
+
constructor(serverRoot: string);
|
|
8
|
+
/**
|
|
9
|
+
* Returns the resolved absolute path if it is inside the jail.
|
|
10
|
+
* Throws if the path escapes the server root.
|
|
11
|
+
*/
|
|
12
|
+
resolve(untrustedPath: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Same as resolve, but also follows symlinks and verifies the real path
|
|
15
|
+
* is still inside the jail. Use for read operations on existing files.
|
|
16
|
+
*/
|
|
17
|
+
resolveReal(untrustedPath: string): string;
|
|
18
|
+
/** Check if a path is inside the jail without throwing. */
|
|
19
|
+
isAllowed(untrustedPath: string): boolean;
|
|
20
|
+
getRoot(): string;
|
|
21
|
+
}
|
|
22
|
+
export declare class PathJailError extends Error {
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=path-jail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-jail.d.ts","sourceRoot":"","sources":["../../src/security/path-jail.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,UAAU,EAAE,MAAM;IAI9B;;;OAGG;IACH,OAAO,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM;IAetC;;;OAGG;IACH,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM;IAmB1C,2DAA2D;IAC3D,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IASzC,OAAO,IAAI,MAAM;CAGlB;AAED,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Path Jail — Restricts file access to the Minecraft server directory.
|
|
4
|
+
* Prevents path traversal attacks via .. or symlinks.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.PathJailError = exports.PathJail = void 0;
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
class PathJail {
|
|
11
|
+
root;
|
|
12
|
+
constructor(serverRoot) {
|
|
13
|
+
this.root = (0, node_path_1.resolve)(serverRoot);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Returns the resolved absolute path if it is inside the jail.
|
|
17
|
+
* Throws if the path escapes the server root.
|
|
18
|
+
*/
|
|
19
|
+
resolve(untrustedPath) {
|
|
20
|
+
// Normalize and resolve against root
|
|
21
|
+
const candidate = (0, node_path_1.isAbsolute)(untrustedPath)
|
|
22
|
+
? (0, node_path_1.resolve)(untrustedPath)
|
|
23
|
+
: (0, node_path_1.resolve)(this.root, untrustedPath);
|
|
24
|
+
// Check the resolved path is under root
|
|
25
|
+
const rel = (0, node_path_1.relative)(this.root, candidate);
|
|
26
|
+
if (rel.startsWith("..") || (0, node_path_1.isAbsolute)(rel)) {
|
|
27
|
+
throw new PathJailError(`Path escapes server root: ${untrustedPath}`);
|
|
28
|
+
}
|
|
29
|
+
return candidate;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Same as resolve, but also follows symlinks and verifies the real path
|
|
33
|
+
* is still inside the jail. Use for read operations on existing files.
|
|
34
|
+
*/
|
|
35
|
+
resolveReal(untrustedPath) {
|
|
36
|
+
const candidate = this.resolve(untrustedPath);
|
|
37
|
+
let real;
|
|
38
|
+
try {
|
|
39
|
+
real = (0, node_fs_1.realpathSync)(candidate);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// File doesn't exist yet — resolved path check is sufficient
|
|
43
|
+
return candidate;
|
|
44
|
+
}
|
|
45
|
+
const rel = (0, node_path_1.relative)(this.root, real);
|
|
46
|
+
if (rel.startsWith("..") || (0, node_path_1.isAbsolute)(rel)) {
|
|
47
|
+
throw new PathJailError(`Symlink escapes server root: ${untrustedPath} → ${real}`);
|
|
48
|
+
}
|
|
49
|
+
return real;
|
|
50
|
+
}
|
|
51
|
+
/** Check if a path is inside the jail without throwing. */
|
|
52
|
+
isAllowed(untrustedPath) {
|
|
53
|
+
try {
|
|
54
|
+
this.resolve(untrustedPath);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
getRoot() {
|
|
62
|
+
return this.root;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.PathJail = PathJail;
|
|
66
|
+
class PathJailError extends Error {
|
|
67
|
+
constructor(message) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "PathJailError";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.PathJailError = PathJailError;
|
|
73
|
+
//# sourceMappingURL=path-jail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-jail.js","sourceRoot":"","sources":["../../src/security/path-jail.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAA0D;AAC1D,qCAAuC;AAEvC,MAAa,QAAQ;IACF,IAAI,CAAS;IAE9B,YAAY,UAAkB;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAA,mBAAO,EAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,aAAqB;QAC3B,qCAAqC;QACrC,MAAM,SAAS,GAAG,IAAA,sBAAU,EAAC,aAAa,CAAC;YACzC,CAAC,CAAC,IAAA,mBAAO,EAAC,aAAa,CAAC;YACxB,CAAC,CAAC,IAAA,mBAAO,EAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAEtC,wCAAwC;QACxC,MAAM,GAAG,GAAG,IAAA,oBAAQ,EAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC3C,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAA,sBAAU,EAAC,GAAG,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,aAAa,CAAC,6BAA6B,aAAa,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,aAAqB;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAE9C,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,IAAA,sBAAY,EAAC,SAAS,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;YAC7D,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAG,IAAA,oBAAQ,EAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACtC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAA,sBAAU,EAAC,GAAG,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,aAAa,CAAC,gCAAgC,aAAa,MAAM,IAAI,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2DAA2D;IAC3D,SAAS,CAAC,aAAqB;QAC7B,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;CACF;AA9DD,4BA8DC;AAED,MAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AALD,sCAKC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RCON Filter — Blocks dangerous console commands before they reach the server.
|
|
3
|
+
* Prevents accidental or malicious execution of destructive commands.
|
|
4
|
+
*/
|
|
5
|
+
export declare class RconFilter {
|
|
6
|
+
private readonly blocked;
|
|
7
|
+
constructor(extraBlocked?: string[]);
|
|
8
|
+
/**
|
|
9
|
+
* Check if a command is allowed.
|
|
10
|
+
* Returns true if the command can be sent to RCON.
|
|
11
|
+
*/
|
|
12
|
+
isAllowed(command: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Validate and return the command if allowed.
|
|
15
|
+
* Throws RconFilterError if the command is blocked.
|
|
16
|
+
*/
|
|
17
|
+
validate(command: string): string;
|
|
18
|
+
/** Get the full set of blocked commands. */
|
|
19
|
+
getBlocked(): ReadonlySet<string>;
|
|
20
|
+
}
|
|
21
|
+
export declare class RconFilterError extends Error {
|
|
22
|
+
constructor(message: string);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=rcon-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rcon-filter.d.ts","sourceRoot":"","sources":["../../src/security/rcon-filter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqBH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;gBAE1B,YAAY,CAAC,EAAE,MAAM,EAAE;IASnC;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAcnC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOjC,4CAA4C;IAC5C,UAAU,IAAI,WAAW,CAAC,MAAM,CAAC;CAGlC;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RCON Filter — Blocks dangerous console commands before they reach the server.
|
|
4
|
+
* Prevents accidental or malicious execution of destructive commands.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RconFilterError = exports.RconFilter = void 0;
|
|
8
|
+
/** Default commands that are always blocked */
|
|
9
|
+
const DEFAULT_BLOCKED = new Set([
|
|
10
|
+
"stop",
|
|
11
|
+
"restart",
|
|
12
|
+
"op",
|
|
13
|
+
"deop",
|
|
14
|
+
"ban",
|
|
15
|
+
"ban-ip",
|
|
16
|
+
"pardon",
|
|
17
|
+
"pardon-ip",
|
|
18
|
+
"kick",
|
|
19
|
+
"whitelist",
|
|
20
|
+
"save-off",
|
|
21
|
+
"debug",
|
|
22
|
+
"reload",
|
|
23
|
+
"reload confirm",
|
|
24
|
+
"rl",
|
|
25
|
+
]);
|
|
26
|
+
class RconFilter {
|
|
27
|
+
blocked;
|
|
28
|
+
constructor(extraBlocked) {
|
|
29
|
+
this.blocked = new Set(DEFAULT_BLOCKED);
|
|
30
|
+
if (extraBlocked) {
|
|
31
|
+
for (const cmd of extraBlocked) {
|
|
32
|
+
this.blocked.add(cmd.toLowerCase());
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if a command is allowed.
|
|
38
|
+
* Returns true if the command can be sent to RCON.
|
|
39
|
+
*/
|
|
40
|
+
isAllowed(command) {
|
|
41
|
+
const normalized = command.trim().toLowerCase();
|
|
42
|
+
if (!normalized)
|
|
43
|
+
return false;
|
|
44
|
+
// Check exact match first
|
|
45
|
+
if (this.blocked.has(normalized))
|
|
46
|
+
return false;
|
|
47
|
+
// Check if the base command (first word) is blocked
|
|
48
|
+
const base = normalized.split(/\s+/)[0];
|
|
49
|
+
if (this.blocked.has(base))
|
|
50
|
+
return false;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validate and return the command if allowed.
|
|
55
|
+
* Throws RconFilterError if the command is blocked.
|
|
56
|
+
*/
|
|
57
|
+
validate(command) {
|
|
58
|
+
if (!this.isAllowed(command)) {
|
|
59
|
+
throw new RconFilterError(`Blocked RCON command: ${command.trim()}`);
|
|
60
|
+
}
|
|
61
|
+
return command.trim();
|
|
62
|
+
}
|
|
63
|
+
/** Get the full set of blocked commands. */
|
|
64
|
+
getBlocked() {
|
|
65
|
+
return this.blocked;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.RconFilter = RconFilter;
|
|
69
|
+
class RconFilterError extends Error {
|
|
70
|
+
constructor(message) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = "RconFilterError";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.RconFilterError = RconFilterError;
|
|
76
|
+
//# sourceMappingURL=rcon-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rcon-filter.js","sourceRoot":"","sources":["../../src/security/rcon-filter.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+CAA+C;AAC/C,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAC;IACnD,MAAM;IACN,SAAS;IACT,IAAI;IACJ,MAAM;IACN,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,WAAW;IACX,MAAM;IACN,WAAW;IACX,UAAU;IACV,OAAO;IACP,QAAQ;IACR,gBAAgB;IAChB,IAAI;CACL,CAAC,CAAC;AAEH,MAAa,UAAU;IACJ,OAAO,CAAc;IAEtC,YAAY,YAAuB;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;QACxC,IAAI,YAAY,EAAE,CAAC;YACjB,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;gBAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,OAAe;QACvB,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAChD,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAE9B,0BAA0B;QAC1B,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,OAAO,KAAK,CAAC;QAE/C,oDAAoD;QACpD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAEzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,OAAe;QACtB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,eAAe,CAAC,yBAAyB,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,4CAA4C;IAC5C,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AA7CD,gCA6CC;AAED,MAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AALD,0CAKC"}
|