gossipcat 0.4.4 → 0.4.6
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-mcp/mcp-server.js +82 -31
- package/package.json +1 -1
package/dist-mcp/mcp-server.js
CHANGED
|
@@ -3836,8 +3836,9 @@ var init_file_tools = __esm({
|
|
|
3836
3836
|
constructor(sandbox) {
|
|
3837
3837
|
this.sandbox = sandbox;
|
|
3838
3838
|
}
|
|
3839
|
-
async fileRead(args) {
|
|
3840
|
-
const
|
|
3839
|
+
async fileRead(args, agentRoot) {
|
|
3840
|
+
const allowed = agentRoot ? [agentRoot] : [];
|
|
3841
|
+
const absPath = this.sandbox.validatePath(args.path, allowed);
|
|
3841
3842
|
try {
|
|
3842
3843
|
const content = await (0, import_promises.readFile)(absPath, "utf-8");
|
|
3843
3844
|
const lines = content.split("\n");
|
|
@@ -3854,25 +3855,29 @@ var init_file_tools = __esm({
|
|
|
3854
3855
|
throw err;
|
|
3855
3856
|
}
|
|
3856
3857
|
}
|
|
3857
|
-
async fileWrite(args) {
|
|
3858
|
-
const
|
|
3858
|
+
async fileWrite(args, agentRoot) {
|
|
3859
|
+
const allowed = agentRoot ? [agentRoot] : [];
|
|
3860
|
+
const absPath = this.sandbox.validatePath(args.path, allowed);
|
|
3859
3861
|
const dir = (0, import_path3.resolve)(absPath, "..");
|
|
3860
3862
|
await (0, import_promises.mkdir)(dir, { recursive: true });
|
|
3861
3863
|
await (0, import_promises.writeFile)(absPath, args.content, "utf-8");
|
|
3862
3864
|
return `Written ${args.content.length} bytes to ${args.path}`;
|
|
3863
3865
|
}
|
|
3864
|
-
async fileDelete(args) {
|
|
3865
|
-
const
|
|
3866
|
+
async fileDelete(args, agentRoot) {
|
|
3867
|
+
const allowed = agentRoot ? [agentRoot] : [];
|
|
3868
|
+
const absPath = this.sandbox.validatePath(args.path, allowed);
|
|
3866
3869
|
await (0, import_promises.unlink)(absPath);
|
|
3867
3870
|
return `Deleted ${args.path}`;
|
|
3868
3871
|
}
|
|
3869
|
-
async fileSearch(args) {
|
|
3872
|
+
async fileSearch(args, agentRoot) {
|
|
3870
3873
|
const results = [];
|
|
3871
|
-
|
|
3874
|
+
const root = agentRoot || this.sandbox.projectRoot;
|
|
3875
|
+
await this.walkDir(root, args.pattern, results, 0, 10);
|
|
3872
3876
|
return results.join("\n") || "No files found";
|
|
3873
3877
|
}
|
|
3874
|
-
async fileGrep(args) {
|
|
3875
|
-
const
|
|
3878
|
+
async fileGrep(args, agentRoot) {
|
|
3879
|
+
const allowed = agentRoot ? [agentRoot] : [];
|
|
3880
|
+
const searchRoot = args.path ? this.sandbox.validatePath(args.path, allowed) : agentRoot || this.sandbox.projectRoot;
|
|
3876
3881
|
let regex;
|
|
3877
3882
|
try {
|
|
3878
3883
|
regex = new RegExp(args.pattern);
|
|
@@ -3883,8 +3888,9 @@ var init_file_tools = __esm({
|
|
|
3883
3888
|
await this.grepDir(searchRoot, regex, results, 0, 10);
|
|
3884
3889
|
return results.join("\n") || "No matches found";
|
|
3885
3890
|
}
|
|
3886
|
-
async fileTree(args) {
|
|
3887
|
-
const
|
|
3891
|
+
async fileTree(args, agentRoot) {
|
|
3892
|
+
const allowed = agentRoot ? [agentRoot] : [];
|
|
3893
|
+
const root = args.path ? this.sandbox.validatePath(args.path, allowed) : agentRoot || this.sandbox.projectRoot;
|
|
3888
3894
|
const maxDepth = args.depth || 3;
|
|
3889
3895
|
const lines = [];
|
|
3890
3896
|
await this.buildTree(root, "", lines, 0, maxDepth);
|
|
@@ -4218,12 +4224,19 @@ var init_skill_tools = __esm({
|
|
|
4218
4224
|
});
|
|
4219
4225
|
|
|
4220
4226
|
// packages/tools/src/sandbox.ts
|
|
4221
|
-
|
|
4227
|
+
function fold(p) {
|
|
4228
|
+
return CASE_INSENSITIVE_FS ? p.toLowerCase() : p;
|
|
4229
|
+
}
|
|
4230
|
+
function rootWithSlash(p) {
|
|
4231
|
+
return p.endsWith("/") ? p : p + "/";
|
|
4232
|
+
}
|
|
4233
|
+
var import_path5, import_fs4, CASE_INSENSITIVE_FS, Sandbox;
|
|
4222
4234
|
var init_sandbox = __esm({
|
|
4223
4235
|
"packages/tools/src/sandbox.ts"() {
|
|
4224
4236
|
"use strict";
|
|
4225
4237
|
import_path5 = require("path");
|
|
4226
4238
|
import_fs4 = require("fs");
|
|
4239
|
+
CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
|
|
4227
4240
|
Sandbox = class {
|
|
4228
4241
|
root;
|
|
4229
4242
|
constructor(projectRoot) {
|
|
@@ -4233,12 +4246,26 @@ var init_sandbox = __esm({
|
|
|
4233
4246
|
return this.root;
|
|
4234
4247
|
}
|
|
4235
4248
|
/**
|
|
4236
|
-
* Validate that a path resolves within the project root
|
|
4237
|
-
*
|
|
4238
|
-
* deepest existing ancestor and
|
|
4239
|
-
* Resolves symlinks
|
|
4249
|
+
* Validate that a path resolves within the project root OR inside any
|
|
4250
|
+
* entry in `allowedRoots` (union-of-roots). Handles non-existent files
|
|
4251
|
+
* (for file_write) by walking up to the deepest existing ancestor and
|
|
4252
|
+
* resolving from there. Resolves symlinks BEFORE the membership check
|
|
4253
|
+
* to prevent symlink escape attacks.
|
|
4254
|
+
*
|
|
4255
|
+
* Preserves these security properties:
|
|
4256
|
+
* - path.resolve() on candidate AND every allowed root before compare
|
|
4257
|
+
* - realpathSync on deepest existing ancestor of candidate
|
|
4258
|
+
* - trailing-slash canonical form on root side (blocks sibling-prefix
|
|
4259
|
+
* bypass like `/tmp/gossip-wt-AB` vs `/tmp/gossip-wt-ABXYZ`)
|
|
4260
|
+
* - case-fold on darwin/win32
|
|
4261
|
+
*
|
|
4262
|
+
* @param filePath Path to validate. May be relative (resolved against
|
|
4263
|
+
* projectRoot) or absolute.
|
|
4264
|
+
* @param allowedRoots Optional absolute paths that should also be
|
|
4265
|
+
* accepted as roots. Defaults to `[]` so existing
|
|
4266
|
+
* callers are unchanged.
|
|
4240
4267
|
*/
|
|
4241
|
-
validatePath(filePath) {
|
|
4268
|
+
validatePath(filePath, allowedRoots = []) {
|
|
4242
4269
|
const resolved = (0, import_path5.resolve)(this.root, filePath);
|
|
4243
4270
|
let checkPath = resolved;
|
|
4244
4271
|
while (!(0, import_fs4.existsSync)(checkPath)) {
|
|
@@ -4249,10 +4276,27 @@ var init_sandbox = __esm({
|
|
|
4249
4276
|
const real = (0, import_fs4.existsSync)(checkPath) ? (0, import_fs4.realpathSync)(checkPath) : checkPath;
|
|
4250
4277
|
const remainder = resolved.slice(checkPath.length);
|
|
4251
4278
|
const fullReal = real + remainder;
|
|
4252
|
-
|
|
4253
|
-
|
|
4279
|
+
const candidateRoots = [this.root];
|
|
4280
|
+
for (const r of allowedRoots) {
|
|
4281
|
+
if (!r) continue;
|
|
4282
|
+
const absR = (0, import_path5.resolve)(r);
|
|
4283
|
+
const realR = (0, import_fs4.existsSync)(absR) ? (() => {
|
|
4284
|
+
try {
|
|
4285
|
+
return (0, import_fs4.realpathSync)(absR);
|
|
4286
|
+
} catch {
|
|
4287
|
+
return absR;
|
|
4288
|
+
}
|
|
4289
|
+
})() : absR;
|
|
4290
|
+
candidateRoots.push(realR);
|
|
4291
|
+
}
|
|
4292
|
+
const foldedFull = fold(fullReal);
|
|
4293
|
+
for (const r of candidateRoots) {
|
|
4294
|
+
const foldedRoot = fold(r);
|
|
4295
|
+
if (foldedFull === foldedRoot) return fullReal;
|
|
4296
|
+
if (foldedFull.startsWith(rootWithSlash(foldedRoot))) return fullReal;
|
|
4254
4297
|
}
|
|
4255
|
-
|
|
4298
|
+
const msg = allowedRoots.length > 0 ? `Path "${filePath}" resolves outside project root or agent root` : `Path "${filePath}" resolves outside project root`;
|
|
4299
|
+
throw new Error(msg);
|
|
4256
4300
|
}
|
|
4257
4301
|
};
|
|
4258
4302
|
}
|
|
@@ -8478,19 +8522,19 @@ function canonicalizeForBoundary(p) {
|
|
|
8478
8522
|
}
|
|
8479
8523
|
}
|
|
8480
8524
|
}
|
|
8481
|
-
if (
|
|
8525
|
+
if (CASE_INSENSITIVE_FS2) out = out.toLowerCase();
|
|
8482
8526
|
return out.endsWith("/") ? out : out + "/";
|
|
8483
8527
|
}
|
|
8484
8528
|
function validatePathInScope(scope, canonicalPath) {
|
|
8485
8529
|
return canonicalPath.startsWith(scope);
|
|
8486
8530
|
}
|
|
8487
|
-
var import_path6, import_fs5,
|
|
8531
|
+
var import_path6, import_fs5, CASE_INSENSITIVE_FS2;
|
|
8488
8532
|
var init_scope = __esm({
|
|
8489
8533
|
"packages/tools/src/scope.ts"() {
|
|
8490
8534
|
"use strict";
|
|
8491
8535
|
import_path6 = require("path");
|
|
8492
8536
|
import_fs5 = require("fs");
|
|
8493
|
-
|
|
8537
|
+
CASE_INSENSITIVE_FS2 = process.platform === "darwin" || process.platform === "win32";
|
|
8494
8538
|
}
|
|
8495
8539
|
});
|
|
8496
8540
|
|
|
@@ -8742,7 +8786,7 @@ var init_tool_server = __esm({
|
|
|
8742
8786
|
throw new Error(`Read blocked: "${args.path}" is outside scope "${readScope}"`);
|
|
8743
8787
|
}
|
|
8744
8788
|
}
|
|
8745
|
-
return this.fileTools.fileRead(args);
|
|
8789
|
+
return this.fileTools.fileRead(args, agentRoot);
|
|
8746
8790
|
}
|
|
8747
8791
|
case "file_write": {
|
|
8748
8792
|
if (callerId) {
|
|
@@ -8752,20 +8796,20 @@ var init_tool_server = __esm({
|
|
|
8752
8796
|
throw new Error(`Agent ${callerId} exceeded max tracked file writes (${_ToolServer.MAX_WRITTEN_FILES_PER_AGENT})`);
|
|
8753
8797
|
}
|
|
8754
8798
|
}
|
|
8755
|
-
const result = await this.fileTools.fileWrite(args);
|
|
8799
|
+
const result = await this.fileTools.fileWrite(args, agentRoot);
|
|
8756
8800
|
if (callerId) {
|
|
8757
8801
|
this.agentWrittenFiles.get(callerId).add(args.path);
|
|
8758
8802
|
}
|
|
8759
8803
|
return result;
|
|
8760
8804
|
}
|
|
8761
8805
|
case "file_delete":
|
|
8762
|
-
return this.fileTools.fileDelete(args);
|
|
8806
|
+
return this.fileTools.fileDelete(args, agentRoot);
|
|
8763
8807
|
case "file_search":
|
|
8764
|
-
return this.fileTools.fileSearch(args);
|
|
8808
|
+
return this.fileTools.fileSearch(args, agentRoot);
|
|
8765
8809
|
case "file_grep":
|
|
8766
|
-
return this.fileTools.fileGrep(args);
|
|
8810
|
+
return this.fileTools.fileGrep(args, agentRoot);
|
|
8767
8811
|
case "file_tree":
|
|
8768
|
-
return this.fileTools.fileTree(args);
|
|
8812
|
+
return this.fileTools.fileTree(args, agentRoot);
|
|
8769
8813
|
case "shell_exec":
|
|
8770
8814
|
return this.shellTools.shellExec({
|
|
8771
8815
|
...args,
|
|
@@ -9224,7 +9268,7 @@ var init_definitions = __esm({
|
|
|
9224
9268
|
var src_exports2 = {};
|
|
9225
9269
|
__export(src_exports2, {
|
|
9226
9270
|
ALL_TOOLS: () => ALL_TOOLS,
|
|
9227
|
-
CASE_INSENSITIVE_FS: () =>
|
|
9271
|
+
CASE_INSENSITIVE_FS: () => CASE_INSENSITIVE_FS2,
|
|
9228
9272
|
FILE_TOOLS: () => FILE_TOOLS,
|
|
9229
9273
|
FileTools: () => FileTools,
|
|
9230
9274
|
GIT_TOOLS: () => GIT_TOOLS,
|
|
@@ -21573,6 +21617,13 @@ function buildAuditExclusions(projectRoot, ownWorktree) {
|
|
|
21573
21617
|
const root = canonicalize(projectRoot);
|
|
21574
21618
|
for (const v of expandTmpVariants(`${root}/.gossip`)) excl.add(v);
|
|
21575
21619
|
for (const v of expandTmpVariants(`${root}/.claude`)) excl.add(v);
|
|
21620
|
+
try {
|
|
21621
|
+
const home = canonicalize((0, import_os2.homedir)());
|
|
21622
|
+
for (const sub of ["Library", ".cache", ".npm", ".claude/projects"]) {
|
|
21623
|
+
for (const v of expandTmpVariants(`${home}/${sub}`)) excl.add(v);
|
|
21624
|
+
}
|
|
21625
|
+
} catch {
|
|
21626
|
+
}
|
|
21576
21627
|
if (ownWorktree) {
|
|
21577
21628
|
const wt = canonicalize(ownWorktree);
|
|
21578
21629
|
for (const v of expandTmpVariants(wt)) excl.add(v);
|
package/package.json
CHANGED