mobbdev 1.4.1 → 1.4.2
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/index.mjs +322 -372
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -4148,11 +4148,11 @@ ${rootContent}`;
|
|
|
4148
4148
|
try {
|
|
4149
4149
|
const gitRoot = await this.getGitRoot();
|
|
4150
4150
|
const gitignorePath = path2.join(gitRoot, ".gitignore");
|
|
4151
|
-
const
|
|
4151
|
+
const exists2 = fs2.existsSync(gitignorePath);
|
|
4152
4152
|
this.log("[GitService] .gitignore existence check complete", "debug", {
|
|
4153
|
-
exists
|
|
4153
|
+
exists: exists2
|
|
4154
4154
|
});
|
|
4155
|
-
return
|
|
4155
|
+
return exists2;
|
|
4156
4156
|
} catch (error) {
|
|
4157
4157
|
const errorMessage = `Failed to check .gitignore existence: ${error.message}`;
|
|
4158
4158
|
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
@@ -7178,7 +7178,7 @@ async function getAdoSdk(params) {
|
|
|
7178
7178
|
const url = new URL(repoUrl);
|
|
7179
7179
|
const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
7180
7180
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
7181
|
-
const
|
|
7181
|
+
const path36 = [
|
|
7182
7182
|
prefixPath,
|
|
7183
7183
|
owner,
|
|
7184
7184
|
projectName,
|
|
@@ -7189,7 +7189,7 @@ async function getAdoSdk(params) {
|
|
|
7189
7189
|
"items",
|
|
7190
7190
|
"items"
|
|
7191
7191
|
].filter(Boolean).join("/");
|
|
7192
|
-
return new URL(`${
|
|
7192
|
+
return new URL(`${path36}?${params2}`, origin).toString();
|
|
7193
7193
|
},
|
|
7194
7194
|
async getAdoBranchList({ repoUrl }) {
|
|
7195
7195
|
try {
|
|
@@ -7278,8 +7278,8 @@ async function getAdoSdk(params) {
|
|
|
7278
7278
|
const changeType = entry.changeType;
|
|
7279
7279
|
return changeType !== 16 && entry.item?.path;
|
|
7280
7280
|
}).map((entry) => {
|
|
7281
|
-
const
|
|
7282
|
-
return
|
|
7281
|
+
const path36 = entry.item.path;
|
|
7282
|
+
return path36.startsWith("/") ? path36.slice(1) : path36;
|
|
7283
7283
|
});
|
|
7284
7284
|
},
|
|
7285
7285
|
async searchAdoPullRequests({
|
|
@@ -15172,7 +15172,7 @@ async function postIssueComment(params) {
|
|
|
15172
15172
|
fpDescription
|
|
15173
15173
|
} = params;
|
|
15174
15174
|
const {
|
|
15175
|
-
path:
|
|
15175
|
+
path: path36,
|
|
15176
15176
|
startLine,
|
|
15177
15177
|
vulnerabilityReportIssue: {
|
|
15178
15178
|
vulnerabilityReportIssueTags,
|
|
@@ -15187,7 +15187,7 @@ async function postIssueComment(params) {
|
|
|
15187
15187
|
Refresh the page in order to see the changes.`,
|
|
15188
15188
|
pull_number: pullRequest,
|
|
15189
15189
|
commit_id: commitSha,
|
|
15190
|
-
path:
|
|
15190
|
+
path: path36,
|
|
15191
15191
|
line: startLine
|
|
15192
15192
|
});
|
|
15193
15193
|
const commentId = commentRes.data.id;
|
|
@@ -15221,7 +15221,7 @@ async function postFixComment(params) {
|
|
|
15221
15221
|
scanner
|
|
15222
15222
|
} = params;
|
|
15223
15223
|
const {
|
|
15224
|
-
path:
|
|
15224
|
+
path: path36,
|
|
15225
15225
|
startLine,
|
|
15226
15226
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
15227
15227
|
vulnerabilityReportIssueId
|
|
@@ -15239,7 +15239,7 @@ async function postFixComment(params) {
|
|
|
15239
15239
|
Refresh the page in order to see the changes.`,
|
|
15240
15240
|
pull_number: pullRequest,
|
|
15241
15241
|
commit_id: commitSha,
|
|
15242
|
-
path:
|
|
15242
|
+
path: path36,
|
|
15243
15243
|
line: startLine
|
|
15244
15244
|
});
|
|
15245
15245
|
const commentId = commentRes.data.id;
|
|
@@ -17161,7 +17161,7 @@ import { spawn } from "child_process";
|
|
|
17161
17161
|
|
|
17162
17162
|
// src/features/claude_code/daemon.ts
|
|
17163
17163
|
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
17164
|
-
import
|
|
17164
|
+
import path23 from "path";
|
|
17165
17165
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
17166
17166
|
import Configstore3 from "configstore";
|
|
17167
17167
|
|
|
@@ -17173,7 +17173,8 @@ var HEARTBEAT_DEBOUNCE_MS = (() => {
|
|
|
17173
17173
|
})();
|
|
17174
17174
|
var KILL_SWITCH_ENV = "MOBB_TRACY_SKILL_QUARANTINE_DISABLE";
|
|
17175
17175
|
var MALICIOUS_VERDICT = "MALICIOUS";
|
|
17176
|
-
var
|
|
17176
|
+
var PARTIAL_SWEEP_GRACE_MS = 10 * 60 * 1e3;
|
|
17177
|
+
var STUB_MARKER = "\u26D4 QUARANTINED BY TRACY";
|
|
17177
17178
|
|
|
17178
17179
|
// src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
|
|
17179
17180
|
import { homedir as homedir2 } from "os";
|
|
@@ -17546,11 +17547,11 @@ async function readJsoncSettings(settingsPath) {
|
|
|
17546
17547
|
putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
|
|
17547
17548
|
return payload;
|
|
17548
17549
|
}
|
|
17549
|
-
function putSettingsCache(
|
|
17550
|
-
if (!settingsCache.has(
|
|
17550
|
+
function putSettingsCache(path36, entry) {
|
|
17551
|
+
if (!settingsCache.has(path36) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
|
|
17551
17552
|
settingsCache.delete(settingsCache.keys().next().value);
|
|
17552
17553
|
}
|
|
17553
|
-
settingsCache.set(
|
|
17554
|
+
settingsCache.set(path36, entry);
|
|
17554
17555
|
}
|
|
17555
17556
|
async function readCopilotCustomLocations(workspaceRoot) {
|
|
17556
17557
|
const parsed = await readJsoncSettings(
|
|
@@ -17800,7 +17801,7 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
|
|
|
17800
17801
|
} catch {
|
|
17801
17802
|
return [];
|
|
17802
17803
|
}
|
|
17803
|
-
return files.map((
|
|
17804
|
+
return files.map((path36) => ({ path: path36, category }));
|
|
17804
17805
|
}
|
|
17805
17806
|
async function enumerateSkillBundle(baseDir, skillsRoot) {
|
|
17806
17807
|
const skillsDir = path14.resolve(baseDir, skillsRoot);
|
|
@@ -17966,27 +17967,32 @@ var Metric = {
|
|
|
17966
17967
|
CHECK_DISABLED_ENV: "skill_quarantine.check_disabled_env",
|
|
17967
17968
|
/** Verdict-query call failed. Fail-open. */
|
|
17968
17969
|
QUERY_ERROR: "skill_quarantine.query_error",
|
|
17969
|
-
/** Count of skills enumerated in this run
|
|
17970
|
+
/** Count of skills enumerated in this run. */
|
|
17970
17971
|
SKILLS_CHECKED: "skill_quarantine.skills_checked",
|
|
17971
|
-
/** A skill was freshly quarantined.
|
|
17972
|
+
/** A skill was freshly quarantined. */
|
|
17972
17973
|
QUARANTINED: "skill_quarantine.quarantined",
|
|
17973
|
-
/** Presence check hit; skill already quarantined. */
|
|
17974
|
+
/** Presence check hit (`<md5>.zip` exists); skill already quarantined. */
|
|
17974
17975
|
ALREADY_QUARANTINED: "skill_quarantine.already_quarantined",
|
|
17975
|
-
/**
|
|
17976
|
-
|
|
17977
|
-
/** Stub
|
|
17976
|
+
/** Zip build or partial→tmp rename failed (phase 1). */
|
|
17977
|
+
ZIP_ERROR: "skill_quarantine.zip_error",
|
|
17978
|
+
/** Stub write failed (phase 2); tmp preserved for reconcile. */
|
|
17978
17979
|
STUB_ERROR: "skill_quarantine.stub_error",
|
|
17979
|
-
/**
|
|
17980
|
-
|
|
17980
|
+
/** Tmp→published rename failed (phase 3); reconcile will retry. */
|
|
17981
|
+
PUBLISH_ERROR: "skill_quarantine.publish_error",
|
|
17982
|
+
/** Reconcile published a leftover tmp whose `<md5>.zip` was missing. */
|
|
17983
|
+
RECONCILED: "skill_quarantine.reconciled",
|
|
17984
|
+
/** Tmp removed because `<md5>.zip` already existed. */
|
|
17985
|
+
SWEPT_REDUNDANT_TMP: "skill_quarantine.swept_redundant_tmp",
|
|
17986
|
+
/** Stale partial zip swept (older than grace window). */
|
|
17987
|
+
SWEPT_PARTIAL: "skill_quarantine.swept_partial",
|
|
17981
17988
|
/** Total run duration including I/O. */
|
|
17982
17989
|
DURATION_MS: "skill_quarantine.duration_ms"
|
|
17983
17990
|
};
|
|
17984
17991
|
|
|
17985
17992
|
// src/features/analysis/skill_quarantine/quarantineSkill.ts
|
|
17986
17993
|
import { randomUUID } from "crypto";
|
|
17987
|
-
import { existsSync as existsSync2 } from "fs";
|
|
17988
17994
|
import {
|
|
17989
|
-
|
|
17995
|
+
access,
|
|
17990
17996
|
mkdir,
|
|
17991
17997
|
readdir as readdir2,
|
|
17992
17998
|
readFile as readFile2,
|
|
@@ -17996,8 +18002,8 @@ import {
|
|
|
17996
18002
|
unlink,
|
|
17997
18003
|
writeFile
|
|
17998
18004
|
} from "fs/promises";
|
|
17999
|
-
import
|
|
18000
|
-
import
|
|
18005
|
+
import path18 from "path";
|
|
18006
|
+
import AdmZip4 from "adm-zip";
|
|
18001
18007
|
|
|
18002
18008
|
// src/features/analysis/skill_quarantine/paths.ts
|
|
18003
18009
|
import { homedir as homedir3 } from "os";
|
|
@@ -18005,27 +18011,29 @@ import path16 from "path";
|
|
|
18005
18011
|
function getQuarantineRoot() {
|
|
18006
18012
|
return path16.join(homedir3(), ".tracy", "quarantine", "claude", "skills");
|
|
18007
18013
|
}
|
|
18008
|
-
function
|
|
18009
|
-
return path16.join(getQuarantineRoot(), md5);
|
|
18014
|
+
function getQuarantineZipPath(md5) {
|
|
18015
|
+
return path16.join(getQuarantineRoot(), `${md5}.zip`);
|
|
18010
18016
|
}
|
|
18011
|
-
function
|
|
18012
|
-
return path16.join(
|
|
18017
|
+
function getTmpZipPath(md5, uuid) {
|
|
18018
|
+
return path16.join(getQuarantineRoot(), `${md5}_tmp_${uuid}.zip`);
|
|
18013
18019
|
}
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
}
|
|
18017
|
-
var STAGING_DIR_REGEX = /^([0-9a-f]{32})_tmp_/;
|
|
18020
|
+
var TMP_ZIP_REGEX = /^([0-9a-f]{32})_tmp_[0-9a-f-]+\.zip$/;
|
|
18021
|
+
var COMMITTED_ZIP_REGEX = /^([0-9a-f]{32})\.zip$/;
|
|
18018
18022
|
|
|
18019
18023
|
// src/features/analysis/skill_quarantine/stubTemplate.ts
|
|
18024
|
+
import path17 from "path";
|
|
18025
|
+
import { quote } from "shell-quote";
|
|
18020
18026
|
var LEGACY_SUMMARY_FALLBACK = "not available (scan predates current schema)";
|
|
18021
18027
|
function renderStub(params) {
|
|
18022
18028
|
const folderOrFile = params.isFolder ? "skill folder" : "skill file";
|
|
18023
18029
|
const reason = params.summary ?? LEGACY_SUMMARY_FALLBACK;
|
|
18024
|
-
|
|
18030
|
+
const extractParent = path17.dirname(params.origPath);
|
|
18031
|
+
const recoverCommand = `rm -rf ${quote([params.origPath])} && unzip -o ${quote([params.quarantinedZipPath])} -d ${quote([extractParent])}`;
|
|
18032
|
+
return `# ${STUB_MARKER}
|
|
18025
18033
|
|
|
18026
18034
|
This skill was flagged **MALICIOUS** by the Mobb security scanner and has been
|
|
18027
|
-
|
|
18028
|
-
stub is in place.
|
|
18035
|
+
archived out of your skills folder. **Claude Code will not execute it** while
|
|
18036
|
+
this stub is in place.
|
|
18029
18037
|
|
|
18030
18038
|
## Why this skill was flagged
|
|
18031
18039
|
|
|
@@ -18036,23 +18044,22 @@ stub is in place.
|
|
|
18036
18044
|
|
|
18037
18045
|
## Where the original is now
|
|
18038
18046
|
|
|
18039
|
-
The original ${folderOrFile} has been
|
|
18047
|
+
The original ${folderOrFile} has been archived to:
|
|
18040
18048
|
|
|
18041
|
-
${params.
|
|
18049
|
+
${params.quarantinedZipPath}
|
|
18042
18050
|
|
|
18043
|
-
Nothing has been deleted. The
|
|
18051
|
+
Nothing has been deleted. The archive preserves the skill exactly as it was,
|
|
18052
|
+
including any secrets or local-only edits.
|
|
18044
18053
|
|
|
18045
18054
|
## If this is a false positive \u2014 how to recover
|
|
18046
18055
|
|
|
18047
18056
|
If you're confident this skill is safe and want to restore it:
|
|
18048
18057
|
|
|
18049
|
-
|
|
18058
|
+
${recoverCommand}
|
|
18050
18059
|
|
|
18051
|
-
Tracy will not re-quarantine it as long as
|
|
18052
|
-
|
|
18053
|
-
|
|
18054
|
-
that directory entirely, the next heartbeat will re-evaluate the skill from
|
|
18055
|
-
scratch.
|
|
18060
|
+
Tracy will not re-quarantine it as long as \`${params.md5}.zip\` remains in
|
|
18061
|
+
the quarantine folder. If you delete the archive, the next heartbeat will
|
|
18062
|
+
re-evaluate the skill from scratch.
|
|
18056
18063
|
|
|
18057
18064
|
## How to report a false positive
|
|
18058
18065
|
|
|
@@ -18069,130 +18076,50 @@ Your report helps tune the scanner for everyone.
|
|
|
18069
18076
|
// src/features/analysis/skill_quarantine/quarantineSkill.ts
|
|
18070
18077
|
async function quarantineSkill(params) {
|
|
18071
18078
|
const { skillPath, isFolder, md5, origName, verdict, log: log2 } = params;
|
|
18072
|
-
const
|
|
18073
|
-
if (
|
|
18079
|
+
const finalZip = getQuarantineZipPath(md5);
|
|
18080
|
+
if (await exists(finalZip)) {
|
|
18074
18081
|
log2.debug(
|
|
18075
18082
|
{ md5, metric: Metric.ALREADY_QUARANTINED },
|
|
18076
|
-
"skill_quarantine: already quarantined
|
|
18083
|
+
"skill_quarantine: already quarantined"
|
|
18077
18084
|
);
|
|
18078
18085
|
return { status: "already_quarantined" };
|
|
18079
18086
|
}
|
|
18080
|
-
|
|
18087
|
+
const tmpZip = getTmpZipPath(md5, randomUUID());
|
|
18081
18088
|
try {
|
|
18082
|
-
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18086
|
-
|
|
18087
|
-
|
|
18088
|
-
md5,
|
|
18089
|
-
isFolder,
|
|
18090
|
-
// Symlinks have no quarantine archive; note that in the stub.
|
|
18091
|
-
quarantinedPath: `(symlink at ${skillPath} replaced \u2014 original content was not moved)`,
|
|
18092
|
-
origPath: skillPath,
|
|
18093
|
-
summary: verdict.summary,
|
|
18094
|
-
scannerName: verdict.scannerName,
|
|
18095
|
-
scannerVersion: verdict.scannerVersion,
|
|
18096
|
-
scannedAt: verdict.scannedAt
|
|
18097
|
-
});
|
|
18098
|
-
try {
|
|
18099
|
-
await mkdir(hashDir, { recursive: true });
|
|
18100
|
-
if (isFolder) {
|
|
18101
|
-
const tmpDir = `${skillPath}.__tracy_tmp__`;
|
|
18102
|
-
await mkdir(tmpDir, { recursive: true });
|
|
18103
|
-
await writeFile(path17.join(tmpDir, "SKILL.md"), stubContent2, "utf8");
|
|
18104
|
-
await unlink(skillPath);
|
|
18105
|
-
await rename(tmpDir, skillPath);
|
|
18106
|
-
} else {
|
|
18107
|
-
const tmpFile = `${skillPath}.__tracy_tmp__`;
|
|
18108
|
-
await writeFile(tmpFile, stubContent2, "utf8");
|
|
18109
|
-
await unlink(skillPath);
|
|
18110
|
-
await rename(tmpFile, skillPath);
|
|
18111
|
-
}
|
|
18112
|
-
} catch (err) {
|
|
18113
|
-
log2.error(
|
|
18114
|
-
{ err, md5, skillPath, metric: Metric.STUB_ERROR },
|
|
18115
|
-
"skill_quarantine: symlink stub write failed"
|
|
18116
|
-
);
|
|
18117
|
-
return { status: "stub_error", err };
|
|
18089
|
+
await mkdir(getQuarantineRoot(), { recursive: true });
|
|
18090
|
+
const zip = new AdmZip4();
|
|
18091
|
+
if (isFolder) {
|
|
18092
|
+
await addFolderAsync(zip, skillPath, origName);
|
|
18093
|
+
} else {
|
|
18094
|
+
zip.addFile(origName, await readFile2(skillPath));
|
|
18118
18095
|
}
|
|
18119
|
-
await
|
|
18120
|
-
log2.info(
|
|
18121
|
-
{
|
|
18122
|
-
md5,
|
|
18123
|
-
verdict: verdict.verdict,
|
|
18124
|
-
shape: isFolder ? "folder" : "standalone",
|
|
18125
|
-
scanner: verdict.scannerName,
|
|
18126
|
-
scannerVersion: verdict.scannerVersion,
|
|
18127
|
-
metric: Metric.QUARANTINED
|
|
18128
|
-
},
|
|
18129
|
-
"skill_quarantine: quarantined (symlink)"
|
|
18130
|
-
);
|
|
18131
|
-
return { status: "quarantined" };
|
|
18132
|
-
}
|
|
18133
|
-
const stagingDir = getStagingDir(md5, process.pid, randomUUID());
|
|
18134
|
-
const stagingTarget = path17.join(stagingDir, origName);
|
|
18135
|
-
const finalTarget = getQuarantinedTargetPath(md5, origName);
|
|
18136
|
-
try {
|
|
18137
|
-
await mkdir(stagingDir, { recursive: true });
|
|
18138
|
-
} catch (err) {
|
|
18139
|
-
log2.error(
|
|
18140
|
-
{ err, md5, metric: Metric.MOVE_ERROR, phase: "stage" },
|
|
18141
|
-
"skill_quarantine: failed to create staging dir"
|
|
18142
|
-
);
|
|
18143
|
-
return { status: "move_error", phase: "stage", err };
|
|
18144
|
-
}
|
|
18145
|
-
try {
|
|
18146
|
-
await move(skillPath, stagingTarget);
|
|
18096
|
+
await writeFile(tmpZip, zip.toBuffer());
|
|
18147
18097
|
} catch (err) {
|
|
18148
|
-
await
|
|
18098
|
+
await unlink(tmpZip).catch(ignoreErr);
|
|
18149
18099
|
log2.error(
|
|
18150
|
-
{ err, md5, metric: Metric.
|
|
18151
|
-
"skill_quarantine: phase-1
|
|
18100
|
+
{ err, md5, metric: Metric.ZIP_ERROR },
|
|
18101
|
+
"skill_quarantine: phase-1 zip write failed"
|
|
18152
18102
|
);
|
|
18153
|
-
return { status: "
|
|
18103
|
+
return { status: "zip_error", err };
|
|
18154
18104
|
}
|
|
18155
18105
|
try {
|
|
18156
|
-
await
|
|
18106
|
+
await writeStub(params);
|
|
18157
18107
|
} catch (err) {
|
|
18158
18108
|
log2.error(
|
|
18159
|
-
{
|
|
18160
|
-
|
|
18161
|
-
md5,
|
|
18162
|
-
stagingDir,
|
|
18163
|
-
metric: Metric.MOVE_ERROR,
|
|
18164
|
-
phase: "publish"
|
|
18165
|
-
},
|
|
18166
|
-
"skill_quarantine: phase-2 publish failed; staging dir preserved for manual recovery"
|
|
18109
|
+
{ err, md5, skillPath, metric: Metric.STUB_ERROR },
|
|
18110
|
+
"skill_quarantine: stub write failed; tmp zip preserved for reconcile"
|
|
18167
18111
|
);
|
|
18168
|
-
return { status: "
|
|
18112
|
+
return { status: "stub_error", err };
|
|
18169
18113
|
}
|
|
18170
|
-
const quarantinedPath = finalTarget;
|
|
18171
|
-
const stubContent = renderStub({
|
|
18172
|
-
md5,
|
|
18173
|
-
isFolder,
|
|
18174
|
-
quarantinedPath,
|
|
18175
|
-
origPath: skillPath,
|
|
18176
|
-
summary: verdict.summary,
|
|
18177
|
-
scannerName: verdict.scannerName,
|
|
18178
|
-
scannerVersion: verdict.scannerVersion,
|
|
18179
|
-
scannedAt: verdict.scannedAt
|
|
18180
|
-
});
|
|
18181
18114
|
try {
|
|
18182
|
-
|
|
18183
|
-
await mkdir(skillPath, { recursive: true });
|
|
18184
|
-
await writeFile(path17.join(skillPath, "SKILL.md"), stubContent, "utf8");
|
|
18185
|
-
} else {
|
|
18186
|
-
await writeFile(skillPath, stubContent, "utf8");
|
|
18187
|
-
}
|
|
18115
|
+
await rename(tmpZip, finalZip);
|
|
18188
18116
|
} catch (err) {
|
|
18189
18117
|
log2.error(
|
|
18190
|
-
{ err, md5,
|
|
18191
|
-
"skill_quarantine:
|
|
18118
|
+
{ err, md5, tmpZip, metric: Metric.PUBLISH_ERROR },
|
|
18119
|
+
"skill_quarantine: phase-3 publish failed; reconcile will retry"
|
|
18192
18120
|
);
|
|
18193
|
-
return { status: "
|
|
18121
|
+
return { status: "publish_error", err };
|
|
18194
18122
|
}
|
|
18195
|
-
await preRegisterStubMd5(skillPath, isFolder, log2);
|
|
18196
18123
|
log2.info(
|
|
18197
18124
|
{
|
|
18198
18125
|
md5,
|
|
@@ -18206,87 +18133,110 @@ async function quarantineSkill(params) {
|
|
|
18206
18133
|
);
|
|
18207
18134
|
return { status: "quarantined" };
|
|
18208
18135
|
}
|
|
18209
|
-
async function
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
|
|
18213
|
-
|
|
18214
|
-
|
|
18215
|
-
|
|
18216
|
-
|
|
18217
|
-
|
|
18218
|
-
|
|
18219
|
-
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
|
|
18223
|
-
|
|
18224
|
-
await
|
|
18225
|
-
}
|
|
18226
|
-
|
|
18227
|
-
{ err, skillPath },
|
|
18228
|
-
"skill_quarantine: failed to pre-register stub md5"
|
|
18229
|
-
);
|
|
18136
|
+
async function writeStub(params) {
|
|
18137
|
+
const { skillPath, isFolder, md5, verdict } = params;
|
|
18138
|
+
const stubContent = renderStub({
|
|
18139
|
+
md5,
|
|
18140
|
+
isFolder,
|
|
18141
|
+
quarantinedZipPath: getQuarantineZipPath(md5),
|
|
18142
|
+
origPath: skillPath,
|
|
18143
|
+
summary: verdict.summary,
|
|
18144
|
+
scannerName: verdict.scannerName,
|
|
18145
|
+
scannerVersion: verdict.scannerVersion,
|
|
18146
|
+
scannedAt: verdict.scannedAt
|
|
18147
|
+
});
|
|
18148
|
+
if (isFolder) {
|
|
18149
|
+
await rm(skillPath, { recursive: true, force: true });
|
|
18150
|
+
await mkdir(skillPath, { recursive: true });
|
|
18151
|
+
await writeFile(path18.join(skillPath, "SKILL.md"), stubContent, "utf8");
|
|
18152
|
+
} else {
|
|
18153
|
+
await writeFile(skillPath, stubContent, "utf8");
|
|
18230
18154
|
}
|
|
18231
18155
|
}
|
|
18232
|
-
async function
|
|
18233
|
-
const now = Date.now();
|
|
18234
|
-
const target = isFolder ? path17.join(skillPath, "SKILL.md") : skillPath;
|
|
18235
|
-
const [st, content] = await Promise.all([
|
|
18236
|
-
stat2(target),
|
|
18237
|
-
readFile2(target, "utf8")
|
|
18238
|
-
]);
|
|
18239
|
-
return [
|
|
18240
|
-
{
|
|
18241
|
-
name: isFolder ? "SKILL.md" : path17.basename(skillPath),
|
|
18242
|
-
path: target,
|
|
18243
|
-
content,
|
|
18244
|
-
sizeBytes: st.size,
|
|
18245
|
-
category: "skill",
|
|
18246
|
-
mtimeMs: now
|
|
18247
|
-
}
|
|
18248
|
-
];
|
|
18249
|
-
}
|
|
18250
|
-
async function sweepOrphanStagingDirs(log2) {
|
|
18156
|
+
async function reconcileAndSweep(log2) {
|
|
18251
18157
|
const root = getQuarantineRoot();
|
|
18252
18158
|
let entries;
|
|
18253
18159
|
try {
|
|
18254
18160
|
entries = await readdir2(root);
|
|
18255
18161
|
} catch (err) {
|
|
18256
|
-
if (err.code === "ENOENT") return
|
|
18257
|
-
log2.warn({ err, root }, "skill_quarantine:
|
|
18258
|
-
return
|
|
18162
|
+
if (err.code === "ENOENT") return;
|
|
18163
|
+
log2.warn({ err, root }, "skill_quarantine: reconcile readdir failed");
|
|
18164
|
+
return;
|
|
18259
18165
|
}
|
|
18166
|
+
const committed = new Set(
|
|
18167
|
+
entries.map((e) => COMMITTED_ZIP_REGEX.exec(e)?.[1]).filter((m) => m !== void 0)
|
|
18168
|
+
);
|
|
18260
18169
|
const now = Date.now();
|
|
18261
|
-
let swept = 0;
|
|
18262
18170
|
for (const entry of entries) {
|
|
18263
|
-
|
|
18264
|
-
|
|
18265
|
-
|
|
18266
|
-
|
|
18267
|
-
|
|
18268
|
-
|
|
18171
|
+
const md5 = TMP_ZIP_REGEX.exec(entry)?.[1];
|
|
18172
|
+
if (md5 === void 0) continue;
|
|
18173
|
+
const full = path18.join(root, entry);
|
|
18174
|
+
if (committed.has(md5)) {
|
|
18175
|
+
await unlink(full).catch(
|
|
18176
|
+
(err) => log2.warn(
|
|
18177
|
+
{ err, path: full, md5 },
|
|
18178
|
+
"skill_quarantine: redundant tmp unlink failed"
|
|
18179
|
+
)
|
|
18180
|
+
);
|
|
18181
|
+
log2.info(
|
|
18182
|
+
{ path: full, md5, metric: Metric.SWEPT_REDUNDANT_TMP },
|
|
18183
|
+
"skill_quarantine: swept redundant tmp"
|
|
18184
|
+
);
|
|
18269
18185
|
continue;
|
|
18270
18186
|
}
|
|
18271
|
-
|
|
18187
|
+
let valid;
|
|
18272
18188
|
try {
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
|
|
18278
|
-
|
|
18279
|
-
|
|
18280
|
-
|
|
18189
|
+
new AdmZip4(full).getEntries();
|
|
18190
|
+
valid = true;
|
|
18191
|
+
} catch {
|
|
18192
|
+
valid = false;
|
|
18193
|
+
}
|
|
18194
|
+
if (valid) {
|
|
18195
|
+
try {
|
|
18196
|
+
await rename(full, getQuarantineZipPath(md5));
|
|
18197
|
+
committed.add(md5);
|
|
18198
|
+
log2.info(
|
|
18199
|
+
{ path: full, md5, metric: Metric.RECONCILED },
|
|
18200
|
+
"skill_quarantine: reconciled tmp \u2192 published"
|
|
18201
|
+
);
|
|
18202
|
+
} catch (err) {
|
|
18203
|
+
log2.warn(
|
|
18204
|
+
{ err, path: full, md5 },
|
|
18205
|
+
"skill_quarantine: reconcile rename failed"
|
|
18206
|
+
);
|
|
18207
|
+
}
|
|
18208
|
+
continue;
|
|
18281
18209
|
}
|
|
18210
|
+
const { mtimeMs } = await stat2(full).catch(() => ({ mtimeMs: now }));
|
|
18211
|
+
if (now - mtimeMs < PARTIAL_SWEEP_GRACE_MS) continue;
|
|
18212
|
+
await unlink(full).catch(
|
|
18213
|
+
(err) => log2.warn({ err, path: full }, "skill_quarantine: partial unlink failed")
|
|
18214
|
+
);
|
|
18215
|
+
log2.info(
|
|
18216
|
+
{ path: full, metric: Metric.SWEPT_PARTIAL },
|
|
18217
|
+
"skill_quarantine: swept broken tmp (partial write)"
|
|
18218
|
+
);
|
|
18282
18219
|
}
|
|
18283
|
-
return swept;
|
|
18284
18220
|
}
|
|
18285
|
-
|
|
18286
|
-
|
|
18287
|
-
|
|
18288
|
-
|
|
18221
|
+
function exists(p) {
|
|
18222
|
+
return access(p).then(
|
|
18223
|
+
() => true,
|
|
18224
|
+
() => false
|
|
18225
|
+
);
|
|
18226
|
+
}
|
|
18227
|
+
function ignoreErr() {
|
|
18228
|
+
}
|
|
18229
|
+
async function addFolderAsync(zip, root, prefix) {
|
|
18230
|
+
async function walk(dir, relPrefix) {
|
|
18231
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
18232
|
+
for (const e of entries) {
|
|
18233
|
+
const full = path18.join(dir, e.name);
|
|
18234
|
+
const rel = path18.posix.join(relPrefix, e.name);
|
|
18235
|
+
if (e.isDirectory()) await walk(full, rel);
|
|
18236
|
+
else if (e.isFile()) zip.addFile(rel, await readFile2(full));
|
|
18237
|
+
}
|
|
18289
18238
|
}
|
|
18239
|
+
await walk(root, prefix);
|
|
18290
18240
|
}
|
|
18291
18241
|
|
|
18292
18242
|
// src/features/analysis/skill_quarantine/queryVerdicts.ts
|
|
@@ -18344,7 +18294,7 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
18344
18294
|
);
|
|
18345
18295
|
const t0 = Date.now();
|
|
18346
18296
|
try {
|
|
18347
|
-
await
|
|
18297
|
+
await reconcileAndSweep(log2);
|
|
18348
18298
|
const installed = await enumerateInstalledSkills(cwd);
|
|
18349
18299
|
log2.info(
|
|
18350
18300
|
{ sessionId, count: installed.length, metric: Metric.SKILLS_CHECKED },
|
|
@@ -18394,7 +18344,7 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
18394
18344
|
// src/features/claude_code/daemon_pid_file.ts
|
|
18395
18345
|
import fs13 from "fs";
|
|
18396
18346
|
import os4 from "os";
|
|
18397
|
-
import
|
|
18347
|
+
import path19 from "path";
|
|
18398
18348
|
|
|
18399
18349
|
// src/features/claude_code/data_collector_constants.ts
|
|
18400
18350
|
var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
|
|
@@ -18415,17 +18365,17 @@ var CONTEXT_SCAN_INTERVAL_MS = 5e3;
|
|
|
18415
18365
|
|
|
18416
18366
|
// src/features/claude_code/daemon_pid_file.ts
|
|
18417
18367
|
function getMobbdevDir() {
|
|
18418
|
-
return
|
|
18368
|
+
return path19.join(os4.homedir(), ".mobbdev");
|
|
18419
18369
|
}
|
|
18420
18370
|
function getDaemonCheckScriptPath() {
|
|
18421
|
-
return
|
|
18371
|
+
return path19.join(getMobbdevDir(), "daemon-check.js");
|
|
18422
18372
|
}
|
|
18423
18373
|
var DaemonPidFile = class {
|
|
18424
18374
|
constructor() {
|
|
18425
18375
|
__publicField(this, "data", null);
|
|
18426
18376
|
}
|
|
18427
18377
|
get filePath() {
|
|
18428
|
-
return
|
|
18378
|
+
return path19.join(getMobbdevDir(), "daemon.pid");
|
|
18429
18379
|
}
|
|
18430
18380
|
/** Ensure ~/.mobbdev/ directory exists. */
|
|
18431
18381
|
ensureDir() {
|
|
@@ -18487,8 +18437,8 @@ var DaemonPidFile = class {
|
|
|
18487
18437
|
// src/features/claude_code/data_collector.ts
|
|
18488
18438
|
import { execFile } from "child_process";
|
|
18489
18439
|
import { createHash as createHash3 } from "crypto";
|
|
18490
|
-
import { access, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
|
|
18491
|
-
import
|
|
18440
|
+
import { access as access2, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
|
|
18441
|
+
import path20 from "path";
|
|
18492
18442
|
import { promisify } from "util";
|
|
18493
18443
|
|
|
18494
18444
|
// src/features/analysis/context_file_uploader.ts
|
|
@@ -18753,8 +18703,8 @@ function createConfigstoreStream(store, opts) {
|
|
|
18753
18703
|
heartbeatBuffer.length = 0;
|
|
18754
18704
|
}
|
|
18755
18705
|
}
|
|
18756
|
-
function setScopePath(
|
|
18757
|
-
scopePath =
|
|
18706
|
+
function setScopePath(path36) {
|
|
18707
|
+
scopePath = path36;
|
|
18758
18708
|
}
|
|
18759
18709
|
return { writable, flush, setScopePath };
|
|
18760
18710
|
}
|
|
@@ -18978,7 +18928,7 @@ function createLogger(config2) {
|
|
|
18978
18928
|
|
|
18979
18929
|
// src/features/claude_code/hook_logger.ts
|
|
18980
18930
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
18981
|
-
var CLI_VERSION = true ? "1.4.
|
|
18931
|
+
var CLI_VERSION = true ? "1.4.2" : "unknown";
|
|
18982
18932
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
18983
18933
|
var claudeCodeVersion;
|
|
18984
18934
|
function buildDdTags() {
|
|
@@ -19065,18 +19015,18 @@ function getCursorKey(transcriptPath) {
|
|
|
19065
19015
|
}
|
|
19066
19016
|
async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
19067
19017
|
try {
|
|
19068
|
-
await
|
|
19018
|
+
await access2(transcriptPath);
|
|
19069
19019
|
return transcriptPath;
|
|
19070
19020
|
} catch {
|
|
19071
19021
|
}
|
|
19072
|
-
const filename =
|
|
19073
|
-
const dirName =
|
|
19074
|
-
const projectsDir =
|
|
19022
|
+
const filename = path20.basename(transcriptPath);
|
|
19023
|
+
const dirName = path20.basename(path20.dirname(transcriptPath));
|
|
19024
|
+
const projectsDir = path20.dirname(path20.dirname(transcriptPath));
|
|
19075
19025
|
const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
|
|
19076
19026
|
if (baseDirName !== dirName) {
|
|
19077
|
-
const candidate =
|
|
19027
|
+
const candidate = path20.join(projectsDir, baseDirName, filename);
|
|
19078
19028
|
try {
|
|
19079
|
-
await
|
|
19029
|
+
await access2(candidate);
|
|
19080
19030
|
hookLog.info(
|
|
19081
19031
|
{
|
|
19082
19032
|
data: {
|
|
@@ -19096,9 +19046,9 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
19096
19046
|
const dirs = await readdir3(projectsDir);
|
|
19097
19047
|
for (const dir of dirs) {
|
|
19098
19048
|
if (dir === dirName) continue;
|
|
19099
|
-
const candidate =
|
|
19049
|
+
const candidate = path20.join(projectsDir, dir, filename);
|
|
19100
19050
|
try {
|
|
19101
|
-
await
|
|
19051
|
+
await access2(candidate);
|
|
19102
19052
|
hookLog.info(
|
|
19103
19053
|
{
|
|
19104
19054
|
data: {
|
|
@@ -19281,7 +19231,7 @@ async function cleanupStaleSessions(configDir) {
|
|
|
19281
19231
|
let deletedCount = 0;
|
|
19282
19232
|
for (const file of files) {
|
|
19283
19233
|
if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
|
|
19284
|
-
const filePath =
|
|
19234
|
+
const filePath = path20.join(configDir, file);
|
|
19285
19235
|
try {
|
|
19286
19236
|
const content = JSON.parse(await readFile3(filePath, "utf-8"));
|
|
19287
19237
|
let newest = 0;
|
|
@@ -19519,14 +19469,14 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
|
19519
19469
|
import fs14 from "fs";
|
|
19520
19470
|
import fsPromises4 from "fs/promises";
|
|
19521
19471
|
import os6 from "os";
|
|
19522
|
-
import
|
|
19472
|
+
import path21 from "path";
|
|
19523
19473
|
import chalk11 from "chalk";
|
|
19524
19474
|
|
|
19525
19475
|
// src/features/claude_code/daemon-check-shim.tmpl.js
|
|
19526
19476
|
var daemon_check_shim_tmpl_default = "// Mobb daemon shim \u2014 checks if daemon is alive, spawns if dead.\n// Auto-generated by mobbdev CLI. Do not edit.\nvar fs = require('fs')\nvar spawn = require('child_process').spawn\nvar path = require('path')\nvar os = require('os')\n\nvar pidFile = path.join(os.homedir(), '.mobbdev', 'daemon.pid')\nvar HEARTBEAT_STALE_MS = __HEARTBEAT_STALE_MS__\n\ntry {\n var data = JSON.parse(fs.readFileSync(pidFile, 'utf8'))\n if (Date.now() - data.heartbeat > HEARTBEAT_STALE_MS) throw new Error('stale')\n process.kill(data.pid, 0) // throws ESRCH if the process is gone\n} catch (e) {\n var localCli = process.env.MOBBDEV_LOCAL_CLI\n var child = localCli\n ? spawn('node', [localCli, 'claude-code-daemon'], { detached: true, stdio: 'ignore', windowsHide: true })\n : spawn('npx', ['--yes', 'mobbdev@latest', 'claude-code-daemon'], { detached: true, stdio: 'ignore', shell: true, windowsHide: true })\n child.unref()\n}\n";
|
|
19527
19477
|
|
|
19528
19478
|
// src/features/claude_code/install_hook.ts
|
|
19529
|
-
var CLAUDE_SETTINGS_PATH =
|
|
19479
|
+
var CLAUDE_SETTINGS_PATH = path21.join(os6.homedir(), ".claude", "settings.json");
|
|
19530
19480
|
var RECOMMENDED_MATCHER = "*";
|
|
19531
19481
|
async function claudeSettingsExists() {
|
|
19532
19482
|
try {
|
|
@@ -19674,16 +19624,16 @@ async function installMobbHooks(options = {}) {
|
|
|
19674
19624
|
// src/features/claude_code/transcript_scanner.ts
|
|
19675
19625
|
import { open as open5, readdir as readdir4, stat as stat3 } from "fs/promises";
|
|
19676
19626
|
import os7 from "os";
|
|
19677
|
-
import
|
|
19627
|
+
import path22 from "path";
|
|
19678
19628
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19679
19629
|
function getClaudeProjectsDirs() {
|
|
19680
19630
|
const dirs = [];
|
|
19681
19631
|
const configDir = process.env["CLAUDE_CONFIG_DIR"];
|
|
19682
19632
|
if (configDir) {
|
|
19683
|
-
dirs.push(
|
|
19633
|
+
dirs.push(path22.join(configDir, "projects"));
|
|
19684
19634
|
}
|
|
19685
|
-
dirs.push(
|
|
19686
|
-
dirs.push(
|
|
19635
|
+
dirs.push(path22.join(os7.homedir(), ".config", "claude", "projects"));
|
|
19636
|
+
dirs.push(path22.join(os7.homedir(), ".claude", "projects"));
|
|
19687
19637
|
return dirs;
|
|
19688
19638
|
}
|
|
19689
19639
|
async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
@@ -19691,7 +19641,7 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
|
19691
19641
|
if (!file.endsWith(".jsonl")) continue;
|
|
19692
19642
|
const sessionId = file.replace(".jsonl", "");
|
|
19693
19643
|
if (!UUID_RE.test(sessionId)) continue;
|
|
19694
|
-
const filePath =
|
|
19644
|
+
const filePath = path22.join(dir, file);
|
|
19695
19645
|
if (seen.has(filePath)) continue;
|
|
19696
19646
|
seen.add(filePath);
|
|
19697
19647
|
let fileStat;
|
|
@@ -19722,7 +19672,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19722
19672
|
continue;
|
|
19723
19673
|
}
|
|
19724
19674
|
for (const projName of projectDirs) {
|
|
19725
|
-
const projPath =
|
|
19675
|
+
const projPath = path22.join(projectsDir, projName);
|
|
19726
19676
|
let projStat;
|
|
19727
19677
|
try {
|
|
19728
19678
|
projStat = await stat3(projPath);
|
|
@@ -19739,7 +19689,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19739
19689
|
await collectJsonlFiles(files, projPath, projPath, seen, now, results);
|
|
19740
19690
|
for (const entry of files) {
|
|
19741
19691
|
if (!UUID_RE.test(entry)) continue;
|
|
19742
|
-
const subagentsDir =
|
|
19692
|
+
const subagentsDir = path22.join(projPath, entry, "subagents");
|
|
19743
19693
|
try {
|
|
19744
19694
|
const s = await stat3(subagentsDir);
|
|
19745
19695
|
if (!s.isDirectory()) continue;
|
|
@@ -19842,7 +19792,7 @@ async function startDaemon() {
|
|
|
19842
19792
|
for (const transcript of changed) {
|
|
19843
19793
|
const sessionStore = createSessionConfigStore(transcript.sessionId);
|
|
19844
19794
|
if (!cleanupConfigDir) {
|
|
19845
|
-
cleanupConfigDir =
|
|
19795
|
+
cleanupConfigDir = path23.dirname(sessionStore.path);
|
|
19846
19796
|
}
|
|
19847
19797
|
await drainTranscript(
|
|
19848
19798
|
transcript,
|
|
@@ -20139,8 +20089,8 @@ var WorkspaceService = class {
|
|
|
20139
20089
|
* Sets a known workspace path that was discovered through successful validation
|
|
20140
20090
|
* @param path The validated workspace path to store
|
|
20141
20091
|
*/
|
|
20142
|
-
static setKnownWorkspacePath(
|
|
20143
|
-
this.knownWorkspacePath =
|
|
20092
|
+
static setKnownWorkspacePath(path36) {
|
|
20093
|
+
this.knownWorkspacePath = path36;
|
|
20144
20094
|
}
|
|
20145
20095
|
/**
|
|
20146
20096
|
* Gets the known workspace path that was previously validated
|
|
@@ -21001,7 +20951,7 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
21001
20951
|
import { execSync as execSync2 } from "child_process";
|
|
21002
20952
|
import fs15 from "fs";
|
|
21003
20953
|
import os8 from "os";
|
|
21004
|
-
import
|
|
20954
|
+
import path24 from "path";
|
|
21005
20955
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
21006
20956
|
var runCommand = (cmd) => {
|
|
21007
20957
|
try {
|
|
@@ -21016,7 +20966,7 @@ var gitInfo = {
|
|
|
21016
20966
|
};
|
|
21017
20967
|
var getClaudeWorkspacePaths = () => {
|
|
21018
20968
|
const home = os8.homedir();
|
|
21019
|
-
const claudeIdePath =
|
|
20969
|
+
const claudeIdePath = path24.join(home, ".claude", "ide");
|
|
21020
20970
|
const workspacePaths = [];
|
|
21021
20971
|
if (!fs15.existsSync(claudeIdePath)) {
|
|
21022
20972
|
return workspacePaths;
|
|
@@ -21024,7 +20974,7 @@ var getClaudeWorkspacePaths = () => {
|
|
|
21024
20974
|
try {
|
|
21025
20975
|
const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
|
|
21026
20976
|
for (const lockFile of lockFiles) {
|
|
21027
|
-
const lockFilePath =
|
|
20977
|
+
const lockFilePath = path24.join(claudeIdePath, lockFile);
|
|
21028
20978
|
try {
|
|
21029
20979
|
const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
|
|
21030
20980
|
if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
|
|
@@ -21049,24 +20999,24 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
21049
20999
|
switch (hostName.toLowerCase()) {
|
|
21050
21000
|
case "cursor":
|
|
21051
21001
|
return [
|
|
21052
|
-
|
|
21002
|
+
path24.join(currentDir, ".cursor", "mcp.json"),
|
|
21053
21003
|
// local first
|
|
21054
|
-
|
|
21004
|
+
path24.join(home, ".cursor", "mcp.json")
|
|
21055
21005
|
];
|
|
21056
21006
|
case "windsurf":
|
|
21057
21007
|
return [
|
|
21058
|
-
|
|
21008
|
+
path24.join(currentDir, ".codeium", "mcp_config.json"),
|
|
21059
21009
|
// local first
|
|
21060
|
-
|
|
21010
|
+
path24.join(home, ".codeium", "windsurf", "mcp_config.json")
|
|
21061
21011
|
];
|
|
21062
21012
|
case "webstorm":
|
|
21063
21013
|
return [];
|
|
21064
21014
|
case "visualstudiocode":
|
|
21065
21015
|
case "vscode":
|
|
21066
21016
|
return [
|
|
21067
|
-
|
|
21017
|
+
path24.join(currentDir, ".vscode", "mcp.json"),
|
|
21068
21018
|
// local first
|
|
21069
|
-
process.platform === "win32" ?
|
|
21019
|
+
process.platform === "win32" ? path24.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path24.join(
|
|
21070
21020
|
home,
|
|
21071
21021
|
"Library",
|
|
21072
21022
|
"Application Support",
|
|
@@ -21077,13 +21027,13 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
21077
21027
|
];
|
|
21078
21028
|
case "claude": {
|
|
21079
21029
|
const claudePaths = [
|
|
21080
|
-
|
|
21030
|
+
path24.join(currentDir, ".claude.json"),
|
|
21081
21031
|
// local first
|
|
21082
|
-
|
|
21032
|
+
path24.join(home, ".claude.json")
|
|
21083
21033
|
];
|
|
21084
21034
|
const workspacePaths = getClaudeWorkspacePaths();
|
|
21085
21035
|
for (const workspacePath of workspacePaths) {
|
|
21086
|
-
claudePaths.push(
|
|
21036
|
+
claudePaths.push(path24.join(workspacePath, ".mcp.json"));
|
|
21087
21037
|
}
|
|
21088
21038
|
return claudePaths;
|
|
21089
21039
|
}
|
|
@@ -21244,10 +21194,10 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
21244
21194
|
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
21245
21195
|
for (const ide of IDEs) {
|
|
21246
21196
|
const configPaths = getMCPConfigPaths(ide);
|
|
21247
|
-
configPaths.forEach((
|
|
21197
|
+
configPaths.forEach((path36) => ideConfigPaths.add(path36));
|
|
21248
21198
|
}
|
|
21249
21199
|
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
21250
|
-
(
|
|
21200
|
+
(path36) => !ideConfigPaths.has(path36)
|
|
21251
21201
|
);
|
|
21252
21202
|
for (const ide of IDEs) {
|
|
21253
21203
|
const cfg = readMCPConfig(ide);
|
|
@@ -21369,7 +21319,7 @@ init_configs();
|
|
|
21369
21319
|
init_configs();
|
|
21370
21320
|
import fs16 from "fs";
|
|
21371
21321
|
import os9 from "os";
|
|
21372
|
-
import
|
|
21322
|
+
import path25 from "path";
|
|
21373
21323
|
var MAX_DEPTH = 2;
|
|
21374
21324
|
var patterns = ["mcp", "claude"];
|
|
21375
21325
|
var isFileMatch = (fileName) => {
|
|
@@ -21389,7 +21339,7 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
21389
21339
|
if (depth > MAX_DEPTH) return results;
|
|
21390
21340
|
const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
21391
21341
|
for (const entry of entries) {
|
|
21392
|
-
const fullPath =
|
|
21342
|
+
const fullPath = path25.join(dir, entry.name);
|
|
21393
21343
|
if (entry.isFile() && isFileMatch(entry.name)) {
|
|
21394
21344
|
results.push(fullPath);
|
|
21395
21345
|
} else if (entry.isDirectory()) {
|
|
@@ -21406,14 +21356,14 @@ var findSystemMCPConfigs = async () => {
|
|
|
21406
21356
|
const home = os9.homedir();
|
|
21407
21357
|
const platform2 = os9.platform();
|
|
21408
21358
|
const knownDirs = platform2 === "win32" ? [
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
|
|
21359
|
+
path25.join(home, ".cursor"),
|
|
21360
|
+
path25.join(home, "Documents"),
|
|
21361
|
+
path25.join(home, "Downloads")
|
|
21412
21362
|
] : [
|
|
21413
|
-
|
|
21414
|
-
process.env["XDG_CONFIG_HOME"] ||
|
|
21415
|
-
|
|
21416
|
-
|
|
21363
|
+
path25.join(home, ".cursor"),
|
|
21364
|
+
process.env["XDG_CONFIG_HOME"] || path25.join(home, ".config"),
|
|
21365
|
+
path25.join(home, "Documents"),
|
|
21366
|
+
path25.join(home, "Downloads")
|
|
21417
21367
|
];
|
|
21418
21368
|
const timeoutPromise = new Promise(
|
|
21419
21369
|
(resolve) => setTimeout(() => {
|
|
@@ -23829,13 +23779,13 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
23829
23779
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
23830
23780
|
import * as fs19 from "fs";
|
|
23831
23781
|
import * as os12 from "os";
|
|
23832
|
-
import * as
|
|
23782
|
+
import * as path27 from "path";
|
|
23833
23783
|
|
|
23834
23784
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
23835
23785
|
init_configs();
|
|
23836
23786
|
import * as fs18 from "fs";
|
|
23837
23787
|
import fetch7 from "node-fetch";
|
|
23838
|
-
import * as
|
|
23788
|
+
import * as path26 from "path";
|
|
23839
23789
|
|
|
23840
23790
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
23841
23791
|
import * as fs17 from "fs";
|
|
@@ -23844,14 +23794,14 @@ import * as os11 from "os";
|
|
|
23844
23794
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
23845
23795
|
import * as fs20 from "fs";
|
|
23846
23796
|
import * as os13 from "os";
|
|
23847
|
-
import * as
|
|
23797
|
+
import * as path28 from "path";
|
|
23848
23798
|
|
|
23849
23799
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
23850
23800
|
import { z as z42 } from "zod";
|
|
23851
23801
|
|
|
23852
23802
|
// src/mcp/services/PathValidation.ts
|
|
23853
23803
|
import fs21 from "fs";
|
|
23854
|
-
import
|
|
23804
|
+
import path29 from "path";
|
|
23855
23805
|
async function validatePath(inputPath) {
|
|
23856
23806
|
logDebug("Validating MCP path", { inputPath });
|
|
23857
23807
|
if (/^\/[a-zA-Z]:\//.test(inputPath)) {
|
|
@@ -23883,7 +23833,7 @@ async function validatePath(inputPath) {
|
|
|
23883
23833
|
logError(error);
|
|
23884
23834
|
return { isValid: false, error, path: inputPath };
|
|
23885
23835
|
}
|
|
23886
|
-
const normalizedPath =
|
|
23836
|
+
const normalizedPath = path29.normalize(inputPath);
|
|
23887
23837
|
if (normalizedPath.includes("..")) {
|
|
23888
23838
|
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
23889
23839
|
logError(error);
|
|
@@ -24535,7 +24485,7 @@ init_configs();
|
|
|
24535
24485
|
import fs22 from "fs/promises";
|
|
24536
24486
|
import nodePath from "path";
|
|
24537
24487
|
var getLocalFiles = async ({
|
|
24538
|
-
path:
|
|
24488
|
+
path: path36,
|
|
24539
24489
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
24540
24490
|
maxFiles,
|
|
24541
24491
|
isAllFilesScan,
|
|
@@ -24543,17 +24493,17 @@ var getLocalFiles = async ({
|
|
|
24543
24493
|
scanRecentlyChangedFiles
|
|
24544
24494
|
}) => {
|
|
24545
24495
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
24546
|
-
path:
|
|
24496
|
+
path: path36,
|
|
24547
24497
|
maxFileSize,
|
|
24548
24498
|
maxFiles,
|
|
24549
24499
|
isAllFilesScan,
|
|
24550
24500
|
scanRecentlyChangedFiles
|
|
24551
24501
|
});
|
|
24552
24502
|
try {
|
|
24553
|
-
const resolvedRepoPath = await fs22.realpath(
|
|
24503
|
+
const resolvedRepoPath = await fs22.realpath(path36);
|
|
24554
24504
|
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
24555
24505
|
resolvedRepoPath,
|
|
24556
|
-
originalPath:
|
|
24506
|
+
originalPath: path36
|
|
24557
24507
|
});
|
|
24558
24508
|
const gitService = new GitService(resolvedRepoPath, log);
|
|
24559
24509
|
const gitValidation = await gitService.validateRepository();
|
|
@@ -24566,7 +24516,7 @@ var getLocalFiles = async ({
|
|
|
24566
24516
|
if (!gitValidation.isValid || isAllFilesScan) {
|
|
24567
24517
|
try {
|
|
24568
24518
|
files = await FileUtils.getLastChangedFiles({
|
|
24569
|
-
dir:
|
|
24519
|
+
dir: path36,
|
|
24570
24520
|
maxFileSize,
|
|
24571
24521
|
maxFiles,
|
|
24572
24522
|
isAllFilesScan
|
|
@@ -24658,7 +24608,7 @@ var getLocalFiles = async ({
|
|
|
24658
24608
|
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
24659
24609
|
error: error instanceof Error ? error.message : String(error),
|
|
24660
24610
|
stack: error instanceof Error ? error.stack : void 0,
|
|
24661
|
-
path:
|
|
24611
|
+
path: path36
|
|
24662
24612
|
});
|
|
24663
24613
|
throw error;
|
|
24664
24614
|
}
|
|
@@ -24668,7 +24618,7 @@ var getLocalFiles = async ({
|
|
|
24668
24618
|
init_client_generates();
|
|
24669
24619
|
init_GitService();
|
|
24670
24620
|
import fs23 from "fs";
|
|
24671
|
-
import
|
|
24621
|
+
import path30 from "path";
|
|
24672
24622
|
import { z as z41 } from "zod";
|
|
24673
24623
|
function extractPathFromPatch(patch) {
|
|
24674
24624
|
const match = patch?.match(/diff --git a\/([^\s]+) b\//);
|
|
@@ -24754,7 +24704,7 @@ var LocalMobbFolderService = class {
|
|
|
24754
24704
|
"[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
|
|
24755
24705
|
);
|
|
24756
24706
|
}
|
|
24757
|
-
const mobbFolderPath =
|
|
24707
|
+
const mobbFolderPath = path30.join(
|
|
24758
24708
|
this.repoPath,
|
|
24759
24709
|
this.defaultMobbFolderName
|
|
24760
24710
|
);
|
|
@@ -24926,7 +24876,7 @@ var LocalMobbFolderService = class {
|
|
|
24926
24876
|
mobbFolderPath,
|
|
24927
24877
|
baseFileName
|
|
24928
24878
|
);
|
|
24929
|
-
const filePath =
|
|
24879
|
+
const filePath = path30.join(mobbFolderPath, uniqueFileName);
|
|
24930
24880
|
await fs23.promises.writeFile(filePath, patch, "utf8");
|
|
24931
24881
|
logInfo("[LocalMobbFolderService] Patch saved successfully", {
|
|
24932
24882
|
filePath,
|
|
@@ -24984,11 +24934,11 @@ var LocalMobbFolderService = class {
|
|
|
24984
24934
|
* @returns Unique filename that doesn't conflict with existing files
|
|
24985
24935
|
*/
|
|
24986
24936
|
getUniqueFileName(folderPath, baseFileName) {
|
|
24987
|
-
const baseName =
|
|
24988
|
-
const extension =
|
|
24937
|
+
const baseName = path30.parse(baseFileName).name;
|
|
24938
|
+
const extension = path30.parse(baseFileName).ext;
|
|
24989
24939
|
let uniqueFileName = baseFileName;
|
|
24990
24940
|
let index = 1;
|
|
24991
|
-
while (fs23.existsSync(
|
|
24941
|
+
while (fs23.existsSync(path30.join(folderPath, uniqueFileName))) {
|
|
24992
24942
|
uniqueFileName = `${baseName}-${index}${extension}`;
|
|
24993
24943
|
index++;
|
|
24994
24944
|
if (index > 1e3) {
|
|
@@ -25019,7 +24969,7 @@ var LocalMobbFolderService = class {
|
|
|
25019
24969
|
logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
|
|
25020
24970
|
try {
|
|
25021
24971
|
const mobbFolderPath = await this.getFolder();
|
|
25022
|
-
const patchInfoPath =
|
|
24972
|
+
const patchInfoPath = path30.join(mobbFolderPath, "patchInfo.md");
|
|
25023
24973
|
const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
|
|
25024
24974
|
let existingContent = "";
|
|
25025
24975
|
if (fs23.existsSync(patchInfoPath)) {
|
|
@@ -25061,7 +25011,7 @@ var LocalMobbFolderService = class {
|
|
|
25061
25011
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
25062
25012
|
const patch = this.extractPatchFromFix(fix);
|
|
25063
25013
|
const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
|
|
25064
|
-
const patchedFilePath = relativePatchedFilePath ?
|
|
25014
|
+
const patchedFilePath = relativePatchedFilePath ? path30.resolve(this.repoPath, relativePatchedFilePath) : null;
|
|
25065
25015
|
const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
|
|
25066
25016
|
let markdown = `# Fix ${fixIdentifier}
|
|
25067
25017
|
|
|
@@ -25397,7 +25347,7 @@ var LocalMobbFolderService = class {
|
|
|
25397
25347
|
// src/mcp/services/PatchApplicationService.ts
|
|
25398
25348
|
init_configs();
|
|
25399
25349
|
import {
|
|
25400
|
-
existsSync as
|
|
25350
|
+
existsSync as existsSync6,
|
|
25401
25351
|
mkdirSync,
|
|
25402
25352
|
readFileSync as readFileSync4,
|
|
25403
25353
|
unlinkSync,
|
|
@@ -25405,14 +25355,14 @@ import {
|
|
|
25405
25355
|
} from "fs";
|
|
25406
25356
|
import fs24 from "fs/promises";
|
|
25407
25357
|
import parseDiff2 from "parse-diff";
|
|
25408
|
-
import
|
|
25358
|
+
import path31 from "path";
|
|
25409
25359
|
var PatchApplicationService = class {
|
|
25410
25360
|
/**
|
|
25411
25361
|
* Gets the appropriate comment syntax for a file based on its extension
|
|
25412
25362
|
*/
|
|
25413
25363
|
static getCommentSyntax(filePath) {
|
|
25414
|
-
const ext =
|
|
25415
|
-
const basename2 =
|
|
25364
|
+
const ext = path31.extname(filePath).toLowerCase();
|
|
25365
|
+
const basename2 = path31.basename(filePath);
|
|
25416
25366
|
const commentMap = {
|
|
25417
25367
|
// C-style languages (single line comments)
|
|
25418
25368
|
".js": "//",
|
|
@@ -25620,7 +25570,7 @@ var PatchApplicationService = class {
|
|
|
25620
25570
|
}
|
|
25621
25571
|
);
|
|
25622
25572
|
}
|
|
25623
|
-
const dirPath =
|
|
25573
|
+
const dirPath = path31.dirname(normalizedFilePath);
|
|
25624
25574
|
mkdirSync(dirPath, { recursive: true });
|
|
25625
25575
|
writeFileSync3(normalizedFilePath, finalContent, "utf8");
|
|
25626
25576
|
return normalizedFilePath;
|
|
@@ -25629,9 +25579,9 @@ var PatchApplicationService = class {
|
|
|
25629
25579
|
repositoryPath,
|
|
25630
25580
|
targetPath
|
|
25631
25581
|
}) {
|
|
25632
|
-
const repoRoot =
|
|
25633
|
-
const normalizedPath =
|
|
25634
|
-
const repoRootWithSep = repoRoot.endsWith(
|
|
25582
|
+
const repoRoot = path31.resolve(repositoryPath);
|
|
25583
|
+
const normalizedPath = path31.resolve(repoRoot, targetPath);
|
|
25584
|
+
const repoRootWithSep = repoRoot.endsWith(path31.sep) ? repoRoot : `${repoRoot}${path31.sep}`;
|
|
25635
25585
|
if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
|
|
25636
25586
|
throw new Error(
|
|
25637
25587
|
`Security violation: target path ${targetPath} resolves outside repository`
|
|
@@ -25640,7 +25590,7 @@ var PatchApplicationService = class {
|
|
|
25640
25590
|
return {
|
|
25641
25591
|
repoRoot,
|
|
25642
25592
|
normalizedPath,
|
|
25643
|
-
relativePath:
|
|
25593
|
+
relativePath: path31.relative(repoRoot, normalizedPath)
|
|
25644
25594
|
};
|
|
25645
25595
|
}
|
|
25646
25596
|
/**
|
|
@@ -25922,8 +25872,8 @@ var PatchApplicationService = class {
|
|
|
25922
25872
|
continue;
|
|
25923
25873
|
}
|
|
25924
25874
|
try {
|
|
25925
|
-
const absolutePath =
|
|
25926
|
-
if (
|
|
25875
|
+
const absolutePath = path31.resolve(repositoryPath, targetFile);
|
|
25876
|
+
if (existsSync6(absolutePath)) {
|
|
25927
25877
|
const stats = await fs24.stat(absolutePath);
|
|
25928
25878
|
const fileModTime = stats.mtime.getTime();
|
|
25929
25879
|
if (fileModTime > scanStartTime) {
|
|
@@ -26124,7 +26074,7 @@ var PatchApplicationService = class {
|
|
|
26124
26074
|
targetFile,
|
|
26125
26075
|
absoluteFilePath,
|
|
26126
26076
|
relativePath,
|
|
26127
|
-
exists:
|
|
26077
|
+
exists: existsSync6(absoluteFilePath)
|
|
26128
26078
|
});
|
|
26129
26079
|
return { absoluteFilePath, relativePath };
|
|
26130
26080
|
}
|
|
@@ -26148,7 +26098,7 @@ var PatchApplicationService = class {
|
|
|
26148
26098
|
fix,
|
|
26149
26099
|
scanContext
|
|
26150
26100
|
});
|
|
26151
|
-
appliedFiles.push(
|
|
26101
|
+
appliedFiles.push(path31.relative(repositoryPath, actualPath));
|
|
26152
26102
|
logDebug(`[${scanContext}] Created new file: ${relativePath}`);
|
|
26153
26103
|
}
|
|
26154
26104
|
/**
|
|
@@ -26160,7 +26110,7 @@ var PatchApplicationService = class {
|
|
|
26160
26110
|
appliedFiles,
|
|
26161
26111
|
scanContext
|
|
26162
26112
|
}) {
|
|
26163
|
-
if (
|
|
26113
|
+
if (existsSync6(absoluteFilePath)) {
|
|
26164
26114
|
unlinkSync(absoluteFilePath);
|
|
26165
26115
|
appliedFiles.push(relativePath);
|
|
26166
26116
|
logDebug(`[${scanContext}] Deleted file: ${relativePath}`);
|
|
@@ -26179,7 +26129,7 @@ var PatchApplicationService = class {
|
|
|
26179
26129
|
appliedFiles,
|
|
26180
26130
|
scanContext
|
|
26181
26131
|
}) {
|
|
26182
|
-
if (!
|
|
26132
|
+
if (!existsSync6(absoluteFilePath)) {
|
|
26183
26133
|
throw new Error(
|
|
26184
26134
|
`Target file does not exist: ${targetFile} (resolved to: ${absoluteFilePath})`
|
|
26185
26135
|
);
|
|
@@ -26197,7 +26147,7 @@ var PatchApplicationService = class {
|
|
|
26197
26147
|
fix,
|
|
26198
26148
|
scanContext
|
|
26199
26149
|
});
|
|
26200
|
-
appliedFiles.push(
|
|
26150
|
+
appliedFiles.push(path31.relative(repositoryPath, actualPath));
|
|
26201
26151
|
logDebug(`[${scanContext}] Modified file: ${relativePath}`);
|
|
26202
26152
|
}
|
|
26203
26153
|
}
|
|
@@ -26394,8 +26344,8 @@ init_configs();
|
|
|
26394
26344
|
// src/mcp/services/FileOperations.ts
|
|
26395
26345
|
init_FileUtils();
|
|
26396
26346
|
import fs25 from "fs";
|
|
26397
|
-
import
|
|
26398
|
-
import
|
|
26347
|
+
import path32 from "path";
|
|
26348
|
+
import AdmZip5 from "adm-zip";
|
|
26399
26349
|
var FileOperations = class {
|
|
26400
26350
|
/**
|
|
26401
26351
|
* Creates a ZIP archive containing the specified source files
|
|
@@ -26410,14 +26360,14 @@ var FileOperations = class {
|
|
|
26410
26360
|
maxFileSize
|
|
26411
26361
|
}) {
|
|
26412
26362
|
logDebug("[FileOperations] Packing files");
|
|
26413
|
-
const zip = new
|
|
26363
|
+
const zip = new AdmZip5();
|
|
26414
26364
|
let packedFilesCount = 0;
|
|
26415
26365
|
const packedFiles = [];
|
|
26416
26366
|
const excludedFiles = [];
|
|
26417
|
-
const resolvedRepoPath =
|
|
26367
|
+
const resolvedRepoPath = path32.resolve(repositoryPath);
|
|
26418
26368
|
for (const filepath of fileList) {
|
|
26419
|
-
const absoluteFilepath =
|
|
26420
|
-
const resolvedFilePath =
|
|
26369
|
+
const absoluteFilepath = path32.join(repositoryPath, filepath);
|
|
26370
|
+
const resolvedFilePath = path32.resolve(absoluteFilepath);
|
|
26421
26371
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26422
26372
|
const reason = "potential path traversal security risk";
|
|
26423
26373
|
logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
|
|
@@ -26464,11 +26414,11 @@ var FileOperations = class {
|
|
|
26464
26414
|
fileList,
|
|
26465
26415
|
repositoryPath
|
|
26466
26416
|
}) {
|
|
26467
|
-
const resolvedRepoPath =
|
|
26417
|
+
const resolvedRepoPath = path32.resolve(repositoryPath);
|
|
26468
26418
|
const validatedPaths = [];
|
|
26469
26419
|
for (const filepath of fileList) {
|
|
26470
|
-
const absoluteFilepath =
|
|
26471
|
-
const resolvedFilePath =
|
|
26420
|
+
const absoluteFilepath = path32.join(repositoryPath, filepath);
|
|
26421
|
+
const resolvedFilePath = path32.resolve(absoluteFilepath);
|
|
26472
26422
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26473
26423
|
logDebug(
|
|
26474
26424
|
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
@@ -26496,7 +26446,7 @@ var FileOperations = class {
|
|
|
26496
26446
|
for (const absolutePath of filePaths) {
|
|
26497
26447
|
try {
|
|
26498
26448
|
const content = await fs25.promises.readFile(absolutePath);
|
|
26499
|
-
const relativePath =
|
|
26449
|
+
const relativePath = path32.basename(absolutePath);
|
|
26500
26450
|
fileDataArray.push({
|
|
26501
26451
|
relativePath,
|
|
26502
26452
|
absolutePath,
|
|
@@ -26808,14 +26758,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26808
26758
|
* since the last scan.
|
|
26809
26759
|
*/
|
|
26810
26760
|
async scanForSecurityVulnerabilities({
|
|
26811
|
-
path:
|
|
26761
|
+
path: path36,
|
|
26812
26762
|
isAllDetectionRulesScan,
|
|
26813
26763
|
isAllFilesScan,
|
|
26814
26764
|
scanContext
|
|
26815
26765
|
}) {
|
|
26816
26766
|
this.hasAuthenticationFailed = false;
|
|
26817
26767
|
logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
|
|
26818
|
-
path:
|
|
26768
|
+
path: path36
|
|
26819
26769
|
});
|
|
26820
26770
|
if (!this.gqlClient) {
|
|
26821
26771
|
logInfo(`[${scanContext}] No GQL client found, skipping scan`);
|
|
@@ -26831,11 +26781,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26831
26781
|
}
|
|
26832
26782
|
logDebug(
|
|
26833
26783
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
26834
|
-
{ path:
|
|
26784
|
+
{ path: path36 }
|
|
26835
26785
|
);
|
|
26836
26786
|
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
26837
26787
|
const files = await getLocalFiles({
|
|
26838
|
-
path:
|
|
26788
|
+
path: path36,
|
|
26839
26789
|
isAllFilesScan,
|
|
26840
26790
|
scanContext,
|
|
26841
26791
|
scanRecentlyChangedFiles: !isBackgroundScan
|
|
@@ -26861,13 +26811,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26861
26811
|
});
|
|
26862
26812
|
const { fixReportId, projectId } = await scanFiles({
|
|
26863
26813
|
fileList: filesToScan.map((file) => file.relativePath),
|
|
26864
|
-
repositoryPath:
|
|
26814
|
+
repositoryPath: path36,
|
|
26865
26815
|
gqlClient: this.gqlClient,
|
|
26866
26816
|
isAllDetectionRulesScan,
|
|
26867
26817
|
scanContext
|
|
26868
26818
|
});
|
|
26869
26819
|
logInfo(
|
|
26870
|
-
`[${scanContext}] Security scan completed for ${
|
|
26820
|
+
`[${scanContext}] Security scan completed for ${path36} reportId: ${fixReportId} projectId: ${projectId}`
|
|
26871
26821
|
);
|
|
26872
26822
|
if (isAllFilesScan) {
|
|
26873
26823
|
return;
|
|
@@ -27161,13 +27111,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27161
27111
|
});
|
|
27162
27112
|
return scannedFiles.some((file) => file.relativePath === fixFile);
|
|
27163
27113
|
}
|
|
27164
|
-
async getFreshFixes({ path:
|
|
27114
|
+
async getFreshFixes({ path: path36 }) {
|
|
27165
27115
|
const scanContext = ScanContext.USER_REQUEST;
|
|
27166
|
-
logDebug(`[${scanContext}] Getting fresh fixes`, { path:
|
|
27167
|
-
if (this.path !==
|
|
27168
|
-
this.path =
|
|
27116
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path36 });
|
|
27117
|
+
if (this.path !== path36) {
|
|
27118
|
+
this.path = path36;
|
|
27169
27119
|
this.reset();
|
|
27170
|
-
logInfo(`[${scanContext}] Reset service state for new path`, { path:
|
|
27120
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path36 });
|
|
27171
27121
|
}
|
|
27172
27122
|
try {
|
|
27173
27123
|
const loginContext = createMcpLoginContext("check_new_fixes");
|
|
@@ -27186,7 +27136,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27186
27136
|
}
|
|
27187
27137
|
throw error;
|
|
27188
27138
|
}
|
|
27189
|
-
this.triggerScan({ path:
|
|
27139
|
+
this.triggerScan({ path: path36, gqlClient: this.gqlClient });
|
|
27190
27140
|
let isMvsAutoFixEnabled = null;
|
|
27191
27141
|
try {
|
|
27192
27142
|
isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
|
|
@@ -27220,33 +27170,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27220
27170
|
return noFreshFixesPrompt;
|
|
27221
27171
|
}
|
|
27222
27172
|
triggerScan({
|
|
27223
|
-
path:
|
|
27173
|
+
path: path36,
|
|
27224
27174
|
gqlClient
|
|
27225
27175
|
}) {
|
|
27226
|
-
if (this.path !==
|
|
27227
|
-
this.path =
|
|
27176
|
+
if (this.path !== path36) {
|
|
27177
|
+
this.path = path36;
|
|
27228
27178
|
this.reset();
|
|
27229
|
-
logInfo(`Reset service state for new path in triggerScan`, { path:
|
|
27179
|
+
logInfo(`Reset service state for new path in triggerScan`, { path: path36 });
|
|
27230
27180
|
}
|
|
27231
27181
|
this.gqlClient = gqlClient;
|
|
27232
27182
|
if (!this.intervalId) {
|
|
27233
|
-
this.startPeriodicScanning(
|
|
27234
|
-
this.executeInitialScan(
|
|
27235
|
-
void this.executeInitialFullScan(
|
|
27183
|
+
this.startPeriodicScanning(path36);
|
|
27184
|
+
this.executeInitialScan(path36);
|
|
27185
|
+
void this.executeInitialFullScan(path36);
|
|
27236
27186
|
}
|
|
27237
27187
|
}
|
|
27238
|
-
startPeriodicScanning(
|
|
27188
|
+
startPeriodicScanning(path36) {
|
|
27239
27189
|
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
27240
27190
|
logDebug(
|
|
27241
27191
|
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
27242
27192
|
{
|
|
27243
|
-
path:
|
|
27193
|
+
path: path36
|
|
27244
27194
|
}
|
|
27245
27195
|
);
|
|
27246
27196
|
this.intervalId = setInterval(() => {
|
|
27247
|
-
logDebug(`[${scanContext}] Triggering periodic security scan`, { path:
|
|
27197
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path36 });
|
|
27248
27198
|
this.scanForSecurityVulnerabilities({
|
|
27249
|
-
path:
|
|
27199
|
+
path: path36,
|
|
27250
27200
|
scanContext
|
|
27251
27201
|
}).catch((error) => {
|
|
27252
27202
|
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
@@ -27255,45 +27205,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27255
27205
|
});
|
|
27256
27206
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
27257
27207
|
}
|
|
27258
|
-
async executeInitialFullScan(
|
|
27208
|
+
async executeInitialFullScan(path36) {
|
|
27259
27209
|
const scanContext = ScanContext.FULL_SCAN;
|
|
27260
|
-
logDebug(`[${scanContext}] Triggering initial full security scan`, { path:
|
|
27210
|
+
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path36 });
|
|
27261
27211
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
27262
27212
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
27263
27213
|
});
|
|
27264
|
-
if (this.fullScanPathsScanned.includes(
|
|
27214
|
+
if (this.fullScanPathsScanned.includes(path36)) {
|
|
27265
27215
|
logDebug(`[${scanContext}] Full scan already executed for this path`, {
|
|
27266
|
-
path:
|
|
27216
|
+
path: path36
|
|
27267
27217
|
});
|
|
27268
27218
|
return;
|
|
27269
27219
|
}
|
|
27270
27220
|
configStore.set("fullScanPathsScanned", [
|
|
27271
27221
|
...this.fullScanPathsScanned,
|
|
27272
|
-
|
|
27222
|
+
path36
|
|
27273
27223
|
]);
|
|
27274
27224
|
try {
|
|
27275
27225
|
await this.scanForSecurityVulnerabilities({
|
|
27276
|
-
path:
|
|
27226
|
+
path: path36,
|
|
27277
27227
|
isAllFilesScan: true,
|
|
27278
27228
|
isAllDetectionRulesScan: true,
|
|
27279
27229
|
scanContext: ScanContext.FULL_SCAN
|
|
27280
27230
|
});
|
|
27281
|
-
if (!this.fullScanPathsScanned.includes(
|
|
27282
|
-
this.fullScanPathsScanned.push(
|
|
27231
|
+
if (!this.fullScanPathsScanned.includes(path36)) {
|
|
27232
|
+
this.fullScanPathsScanned.push(path36);
|
|
27283
27233
|
configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
|
|
27284
27234
|
}
|
|
27285
|
-
logInfo(`[${scanContext}] Full scan completed`, { path:
|
|
27235
|
+
logInfo(`[${scanContext}] Full scan completed`, { path: path36 });
|
|
27286
27236
|
} catch (error) {
|
|
27287
27237
|
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
27288
27238
|
error
|
|
27289
27239
|
});
|
|
27290
27240
|
}
|
|
27291
27241
|
}
|
|
27292
|
-
executeInitialScan(
|
|
27242
|
+
executeInitialScan(path36) {
|
|
27293
27243
|
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
27294
|
-
logDebug(`[${scanContext}] Triggering initial security scan`, { path:
|
|
27244
|
+
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path36 });
|
|
27295
27245
|
this.scanForSecurityVulnerabilities({
|
|
27296
|
-
path:
|
|
27246
|
+
path: path36,
|
|
27297
27247
|
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
27298
27248
|
}).catch((error) => {
|
|
27299
27249
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
@@ -27390,9 +27340,9 @@ Example payload:
|
|
|
27390
27340
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27391
27341
|
);
|
|
27392
27342
|
}
|
|
27393
|
-
const
|
|
27343
|
+
const path36 = pathValidationResult.path;
|
|
27394
27344
|
const resultText = await this.newFixesService.getFreshFixes({
|
|
27395
|
-
path:
|
|
27345
|
+
path: path36
|
|
27396
27346
|
});
|
|
27397
27347
|
logInfo("CheckForNewAvailableFixesTool execution completed", {
|
|
27398
27348
|
resultText
|
|
@@ -27570,8 +27520,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
27570
27520
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27571
27521
|
);
|
|
27572
27522
|
}
|
|
27573
|
-
const
|
|
27574
|
-
const gitService = new GitService(
|
|
27523
|
+
const path36 = pathValidationResult.path;
|
|
27524
|
+
const gitService = new GitService(path36, log);
|
|
27575
27525
|
const gitValidation = await gitService.validateRepository();
|
|
27576
27526
|
if (!gitValidation.isValid) {
|
|
27577
27527
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -27956,9 +27906,9 @@ Example payload:
|
|
|
27956
27906
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27957
27907
|
);
|
|
27958
27908
|
}
|
|
27959
|
-
const
|
|
27909
|
+
const path36 = pathValidationResult.path;
|
|
27960
27910
|
const files = await getLocalFiles({
|
|
27961
|
-
path:
|
|
27911
|
+
path: path36,
|
|
27962
27912
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
27963
27913
|
maxFiles: args.maxFiles,
|
|
27964
27914
|
scanContext: ScanContext.USER_REQUEST,
|
|
@@ -27978,7 +27928,7 @@ Example payload:
|
|
|
27978
27928
|
try {
|
|
27979
27929
|
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
27980
27930
|
fileList: files.map((file) => file.relativePath),
|
|
27981
|
-
repositoryPath:
|
|
27931
|
+
repositoryPath: path36,
|
|
27982
27932
|
offset: args.offset,
|
|
27983
27933
|
limit: args.limit,
|
|
27984
27934
|
isRescan: args.rescan || !!args.maxFiles
|
|
@@ -28279,10 +28229,10 @@ init_client_generates();
|
|
|
28279
28229
|
init_urlParser2();
|
|
28280
28230
|
|
|
28281
28231
|
// src/features/codeium_intellij/codeium_language_server_grpc_client.ts
|
|
28282
|
-
import
|
|
28232
|
+
import path33 from "path";
|
|
28283
28233
|
import * as grpc from "@grpc/grpc-js";
|
|
28284
28234
|
import * as protoLoader from "@grpc/proto-loader";
|
|
28285
|
-
var PROTO_PATH =
|
|
28235
|
+
var PROTO_PATH = path33.join(
|
|
28286
28236
|
getModuleRootDir(),
|
|
28287
28237
|
"src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
|
|
28288
28238
|
);
|
|
@@ -28294,7 +28244,7 @@ function loadProto() {
|
|
|
28294
28244
|
defaults: true,
|
|
28295
28245
|
oneofs: true,
|
|
28296
28246
|
includeDirs: [
|
|
28297
|
-
|
|
28247
|
+
path33.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
|
|
28298
28248
|
]
|
|
28299
28249
|
});
|
|
28300
28250
|
return grpc.loadPackageDefinition(
|
|
@@ -28350,28 +28300,28 @@ async function getGrpcClient(port, csrf3) {
|
|
|
28350
28300
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
28351
28301
|
import fs27 from "fs";
|
|
28352
28302
|
import os14 from "os";
|
|
28353
|
-
import
|
|
28303
|
+
import path34 from "path";
|
|
28354
28304
|
function getLogsDir() {
|
|
28355
28305
|
if (process.platform === "darwin") {
|
|
28356
|
-
return
|
|
28306
|
+
return path34.join(os14.homedir(), "Library/Logs/JetBrains");
|
|
28357
28307
|
} else if (process.platform === "win32") {
|
|
28358
|
-
return
|
|
28359
|
-
process.env["LOCALAPPDATA"] ||
|
|
28308
|
+
return path34.join(
|
|
28309
|
+
process.env["LOCALAPPDATA"] || path34.join(os14.homedir(), "AppData/Local"),
|
|
28360
28310
|
"JetBrains"
|
|
28361
28311
|
);
|
|
28362
28312
|
} else {
|
|
28363
|
-
return
|
|
28313
|
+
return path34.join(os14.homedir(), ".cache/JetBrains");
|
|
28364
28314
|
}
|
|
28365
28315
|
}
|
|
28366
28316
|
function parseIdeLogDir(ideLogDir) {
|
|
28367
28317
|
const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
|
|
28368
28318
|
name: f,
|
|
28369
|
-
mtime: fs27.statSync(
|
|
28319
|
+
mtime: fs27.statSync(path34.join(ideLogDir, f)).mtimeMs
|
|
28370
28320
|
})).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
|
|
28371
28321
|
let latestCsrf = null;
|
|
28372
28322
|
let latestPort = null;
|
|
28373
28323
|
for (const logFile of logFiles) {
|
|
28374
|
-
const lines = fs27.readFileSync(
|
|
28324
|
+
const lines = fs27.readFileSync(path34.join(ideLogDir, logFile), "utf-8").split("\n");
|
|
28375
28325
|
for (const line of lines) {
|
|
28376
28326
|
if (!line.includes(
|
|
28377
28327
|
"com.codeium.intellij.language_server.LanguageServerProcessHandler"
|
|
@@ -28399,9 +28349,9 @@ function findRunningCodeiumLanguageServers() {
|
|
|
28399
28349
|
const logsDir = getLogsDir();
|
|
28400
28350
|
if (!fs27.existsSync(logsDir)) return results;
|
|
28401
28351
|
for (const ide of fs27.readdirSync(logsDir)) {
|
|
28402
|
-
let ideLogDir =
|
|
28352
|
+
let ideLogDir = path34.join(logsDir, ide);
|
|
28403
28353
|
if (process.platform !== "darwin") {
|
|
28404
|
-
ideLogDir =
|
|
28354
|
+
ideLogDir = path34.join(ideLogDir, "log");
|
|
28405
28355
|
}
|
|
28406
28356
|
if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
|
|
28407
28357
|
continue;
|
|
@@ -28584,10 +28534,10 @@ function processChatStepCodeAction(step) {
|
|
|
28584
28534
|
// src/features/codeium_intellij/install_hook.ts
|
|
28585
28535
|
import fsPromises5 from "fs/promises";
|
|
28586
28536
|
import os15 from "os";
|
|
28587
|
-
import
|
|
28537
|
+
import path35 from "path";
|
|
28588
28538
|
import chalk14 from "chalk";
|
|
28589
28539
|
function getCodeiumHooksPath() {
|
|
28590
|
-
return
|
|
28540
|
+
return path35.join(os15.homedir(), ".codeium", "hooks.json");
|
|
28591
28541
|
}
|
|
28592
28542
|
async function readCodeiumHooks() {
|
|
28593
28543
|
const hooksPath = getCodeiumHooksPath();
|
|
@@ -28600,7 +28550,7 @@ async function readCodeiumHooks() {
|
|
|
28600
28550
|
}
|
|
28601
28551
|
async function writeCodeiumHooks(config2) {
|
|
28602
28552
|
const hooksPath = getCodeiumHooksPath();
|
|
28603
|
-
const dir =
|
|
28553
|
+
const dir = path35.dirname(hooksPath);
|
|
28604
28554
|
await fsPromises5.mkdir(dir, { recursive: true });
|
|
28605
28555
|
await fsPromises5.writeFile(
|
|
28606
28556
|
hooksPath,
|