@vtstech/pi-security 1.0.3 → 1.0.4-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.
Files changed (3) hide show
  1. package/README.md +28 -0
  2. package/package.json +3 -2
  3. package/security.js +45 -56
package/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # @vtstech/pi-security
2
+
3
+ Security extension for the [Pi Coding Agent](https://github.com/badlogic/pi-mono).
4
+
5
+ Command, path, and network security layer for Pi's tool execution. Automatically loaded — no commands needed.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pi install "npm:@vtstech/pi-security"
11
+ ```
12
+
13
+ ## Protection
14
+
15
+ - **65 blocked commands** — system modification, privilege escalation, network attacks, package management, process control, shell escapes
16
+ - **SSRF protection** — 27 blocked hostname patterns (loopback, RFC1918 private ranges, cloud metadata endpoints)
17
+ - **Path validation** — prevents filesystem escape and access to critical system directories
18
+ - **Shell injection detection** — regex patterns for command chaining, substitution, and redirection
19
+ - **Audit logging** — JSON-lines audit log at `~/.pi/agent/audit.log`
20
+
21
+ ## Links
22
+
23
+ - [Full Documentation](https://github.com/VTSTech/pi-coding-agent#security-securityts)
24
+ - [Changelog](https://github.com/VTSTech/pi-coding-agent/blob/main/CHANGELOG.md)
25
+
26
+ ## License
27
+
28
+ MIT — [VTSTech](https://www.vts-tech.org)
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@vtstech/pi-security",
3
- "version": "1.0.3",
3
+ "version": "1.0.4-1",
4
4
  "description": "Security extension for Pi Coding Agent",
5
5
  "main": "security.js",
6
6
  "keywords": ["pi-package", "pi-extensions"],
7
7
  "license": "MIT",
8
8
  "access": "public",
9
+ "type": "module",
9
10
  "author": "VTSTech",
10
11
  "homepage": "https://www.vts-tech.org",
11
12
  "repository": {
@@ -13,7 +14,7 @@
13
14
  "url": "https://github.com/VTSTech/pi-coding-agent"
14
15
  },
15
16
  "dependencies": {
16
- "@vtstech/pi-shared": "1.0.3"
17
+ "@vtstech/pi-shared": "1.0.4-1"
17
18
  },
18
19
  "peerDependencies": {
19
20
  "@mariozechner/pi-coding-agent": ">=0.66"
package/security.js CHANGED
@@ -1,29 +1,15 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
-
19
1
  // .build-npm/security/security.temp.ts
20
- var security_temp_exports = {};
21
- __export(security_temp_exports, {
22
- default: () => security_temp_default
23
- });
24
- module.exports = __toCommonJS(security_temp_exports);
25
- var import_security = require("@vtstech/pi-shared/security");
26
- var import_format = require("@vtstech/pi-shared/format");
2
+ import {
3
+ checkBashToolInput,
4
+ checkFileToolInput,
5
+ checkHttpToolInput,
6
+ checkInjectionPatterns,
7
+ appendAuditEntry,
8
+ readRecentAuditEntries,
9
+ BLOCKED_COMMANDS,
10
+ BLOCKED_URL_PATTERNS
11
+ } from "@vtstech/pi-shared/security";
12
+ import { section, ok, fail, warn, info } from "@vtstech/pi-shared/format";
27
13
  function security_temp_default(pi) {
28
14
  const stats = {
29
15
  blocked: 0,
@@ -46,7 +32,7 @@ function security_temp_default(pi) {
46
32
  case "bash":
47
33
  case "shell":
48
34
  case "run_command":
49
- result = (0, import_security.checkBashToolInput)(input);
35
+ result = checkBashToolInput(input);
50
36
  break;
51
37
  case "read":
52
38
  case "read_file":
@@ -56,17 +42,17 @@ function security_temp_default(pi) {
56
42
  case "edit_file":
57
43
  case "list_directory":
58
44
  case "list_dir":
59
- result = (0, import_security.checkFileToolInput)(input);
45
+ result = checkFileToolInput(input);
60
46
  break;
61
47
  case "http_get":
62
48
  case "http_post":
63
49
  case "fetch":
64
50
  case "web_search":
65
51
  case "http_request":
66
- result = (0, import_security.checkHttpToolInput)(input);
52
+ result = checkHttpToolInput(input);
67
53
  break;
68
54
  default:
69
- result = (0, import_security.checkInjectionPatterns)(input);
55
+ result = checkInjectionPatterns(input);
70
56
  break;
71
57
  }
72
58
  if (!result.safe) {
@@ -78,7 +64,7 @@ function security_temp_default(pi) {
78
64
  detail: result.detail,
79
65
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
80
66
  };
81
- (0, import_security.appendAuditEntry)({
67
+ appendAuditEntry({
82
68
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
83
69
  toolName,
84
70
  toolCallId,
@@ -95,7 +81,7 @@ function security_temp_default(pi) {
95
81
  stats.allowed++;
96
82
  if (["bash", "shell", "write", "write_file", "edit", "edit_file"].includes(toolName)) {
97
83
  stats.warnings++;
98
- (0, import_security.appendAuditEntry)({
84
+ appendAuditEntry({
99
85
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
100
86
  toolName,
101
87
  toolCallId,
@@ -110,7 +96,7 @@ function security_temp_default(pi) {
110
96
  const toolName = event.toolName;
111
97
  const isError = event.isError;
112
98
  if (isError) {
113
- (0, import_security.appendAuditEntry)({
99
+ appendAuditEntry({
114
100
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
115
101
  toolName,
116
102
  toolCallId: event.toolCallId,
@@ -124,34 +110,34 @@ function security_temp_default(pi) {
124
110
  async function generateAuditReport() {
125
111
  const lines = [];
126
112
  lines.push(branding);
127
- lines.push((0, import_format.section)("SESSION STATISTICS"));
128
- lines.push((0, import_format.info)(`Tool calls allowed: ${stats.allowed}`));
129
- lines.push((0, import_format.info)(`Tool calls blocked: ${stats.blocked}`));
130
- lines.push((0, import_format.info)(`Dangerous operations logged: ${stats.warnings}`));
113
+ lines.push(section("SESSION STATISTICS"));
114
+ lines.push(info(`Tool calls allowed: ${stats.allowed}`));
115
+ lines.push(info(`Tool calls blocked: ${stats.blocked}`));
116
+ lines.push(info(`Dangerous operations logged: ${stats.warnings}`));
131
117
  if (stats.blocked > 0) {
132
- lines.push((0, import_format.warn)(`${stats.blocked} operation(s) were blocked by security rules`));
118
+ lines.push(warn(`${stats.blocked} operation(s) were blocked by security rules`));
133
119
  }
134
120
  const ruleNames = Object.keys(stats.byRule);
135
121
  if (ruleNames.length > 0) {
136
- lines.push((0, import_format.section)("BLOCKED BY RULE"));
122
+ lines.push(section("BLOCKED BY RULE"));
137
123
  for (const rule of ruleNames) {
138
- lines.push((0, import_format.info)(` ${rule}: ${stats.byRule[rule]} blocked`));
124
+ lines.push(info(` ${rule}: ${stats.byRule[rule]} blocked`));
139
125
  }
140
126
  }
141
127
  if (stats.lastBlocked) {
142
- lines.push((0, import_format.section)("LAST BLOCKED"));
143
- lines.push((0, import_format.fail)(`Tool: ${stats.lastBlocked.tool}`));
144
- lines.push((0, import_format.fail)(`Rule: ${stats.lastBlocked.rule}`));
145
- lines.push((0, import_format.fail)(`Detail: ${stats.lastBlocked.detail}`));
146
- lines.push((0, import_format.info)(`Time: ${stats.lastBlocked.timestamp}`));
128
+ lines.push(section("LAST BLOCKED"));
129
+ lines.push(fail(`Tool: ${stats.lastBlocked.tool}`));
130
+ lines.push(fail(`Rule: ${stats.lastBlocked.rule}`));
131
+ lines.push(fail(`Detail: ${stats.lastBlocked.detail}`));
132
+ lines.push(info(`Time: ${stats.lastBlocked.timestamp}`));
147
133
  }
148
- lines.push((0, import_format.section)("SECURITY CONFIGURATION"));
149
- lines.push((0, import_format.info)(`Blocked commands: ${import_security.BLOCKED_COMMANDS.size}`));
150
- lines.push((0, import_format.info)(`Blocked URL patterns: ${import_security.BLOCKED_URL_PATTERNS.size}`));
151
- lines.push((0, import_format.info)(`Active checks: command_blocklist, path_validation, ssrf_protection, injection_detection`));
152
- const recentEntries = (0, import_security.readRecentAuditEntries)(20);
134
+ lines.push(section("SECURITY CONFIGURATION"));
135
+ lines.push(info(`Blocked commands: ${BLOCKED_COMMANDS.size}`));
136
+ lines.push(info(`Blocked URL patterns: ${BLOCKED_URL_PATTERNS.size}`));
137
+ lines.push(info(`Active checks: command_blocklist, path_validation, ssrf_protection, injection_detection`));
138
+ const recentEntries = readRecentAuditEntries(20);
153
139
  if (recentEntries.length > 0) {
154
- lines.push((0, import_format.section)("RECENT AUDIT LOG (last 20)"));
140
+ lines.push(section("RECENT AUDIT LOG (last 20)"));
155
141
  for (const entry of recentEntries) {
156
142
  const ts = entry.timestamp || "?";
157
143
  const action = entry.action;
@@ -159,19 +145,19 @@ function security_temp_default(pi) {
159
145
  const rule = entry.rule;
160
146
  const detail = entry.detail;
161
147
  if (action === "blocked") {
162
- lines.push((0, import_format.fail)(`[${ts}] ${tool} \u2192 BLOCKED (${rule}): ${detail}`));
148
+ lines.push(fail(`[${ts}] ${tool} \u2192 BLOCKED (${rule}): ${detail}`));
163
149
  } else if (action === "warning") {
164
- lines.push((0, import_format.warn)(`[${ts}] ${tool} \u2192 WARNING (${rule}): ${detail}`));
150
+ lines.push(warn(`[${ts}] ${tool} \u2192 WARNING (${rule}): ${detail}`));
165
151
  } else {
166
- lines.push((0, import_format.ok)(`[${ts}] ${tool} \u2192 allowed (${rule})`));
152
+ lines.push(ok(`[${ts}] ${tool} \u2192 allowed (${rule})`));
167
153
  }
168
154
  }
169
155
  }
170
- lines.push((0, import_format.section)("SUMMARY"));
156
+ lines.push(section("SUMMARY"));
171
157
  if (stats.blocked === 0) {
172
- lines.push((0, import_format.ok)("No security violations detected in this session"));
158
+ lines.push(ok("No security violations detected in this session"));
173
159
  } else {
174
- lines.push((0, import_format.fail)(`${stats.blocked} security violation(s) blocked`));
160
+ lines.push(fail(`${stats.blocked} security violation(s) blocked`));
175
161
  }
176
162
  lines.push(branding);
177
163
  return lines.join("\n");
@@ -231,3 +217,6 @@ function sanitizeInputForLog(input) {
231
217
  }
232
218
  return sanitized;
233
219
  }
220
+ export {
221
+ security_temp_default as default
222
+ };