mobbdev 1.4.2 → 1.4.9
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/args/commands/upload_ai_blame.mjs +109 -17
- package/dist/index.mjs +760 -335
- package/package.json +10 -10
package/dist/index.mjs
CHANGED
|
@@ -94,6 +94,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
94
94
|
performCliLogin(variables, requestHeaders, signal) {
|
|
95
95
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: PerformCliLoginDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "performCliLogin", "mutation", variables);
|
|
96
96
|
},
|
|
97
|
+
SetQuarantineEnabled(variables, requestHeaders, signal) {
|
|
98
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: SetQuarantineEnabledDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "SetQuarantineEnabled", "mutation", variables);
|
|
99
|
+
},
|
|
97
100
|
CreateProject(variables, requestHeaders, signal) {
|
|
98
101
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: CreateProjectDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "CreateProject", "mutation", variables);
|
|
99
102
|
},
|
|
@@ -135,7 +138,7 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
135
138
|
}
|
|
136
139
|
};
|
|
137
140
|
}
|
|
138
|
-
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
|
|
141
|
+
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
|
|
139
142
|
var init_client_generates = __esm({
|
|
140
143
|
"src/features/analysis/scm/generates/client_generates.ts"() {
|
|
141
144
|
"use strict";
|
|
@@ -260,6 +263,7 @@ var init_client_generates = __esm({
|
|
|
260
263
|
IssueType_Enum2["HttpParameterPollution"] = "HTTP_PARAMETER_POLLUTION";
|
|
261
264
|
IssueType_Enum2["HttpResponseSplitting"] = "HTTP_RESPONSE_SPLITTING";
|
|
262
265
|
IssueType_Enum2["IframeWithoutSandbox"] = "IFRAME_WITHOUT_SANDBOX";
|
|
266
|
+
IssueType_Enum2["ImproperCertificateValidation"] = "IMPROPER_CERTIFICATE_VALIDATION";
|
|
263
267
|
IssueType_Enum2["ImproperExceptionHandling"] = "IMPROPER_EXCEPTION_HANDLING";
|
|
264
268
|
IssueType_Enum2["ImproperResourceShutdownOrRelease"] = "IMPROPER_RESOURCE_SHUTDOWN_OR_RELEASE";
|
|
265
269
|
IssueType_Enum2["ImproperStringFormatting"] = "IMPROPER_STRING_FORMATTING";
|
|
@@ -278,6 +282,7 @@ var init_client_generates = __esm({
|
|
|
278
282
|
IssueType_Enum2["InsecureTmpFile"] = "INSECURE_TMP_FILE";
|
|
279
283
|
IssueType_Enum2["InsecureUuidVersion"] = "INSECURE_UUID_VERSION";
|
|
280
284
|
IssueType_Enum2["InsufficientLogging"] = "INSUFFICIENT_LOGGING";
|
|
285
|
+
IssueType_Enum2["J2EeGetConnection"] = "J2EE_GET_CONNECTION";
|
|
281
286
|
IssueType_Enum2["JqueryDeprecatedSymbols"] = "JQUERY_DEPRECATED_SYMBOLS";
|
|
282
287
|
IssueType_Enum2["LeftoverDebugCode"] = "LEFTOVER_DEBUG_CODE";
|
|
283
288
|
IssueType_Enum2["LocaleDependentComparison"] = "LOCALE_DEPENDENT_COMPARISON";
|
|
@@ -593,6 +598,7 @@ var init_client_generates = __esm({
|
|
|
593
598
|
id
|
|
594
599
|
organization {
|
|
595
600
|
id
|
|
601
|
+
enableV2Fixes
|
|
596
602
|
projects(where: {name: {_eq: $projectName}}) {
|
|
597
603
|
name
|
|
598
604
|
id
|
|
@@ -611,6 +617,7 @@ var init_client_generates = __esm({
|
|
|
611
617
|
id
|
|
612
618
|
organization {
|
|
613
619
|
id
|
|
620
|
+
enableV2Fixes
|
|
614
621
|
}
|
|
615
622
|
}
|
|
616
623
|
}
|
|
@@ -941,6 +948,12 @@ var init_client_generates = __esm({
|
|
|
941
948
|
level
|
|
942
949
|
justification
|
|
943
950
|
}
|
|
951
|
+
appliedSkills
|
|
952
|
+
mcpCalls {
|
|
953
|
+
mcpServer
|
|
954
|
+
mcpTool
|
|
955
|
+
callCount
|
|
956
|
+
}
|
|
944
957
|
}
|
|
945
958
|
}
|
|
946
959
|
... on PromptSummaryProcessing {
|
|
@@ -1092,6 +1105,13 @@ var init_client_generates = __esm({
|
|
|
1092
1105
|
performCliLogin(loginId: $loginId) {
|
|
1093
1106
|
status
|
|
1094
1107
|
}
|
|
1108
|
+
}
|
|
1109
|
+
`;
|
|
1110
|
+
SetQuarantineEnabledDocument = `
|
|
1111
|
+
mutation SetQuarantineEnabled($enabled: Boolean!) {
|
|
1112
|
+
update_organization(where: {}, _set: {quarantineEnabled: $enabled}) {
|
|
1113
|
+
affected_rows
|
|
1114
|
+
}
|
|
1095
1115
|
}
|
|
1096
1116
|
`;
|
|
1097
1117
|
CreateProjectDocument = `
|
|
@@ -1277,12 +1297,15 @@ var init_client_generates = __esm({
|
|
|
1277
1297
|
SkillVerdictsByMd5Document = `
|
|
1278
1298
|
query SkillVerdictsByMd5($md5s: [String!]!) {
|
|
1279
1299
|
skillVerdictsByMd5(md5s: $md5s) {
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1300
|
+
quarantineEnabled
|
|
1301
|
+
verdicts {
|
|
1302
|
+
md5
|
|
1303
|
+
verdict
|
|
1304
|
+
summary
|
|
1305
|
+
scannerName
|
|
1306
|
+
scannerVersion
|
|
1307
|
+
scannedAt
|
|
1308
|
+
}
|
|
1286
1309
|
}
|
|
1287
1310
|
}
|
|
1288
1311
|
`;
|
|
@@ -1400,6 +1423,7 @@ var init_getIssueType = __esm({
|
|
|
1400
1423
|
["NO_EQUIVALENCE_METHOD" /* NoEquivalenceMethod */]: "Class Does Not Implement Equivalence Method",
|
|
1401
1424
|
["INFORMATION_EXPOSURE_VIA_HEADERS" /* InformationExposureViaHeaders */]: "Information Exposure via Headers",
|
|
1402
1425
|
["DEBUG_ENABLED" /* DebugEnabled */]: "Debug Enabled",
|
|
1426
|
+
["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: "J2EE Bad Practices: getConnection()",
|
|
1403
1427
|
["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: "Leftover Debug Code",
|
|
1404
1428
|
["POOR_ERROR_HANDLING_EMPTY_CATCH_BLOCK" /* PoorErrorHandlingEmptyCatchBlock */]: "Poor Error Handling: Empty Catch Block",
|
|
1405
1429
|
["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: "Erroneous String Compare",
|
|
@@ -1474,7 +1498,8 @@ var init_getIssueType = __esm({
|
|
|
1474
1498
|
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast",
|
|
1475
1499
|
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: "Missing X-Frame-Options Header",
|
|
1476
1500
|
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: "Improper Validation of Array Index",
|
|
1477
|
-
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion"
|
|
1501
|
+
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion",
|
|
1502
|
+
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: "Improper Certificate Validation"
|
|
1478
1503
|
};
|
|
1479
1504
|
issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
1480
1505
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -2733,7 +2758,7 @@ var init_env = __esm({
|
|
|
2733
2758
|
GITLAB_API_TOKEN: z15.string().optional(),
|
|
2734
2759
|
GITHUB_API_TOKEN: z15.string().optional(),
|
|
2735
2760
|
GIT_PROXY_HOST: z15.string().optional().default("http://tinyproxy:8888"),
|
|
2736
|
-
MAX_UPLOAD_FILE_SIZE_MB: z15.coerce.number().gt(0).default(
|
|
2761
|
+
MAX_UPLOAD_FILE_SIZE_MB: z15.coerce.number().gt(0).default(2),
|
|
2737
2762
|
GITHUB_API_CONCURRENCY: z15.coerce.number().gt(0).optional().default(10)
|
|
2738
2763
|
});
|
|
2739
2764
|
({
|
|
@@ -3598,11 +3623,17 @@ var init_FileUtils = __esm({
|
|
|
3598
3623
|
const results = [];
|
|
3599
3624
|
const filePromises = [];
|
|
3600
3625
|
for (const item of items) {
|
|
3601
|
-
const
|
|
3626
|
+
const safeInput = path.resolve(
|
|
3627
|
+
path.sep,
|
|
3628
|
+
path.normalize(
|
|
3629
|
+
String(dir || "").replace("\0", "").replace(/^(\.\.(\/|\\$))+/, "")
|
|
3630
|
+
)
|
|
3631
|
+
);
|
|
3632
|
+
const fullPath = path.join(safeInput, item);
|
|
3602
3633
|
try {
|
|
3603
3634
|
await fsPromises.access(fullPath, fs.constants.R_OK);
|
|
3604
|
-
const
|
|
3605
|
-
if (
|
|
3635
|
+
const stat5 = await fsPromises.stat(fullPath);
|
|
3636
|
+
if (stat5.isDirectory()) {
|
|
3606
3637
|
if (isRootLevel && excludedRootDirectories.includes(item)) {
|
|
3607
3638
|
continue;
|
|
3608
3639
|
}
|
|
@@ -3614,7 +3645,7 @@ var init_FileUtils = __esm({
|
|
|
3614
3645
|
name: item,
|
|
3615
3646
|
fullPath,
|
|
3616
3647
|
relativePath: path.relative(rootDir, fullPath),
|
|
3617
|
-
time:
|
|
3648
|
+
time: stat5.mtime.getTime(),
|
|
3618
3649
|
isFile: true
|
|
3619
3650
|
});
|
|
3620
3651
|
}
|
|
@@ -3636,7 +3667,9 @@ var init_FileUtils = __esm({
|
|
|
3636
3667
|
}) {
|
|
3637
3668
|
try {
|
|
3638
3669
|
const stats = fs.statSync(dir);
|
|
3639
|
-
if (!stats.isDirectory())
|
|
3670
|
+
if (!stats.isDirectory()) {
|
|
3671
|
+
return [];
|
|
3672
|
+
}
|
|
3640
3673
|
} catch {
|
|
3641
3674
|
return [];
|
|
3642
3675
|
}
|
|
@@ -3645,7 +3678,7 @@ var init_FileUtils = __esm({
|
|
|
3645
3678
|
const { GitService: GitService2 } = await Promise.resolve().then(() => (init_GitService(), GitService_exports));
|
|
3646
3679
|
const gitService = new GitService2(dir);
|
|
3647
3680
|
gitMatcher = await gitService.getGitignoreMatcher();
|
|
3648
|
-
} catch
|
|
3681
|
+
} catch {
|
|
3649
3682
|
}
|
|
3650
3683
|
const allFiles = await this.processRootDirectory(dir, EXCLUDED_DIRS);
|
|
3651
3684
|
const filteredFiles = allFiles.filter(
|
|
@@ -4568,6 +4601,7 @@ var fixDetailsData = {
|
|
|
4568
4601
|
issueDescription: "A data member and a function have the same name which can be confusing to the developer.",
|
|
4569
4602
|
fixInstructions: "Rename the data member to avoid confusion."
|
|
4570
4603
|
},
|
|
4604
|
+
["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: void 0,
|
|
4571
4605
|
["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: void 0,
|
|
4572
4606
|
["UNVALIDATED_PUBLIC_METHOD_ARGUMENT" /* UnvalidatedPublicMethodArgument */]: void 0,
|
|
4573
4607
|
["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: void 0,
|
|
@@ -4670,7 +4704,8 @@ var fixDetailsData = {
|
|
|
4670
4704
|
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0,
|
|
4671
4705
|
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: void 0,
|
|
4672
4706
|
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: void 0,
|
|
4673
|
-
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0
|
|
4707
|
+
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0,
|
|
4708
|
+
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0
|
|
4674
4709
|
};
|
|
4675
4710
|
|
|
4676
4711
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -4834,6 +4869,31 @@ var go_default = vulnerabilities3;
|
|
|
4834
4869
|
// src/features/analysis/scm/shared/src/storedFixData/java/index.ts
|
|
4835
4870
|
init_client_generates();
|
|
4836
4871
|
|
|
4872
|
+
// src/features/analysis/scm/shared/src/storedFixData/java/j2eeGetConnection.ts
|
|
4873
|
+
var j2eeGetConnection = {
|
|
4874
|
+
guidance: () => `This fix replaces direct \`DriverManager.getConnection(...)\` calls with a container-managed JNDI \`DataSource\` lookup. The new code expects the app server (Tomcat / WildFly / WebSphere / etc.) to expose a configured connection pool under the JNDI name you specified.
|
|
4875
|
+
|
|
4876
|
+
|
|
4877
|
+
|
|
4878
|
+
|
|
4879
|
+
***Make sure the resource pool exists before merging.*** The patched code will throw a \`NamingException\` at runtime if the JNDI name does not resolve. Configure it in your container's resource definition:
|
|
4880
|
+
|
|
4881
|
+
- **Tomcat**: declare a \`<Resource>\` element in \`context.xml\` (or per-app \`META-INF/context.xml\`) with the same JNDI name, plus \`url\`, \`username\`, \`password\`, \`driverClassName\`, and any pool sizing.
|
|
4882
|
+
- **Spring Boot (embedded Tomcat)**: configure via \`spring.datasource.jndi-name\` and matching \`<Resource>\`, or use \`@ConfigurationProperties\` to bind a \`DataSource\` bean.
|
|
4883
|
+
- **WildFly / JBoss EAP**: declare a \`<datasource>\` in the standalone/domain XML and reference its JNDI binding.
|
|
4884
|
+
- **WebSphere / WebLogic**: define the JDBC provider and data source through the admin console; bind it to the JNDI name.
|
|
4885
|
+
|
|
4886
|
+
|
|
4887
|
+
|
|
4888
|
+
|
|
4889
|
+
Also add a matching \`<resource-ref>\` (or \`<data-source>\`) in your \`WEB-INF/web.xml\` if you use one. The original connection details (URL, user, password) move from the call site into the resource definition \u2014 remove them from any constants / properties files where they were duplicated.
|
|
4890
|
+
|
|
4891
|
+
|
|
4892
|
+
|
|
4893
|
+
|
|
4894
|
+
This fix is mandated by the J2EE / Jakarta EE specification (CWE-245) \u2014 direct driver management bypasses the container's pooling, retry, and failover policies.`
|
|
4895
|
+
};
|
|
4896
|
+
|
|
4837
4897
|
// src/features/analysis/scm/shared/src/storedFixData/java/sqlInjection.ts
|
|
4838
4898
|
var sqlInjection = {
|
|
4839
4899
|
guidance: ({
|
|
@@ -4861,6 +4921,7 @@ var systemInformationLeak = {
|
|
|
4861
4921
|
// src/features/analysis/scm/shared/src/storedFixData/java/index.ts
|
|
4862
4922
|
var vulnerabilities4 = {
|
|
4863
4923
|
["PASSWORD_IN_COMMENT" /* PasswordInComment */]: passwordInComment,
|
|
4924
|
+
["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection,
|
|
4864
4925
|
["SQL_Injection" /* SqlInjection */]: sqlInjection,
|
|
4865
4926
|
["SYSTEM_INFORMATION_LEAK" /* SystemInformationLeak */]: systemInformationLeak
|
|
4866
4927
|
};
|
|
@@ -4945,10 +5006,24 @@ See more information [here](https://jinja.palletsprojects.com/en/3.1.x/templates
|
|
|
4945
5006
|
***Note: make sure that none of the data you're marking as safe is coming from user input, as this can lead to XSS vulnerabilities!***`
|
|
4946
5007
|
};
|
|
4947
5008
|
|
|
5009
|
+
// src/features/analysis/scm/shared/src/storedFixData/python/improperCertificateValidation.ts
|
|
5010
|
+
var improperCertificateValidation = {
|
|
5011
|
+
guidance: () => `This fix re-enables TLS certificate validation by changing \`verify=False\` to \`verify=True\` on the HTTP request. Any call that was deliberately reaching a server with a self-signed, expired, or otherwise untrusted certificate will start raising \`ssl.SSLError\` / \`requests.exceptions.SSLError\` after this change.
|
|
5012
|
+
|
|
5013
|
+
|
|
5014
|
+
|
|
5015
|
+
***Before merging, confirm that every endpoint reached by this call presents a certificate signed by a trusted CA.*** If the call must talk to an internal service that uses a private CA, prefer pointing \`verify\` at the CA bundle (\`verify="/path/to/ca.pem"\`) over disabling validation. If the certificate cannot be trusted at all, the safe fix is to terminate that connection at a properly configured proxy, not to keep it unvalidated.
|
|
5016
|
+
|
|
5017
|
+
|
|
5018
|
+
|
|
5019
|
+
See the [\`requests\` SSL verification docs](https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification) for the supported \`verify\` values.`
|
|
5020
|
+
};
|
|
5021
|
+
|
|
4948
5022
|
// src/features/analysis/scm/shared/src/storedFixData/python/index.ts
|
|
4949
5023
|
var vulnerabilities7 = {
|
|
4950
5024
|
["AUTO_ESCAPE_FALSE" /* AutoEscapeFalse */]: autoEscapeFalse,
|
|
4951
|
-
["CSRF" /* Csrf */]: csrf
|
|
5025
|
+
["CSRF" /* Csrf */]: csrf,
|
|
5026
|
+
["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: improperCertificateValidation
|
|
4952
5027
|
};
|
|
4953
5028
|
var python_default = vulnerabilities7;
|
|
4954
5029
|
|
|
@@ -5484,6 +5559,15 @@ var insecureCookie2 = {
|
|
|
5484
5559
|
}
|
|
5485
5560
|
};
|
|
5486
5561
|
|
|
5562
|
+
// src/features/analysis/scm/shared/src/storedQuestionData/java/j2eeGetConnection.ts
|
|
5563
|
+
var j2eeGetConnection2 = {
|
|
5564
|
+
jndiResourceName: {
|
|
5565
|
+
content: () => "What JNDI name is the database connection pool registered under?",
|
|
5566
|
+
description: () => 'We need the JNDI name your app server uses to expose its container-managed `DataSource`. The fix performs `new InitialContext().lookup(<jndi-name>)` to retrieve the pool, so this value must exactly match the resource definition (e.g. `<Resource name="...">` in Tomcat `context.xml`, or the binding declared in WildFly / WebSphere / WebLogic). The default `java:comp/env/jdbc/myDataSource` is the canonical Tomcat / Spring convention; replace it with whatever your environment uses.',
|
|
5567
|
+
guidance: () => ""
|
|
5568
|
+
}
|
|
5569
|
+
};
|
|
5570
|
+
|
|
5487
5571
|
// src/features/analysis/scm/shared/src/storedQuestionData/java/leftoverDebugCode.ts
|
|
5488
5572
|
var leftoverDebugCode = {
|
|
5489
5573
|
isCodeUsed: {
|
|
@@ -5812,6 +5896,7 @@ var vulnerabilities12 = {
|
|
|
5812
5896
|
["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition,
|
|
5813
5897
|
["INSECURE_COOKIE" /* InsecureCookie */]: insecureCookie2,
|
|
5814
5898
|
["TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */]: trustBoundaryViolation2,
|
|
5899
|
+
["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection2,
|
|
5815
5900
|
["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: leftoverDebugCode,
|
|
5816
5901
|
["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: erroneousStringCompare,
|
|
5817
5902
|
["DUPLICATED_STRINGS" /* DuplicatedStrings */]: duplicatedStrings
|
|
@@ -7178,7 +7263,7 @@ async function getAdoSdk(params) {
|
|
|
7178
7263
|
const url = new URL(repoUrl);
|
|
7179
7264
|
const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
7180
7265
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
7181
|
-
const
|
|
7266
|
+
const path37 = [
|
|
7182
7267
|
prefixPath,
|
|
7183
7268
|
owner,
|
|
7184
7269
|
projectName,
|
|
@@ -7189,7 +7274,7 @@ async function getAdoSdk(params) {
|
|
|
7189
7274
|
"items",
|
|
7190
7275
|
"items"
|
|
7191
7276
|
].filter(Boolean).join("/");
|
|
7192
|
-
return new URL(`${
|
|
7277
|
+
return new URL(`${path37}?${params2}`, origin).toString();
|
|
7193
7278
|
},
|
|
7194
7279
|
async getAdoBranchList({ repoUrl }) {
|
|
7195
7280
|
try {
|
|
@@ -7278,8 +7363,8 @@ async function getAdoSdk(params) {
|
|
|
7278
7363
|
const changeType = entry.changeType;
|
|
7279
7364
|
return changeType !== 16 && entry.item?.path;
|
|
7280
7365
|
}).map((entry) => {
|
|
7281
|
-
const
|
|
7282
|
-
return
|
|
7366
|
+
const path37 = entry.item.path;
|
|
7367
|
+
return path37.startsWith("/") ? path37.slice(1) : path37;
|
|
7283
7368
|
});
|
|
7284
7369
|
},
|
|
7285
7370
|
async searchAdoPullRequests({
|
|
@@ -13234,7 +13319,8 @@ var GQLClient = class {
|
|
|
13234
13319
|
const getLastOrgRes = await this._clientSdk.getLastOrg({ email });
|
|
13235
13320
|
return {
|
|
13236
13321
|
organizationId: getLastOrgRes?.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization?.id,
|
|
13237
|
-
userName: getLastOrgRes?.user?.[0]?.name ?? ""
|
|
13322
|
+
userName: getLastOrgRes?.user?.[0]?.name ?? "",
|
|
13323
|
+
enableV2Fixes: getLastOrgRes?.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization?.enableV2Fixes === true
|
|
13238
13324
|
};
|
|
13239
13325
|
}
|
|
13240
13326
|
async createCliLogin(variables) {
|
|
@@ -13316,7 +13402,8 @@ var GQLClient = class {
|
|
|
13316
13402
|
}
|
|
13317
13403
|
return {
|
|
13318
13404
|
organizationId: organization.id,
|
|
13319
|
-
projectId
|
|
13405
|
+
projectId,
|
|
13406
|
+
enableV2Fixes: organization.enableV2Fixes === true
|
|
13320
13407
|
};
|
|
13321
13408
|
}
|
|
13322
13409
|
async getEncryptedApiToken(variables) {
|
|
@@ -13646,6 +13733,7 @@ var GQLClient = class {
|
|
|
13646
13733
|
return await this._clientSdk.ScanSkill(variables);
|
|
13647
13734
|
}
|
|
13648
13735
|
// T-467 — batched verdict lookup for the client-side quarantine check.
|
|
13736
|
+
// T-493 — response is the envelope `{ quarantineEnabled, verdicts }`.
|
|
13649
13737
|
async skillVerdictsByMd5(md5s) {
|
|
13650
13738
|
return await this._clientSdk.SkillVerdictsByMd5({ md5s });
|
|
13651
13739
|
}
|
|
@@ -14068,7 +14156,11 @@ async function sanitizeDataWithCounts(obj, options) {
|
|
|
14068
14156
|
if (typeof data === "string") {
|
|
14069
14157
|
return sanitizeString(data);
|
|
14070
14158
|
} else if (Array.isArray(data)) {
|
|
14071
|
-
|
|
14159
|
+
const results = [];
|
|
14160
|
+
for (const item of data) {
|
|
14161
|
+
results.push(await sanitizeRecursive(item));
|
|
14162
|
+
}
|
|
14163
|
+
return results;
|
|
14072
14164
|
} else if (data instanceof Error) {
|
|
14073
14165
|
return data;
|
|
14074
14166
|
} else if (data instanceof Date) {
|
|
@@ -14499,22 +14591,25 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
|
|
|
14499
14591
|
const serializedRawDataByIndex = /* @__PURE__ */ new Map();
|
|
14500
14592
|
const records = await timedStep(
|
|
14501
14593
|
`${shouldSanitize ? "sanitize" : "serialize"} ${rawRecords.length} records`,
|
|
14502
|
-
() =>
|
|
14503
|
-
|
|
14594
|
+
async () => {
|
|
14595
|
+
const results = [];
|
|
14596
|
+
for (let index = 0; index < rawRecords.length; index++) {
|
|
14597
|
+
const record = rawRecords[index];
|
|
14504
14598
|
if (record.rawData != null && record.rawDataS3Key == null) {
|
|
14505
14599
|
const serialized = shouldSanitize ? await sanitizeRawData(record.rawData) : JSON.stringify(record.rawData);
|
|
14506
14600
|
serializedRawDataByIndex.set(index, serialized);
|
|
14507
14601
|
}
|
|
14508
14602
|
const { rawData: _rawData, ...rest } = record;
|
|
14509
|
-
|
|
14603
|
+
results.push({
|
|
14510
14604
|
...rest,
|
|
14511
14605
|
repositoryUrl: record.repositoryUrl ?? defaultRepoUrl,
|
|
14512
14606
|
computerName,
|
|
14513
14607
|
userName,
|
|
14514
14608
|
clientVersion: record.clientVersion ?? defaultClientVersion
|
|
14515
|
-
};
|
|
14516
|
-
}
|
|
14517
|
-
|
|
14609
|
+
});
|
|
14610
|
+
}
|
|
14611
|
+
return results;
|
|
14612
|
+
}
|
|
14518
14613
|
);
|
|
14519
14614
|
const recordsWithRawData = rawRecords.map((r, i) => ({ recordId: r.recordId, index: i })).filter((entry) => serializedRawDataByIndex.has(entry.index));
|
|
14520
14615
|
if (recordsWithRawData.length > 0) {
|
|
@@ -14555,32 +14650,39 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
|
|
|
14555
14650
|
errors: ["[step:s3-url] Malformed uploadFieldsJSON from server"]
|
|
14556
14651
|
};
|
|
14557
14652
|
}
|
|
14653
|
+
const MAX_CONCURRENT_S3_UPLOADS = 5;
|
|
14558
14654
|
debug10(
|
|
14559
|
-
"[step:s3-upload] Uploading %d files to S3",
|
|
14560
|
-
recordsWithRawData.length
|
|
14655
|
+
"[step:s3-upload] Uploading %d files to S3 (concurrency=%d)",
|
|
14656
|
+
recordsWithRawData.length,
|
|
14657
|
+
MAX_CONCURRENT_S3_UPLOADS
|
|
14561
14658
|
);
|
|
14562
14659
|
const s3Start = Date.now();
|
|
14563
|
-
const uploadResults =
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
14573
|
-
|
|
14574
|
-
|
|
14575
|
-
|
|
14576
|
-
|
|
14577
|
-
|
|
14578
|
-
|
|
14579
|
-
|
|
14580
|
-
|
|
14581
|
-
|
|
14582
|
-
|
|
14583
|
-
|
|
14660
|
+
const uploadResults = [];
|
|
14661
|
+
for (let i = 0; i < recordsWithRawData.length; i += MAX_CONCURRENT_S3_UPLOADS) {
|
|
14662
|
+
const chunk = recordsWithRawData.slice(i, i + MAX_CONCURRENT_S3_UPLOADS);
|
|
14663
|
+
const chunkResults = await Promise.allSettled(
|
|
14664
|
+
chunk.map(async (entry) => {
|
|
14665
|
+
const rawDataJson = serializedRawDataByIndex.get(entry.index);
|
|
14666
|
+
if (!rawDataJson) {
|
|
14667
|
+
debug10("No serialized rawData for recordId=%s", entry.recordId);
|
|
14668
|
+
return;
|
|
14669
|
+
}
|
|
14670
|
+
const uploadKey = `${keyPrefix}${entry.recordId}.json`;
|
|
14671
|
+
await withTimeout(
|
|
14672
|
+
uploadFile({
|
|
14673
|
+
file: Buffer.from(rawDataJson, "utf-8"),
|
|
14674
|
+
url,
|
|
14675
|
+
uploadKey,
|
|
14676
|
+
uploadFields
|
|
14677
|
+
}),
|
|
14678
|
+
BATCH_TIMEOUT_MS,
|
|
14679
|
+
`[step:s3-upload] uploadFile ${entry.recordId}`
|
|
14680
|
+
);
|
|
14681
|
+
records[entry.index].rawDataS3Key = uploadKey;
|
|
14682
|
+
})
|
|
14683
|
+
);
|
|
14684
|
+
uploadResults.push(...chunkResults);
|
|
14685
|
+
}
|
|
14584
14686
|
debug10(
|
|
14585
14687
|
"[perf] s3-upload %d files: %dms",
|
|
14586
14688
|
recordsWithRawData.length,
|
|
@@ -15172,7 +15274,7 @@ async function postIssueComment(params) {
|
|
|
15172
15274
|
fpDescription
|
|
15173
15275
|
} = params;
|
|
15174
15276
|
const {
|
|
15175
|
-
path:
|
|
15277
|
+
path: path37,
|
|
15176
15278
|
startLine,
|
|
15177
15279
|
vulnerabilityReportIssue: {
|
|
15178
15280
|
vulnerabilityReportIssueTags,
|
|
@@ -15187,7 +15289,7 @@ async function postIssueComment(params) {
|
|
|
15187
15289
|
Refresh the page in order to see the changes.`,
|
|
15188
15290
|
pull_number: pullRequest,
|
|
15189
15291
|
commit_id: commitSha,
|
|
15190
|
-
path:
|
|
15292
|
+
path: path37,
|
|
15191
15293
|
line: startLine
|
|
15192
15294
|
});
|
|
15193
15295
|
const commentId = commentRes.data.id;
|
|
@@ -15221,7 +15323,7 @@ async function postFixComment(params) {
|
|
|
15221
15323
|
scanner
|
|
15222
15324
|
} = params;
|
|
15223
15325
|
const {
|
|
15224
|
-
path:
|
|
15326
|
+
path: path37,
|
|
15225
15327
|
startLine,
|
|
15226
15328
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
15227
15329
|
vulnerabilityReportIssueId
|
|
@@ -15239,7 +15341,7 @@ async function postFixComment(params) {
|
|
|
15239
15341
|
Refresh the page in order to see the changes.`,
|
|
15240
15342
|
pull_number: pullRequest,
|
|
15241
15343
|
commit_id: commitSha,
|
|
15242
|
-
path:
|
|
15344
|
+
path: path37,
|
|
15243
15345
|
line: startLine
|
|
15244
15346
|
});
|
|
15245
15347
|
const commentId = commentRes.data.id;
|
|
@@ -15607,7 +15709,7 @@ function getManifestFilesSuffixes() {
|
|
|
15607
15709
|
}
|
|
15608
15710
|
async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
|
|
15609
15711
|
debug18("pack folder %s", srcDirPath);
|
|
15610
|
-
let git
|
|
15712
|
+
let git;
|
|
15611
15713
|
try {
|
|
15612
15714
|
git = simpleGit3({
|
|
15613
15715
|
baseDir: srcDirPath,
|
|
@@ -15653,7 +15755,11 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
|
|
|
15653
15755
|
}
|
|
15654
15756
|
}
|
|
15655
15757
|
if (fs9.lstatSync(absFilepath).size > MCP_MAX_FILE_SIZE) {
|
|
15656
|
-
debug18(
|
|
15758
|
+
debug18(
|
|
15759
|
+
"ignoring %s \u2014 file size exceeds MCP_MAX_FILE_SIZE (%d bytes)",
|
|
15760
|
+
filepath,
|
|
15761
|
+
MCP_MAX_FILE_SIZE
|
|
15762
|
+
);
|
|
15657
15763
|
continue;
|
|
15658
15764
|
}
|
|
15659
15765
|
let data;
|
|
@@ -15843,8 +15949,8 @@ if (typeof __filename !== "undefined") {
|
|
|
15843
15949
|
}
|
|
15844
15950
|
var costumeRequire = createRequire(moduleUrl);
|
|
15845
15951
|
var getCheckmarxPath = () => {
|
|
15846
|
-
const
|
|
15847
|
-
const cxFileName =
|
|
15952
|
+
const os17 = type();
|
|
15953
|
+
const cxFileName = os17 === "Windows_NT" ? "cx.exe" : "cx";
|
|
15848
15954
|
try {
|
|
15849
15955
|
return costumeRequire.resolve(`.bin/${cxFileName}`);
|
|
15850
15956
|
} catch (e) {
|
|
@@ -16034,7 +16140,9 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
|
16034
16140
|
init_client_generates();
|
|
16035
16141
|
var { CliError: CliError2, Spinner: Spinner2 } = utils_exports;
|
|
16036
16142
|
function _getScanSource(command, ci) {
|
|
16037
|
-
if (command === "review")
|
|
16143
|
+
if (command === "review") {
|
|
16144
|
+
return "AUTO_FIXER" /* AutoFixer */;
|
|
16145
|
+
}
|
|
16038
16146
|
const envToCi = [
|
|
16039
16147
|
["GITLAB_CI", "CI_GITLAB" /* CiGitlab */],
|
|
16040
16148
|
["GITHUB_ACTIONS", "CI_GITHUB" /* CiGithub */],
|
|
@@ -16251,7 +16359,11 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
16251
16359
|
if (!mobbProjectName) {
|
|
16252
16360
|
throw new Error("mobbProjectName is required");
|
|
16253
16361
|
}
|
|
16254
|
-
const {
|
|
16362
|
+
const {
|
|
16363
|
+
projectId,
|
|
16364
|
+
organizationId,
|
|
16365
|
+
enableV2Fixes: orgEnableV2Fixes
|
|
16366
|
+
} = await gqlClient.getLastOrgAndNamedProject({
|
|
16255
16367
|
projectName: mobbProjectName,
|
|
16256
16368
|
userDefinedOrganizationId: userOrganizationId
|
|
16257
16369
|
});
|
|
@@ -16501,7 +16613,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
16501
16613
|
srcPath,
|
|
16502
16614
|
vulnFiles,
|
|
16503
16615
|
repoUploadInfo,
|
|
16504
|
-
isIncludeAllFiles:
|
|
16616
|
+
isIncludeAllFiles: orgEnableV2Fixes
|
|
16505
16617
|
});
|
|
16506
16618
|
gitInfo2 = res.gitInfo;
|
|
16507
16619
|
} else {
|
|
@@ -16761,8 +16873,8 @@ async function resolveSkillScanInput(skillInput) {
|
|
|
16761
16873
|
if (!fs11.existsSync(resolvedPath)) {
|
|
16762
16874
|
return skillInput;
|
|
16763
16875
|
}
|
|
16764
|
-
const
|
|
16765
|
-
if (!
|
|
16876
|
+
const stat5 = fs11.statSync(resolvedPath);
|
|
16877
|
+
if (!stat5.isDirectory()) {
|
|
16766
16878
|
throw new CliError(
|
|
16767
16879
|
"Local skill input must be a directory containing SKILL.md"
|
|
16768
16880
|
);
|
|
@@ -17161,10 +17273,16 @@ import { spawn } from "child_process";
|
|
|
17161
17273
|
|
|
17162
17274
|
// src/features/claude_code/daemon.ts
|
|
17163
17275
|
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
17164
|
-
import
|
|
17276
|
+
import * as os8 from "os";
|
|
17277
|
+
import path24 from "path";
|
|
17165
17278
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
17166
17279
|
import Configstore3 from "configstore";
|
|
17167
17280
|
|
|
17281
|
+
// src/features/analysis/skill_quarantine/runQuarantineCheck.ts
|
|
17282
|
+
import { stat as stat3 } from "fs/promises";
|
|
17283
|
+
import { homedir as homedir4 } from "os";
|
|
17284
|
+
import path19 from "path";
|
|
17285
|
+
|
|
17168
17286
|
// src/features/analysis/skill_quarantine/constants.ts
|
|
17169
17287
|
var HEARTBEAT_DEBOUNCE_MS = (() => {
|
|
17170
17288
|
const raw = Number(process.env["MOBB_TRACY_SKILL_QUARANTINE_DEBOUNCE_MS"]);
|
|
@@ -17240,51 +17358,86 @@ import { globby as globby2 } from "globby";
|
|
|
17240
17358
|
import { parse as parseJsoncLib } from "jsonc-parser";
|
|
17241
17359
|
|
|
17242
17360
|
// src/features/analysis/context_file_scan_paths.ts
|
|
17243
|
-
var
|
|
17361
|
+
var CATEGORY = {
|
|
17362
|
+
RULE: "rule",
|
|
17363
|
+
MEMORY: "memory",
|
|
17364
|
+
SKILL: "skill",
|
|
17365
|
+
COMMAND: "command",
|
|
17366
|
+
PROMPT: "prompt",
|
|
17367
|
+
AGENT_CONFIG: "agent-config",
|
|
17368
|
+
CONFIG: "config",
|
|
17369
|
+
MCP_CONFIG: "mcp-config",
|
|
17370
|
+
IGNORE: "ignore"
|
|
17371
|
+
};
|
|
17372
|
+
var SKILL_CATEGORY = CATEGORY.SKILL;
|
|
17244
17373
|
var SCAN_PATHS = {
|
|
17245
17374
|
"claude-code": [
|
|
17246
|
-
{ glob: "CLAUDE.md", category:
|
|
17247
|
-
{ glob: "CLAUDE.local.md", category:
|
|
17248
|
-
{ glob: "INSIGHTS.md", category:
|
|
17249
|
-
{ glob: "AGENTS.md", category:
|
|
17250
|
-
{
|
|
17251
|
-
|
|
17252
|
-
|
|
17253
|
-
|
|
17375
|
+
{ glob: "CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17376
|
+
{ glob: "CLAUDE.local.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17377
|
+
{ glob: "INSIGHTS.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17378
|
+
{ glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17379
|
+
{
|
|
17380
|
+
glob: ".claude/rules/**/*.md",
|
|
17381
|
+
category: CATEGORY.RULE,
|
|
17382
|
+
root: "workspace"
|
|
17383
|
+
},
|
|
17384
|
+
{ glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "home" },
|
|
17385
|
+
{ glob: ".claude/INSIGHTS.md", category: CATEGORY.RULE, root: "home" },
|
|
17386
|
+
{ glob: ".claude/rules/**/*.md", category: CATEGORY.RULE, root: "home" },
|
|
17254
17387
|
{
|
|
17255
17388
|
glob: ".claude/projects/*/memory/*.md",
|
|
17256
|
-
category:
|
|
17389
|
+
category: CATEGORY.MEMORY,
|
|
17257
17390
|
root: "home"
|
|
17258
17391
|
},
|
|
17259
17392
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
|
|
17260
|
-
{
|
|
17393
|
+
{
|
|
17394
|
+
glob: ".claude/commands/*.md",
|
|
17395
|
+
category: CATEGORY.COMMAND,
|
|
17396
|
+
root: "workspace"
|
|
17397
|
+
},
|
|
17261
17398
|
{
|
|
17262
17399
|
glob: ".claude/agents/*.md",
|
|
17263
|
-
category:
|
|
17400
|
+
category: CATEGORY.SKILL,
|
|
17264
17401
|
root: "workspace"
|
|
17265
17402
|
},
|
|
17266
17403
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
|
|
17267
|
-
{ glob: ".claude/commands/*.md", category:
|
|
17268
|
-
{ glob: ".claude/agents/*.md", category:
|
|
17269
|
-
{
|
|
17404
|
+
{ glob: ".claude/commands/*.md", category: CATEGORY.COMMAND, root: "home" },
|
|
17405
|
+
{ glob: ".claude/agents/*.md", category: CATEGORY.SKILL, root: "home" },
|
|
17406
|
+
{
|
|
17407
|
+
glob: ".claude/settings.json",
|
|
17408
|
+
category: CATEGORY.CONFIG,
|
|
17409
|
+
root: "workspace"
|
|
17410
|
+
},
|
|
17270
17411
|
{
|
|
17271
17412
|
glob: ".claude/settings.local.json",
|
|
17272
|
-
category:
|
|
17413
|
+
category: CATEGORY.CONFIG,
|
|
17273
17414
|
root: "workspace"
|
|
17274
17415
|
},
|
|
17275
|
-
{ glob: ".mcp.json", category:
|
|
17276
|
-
{
|
|
17277
|
-
|
|
17278
|
-
|
|
17416
|
+
{ glob: ".mcp.json", category: CATEGORY.MCP_CONFIG, root: "workspace" },
|
|
17417
|
+
{
|
|
17418
|
+
glob: ".claude/.mcp.json",
|
|
17419
|
+
category: CATEGORY.MCP_CONFIG,
|
|
17420
|
+
root: "workspace"
|
|
17421
|
+
},
|
|
17422
|
+
{ glob: ".claude/settings.json", category: CATEGORY.CONFIG, root: "home" },
|
|
17423
|
+
{ glob: ".claudeignore", category: CATEGORY.IGNORE, root: "workspace" }
|
|
17279
17424
|
],
|
|
17280
17425
|
cursor: [
|
|
17281
17426
|
// Legacy single-file rules
|
|
17282
|
-
{ glob: ".cursorrules", category:
|
|
17427
|
+
{ glob: ".cursorrules", category: CATEGORY.RULE, root: "workspace" },
|
|
17283
17428
|
// Project Rules — docs support both `.mdc` and `.md` inside .cursor/rules/
|
|
17284
|
-
{
|
|
17285
|
-
|
|
17429
|
+
{
|
|
17430
|
+
glob: ".cursor/rules/**/*.mdc",
|
|
17431
|
+
category: CATEGORY.RULE,
|
|
17432
|
+
root: "workspace"
|
|
17433
|
+
},
|
|
17434
|
+
{
|
|
17435
|
+
glob: ".cursor/rules/**/*.md",
|
|
17436
|
+
category: CATEGORY.RULE,
|
|
17437
|
+
root: "workspace"
|
|
17438
|
+
},
|
|
17286
17439
|
// AGENTS.md — Cursor's documented alternative to .cursor/rules/
|
|
17287
|
-
{ glob: "AGENTS.md", category:
|
|
17440
|
+
{ glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17288
17441
|
// Agent skills — Cursor auto-loads from these dirs plus compat with
|
|
17289
17442
|
// Claude / Codex / generic .agents/ per Cursor docs.
|
|
17290
17443
|
{ kind: "skill-bundle", skillsRoot: ".cursor/skills", root: "workspace" },
|
|
@@ -17292,15 +17445,19 @@ var SCAN_PATHS = {
|
|
|
17292
17445
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
|
|
17293
17446
|
{ kind: "skill-bundle", skillsRoot: ".codex/skills", root: "workspace" },
|
|
17294
17447
|
// MCP — project + global
|
|
17295
|
-
{
|
|
17296
|
-
|
|
17448
|
+
{
|
|
17449
|
+
glob: ".cursor/mcp.json",
|
|
17450
|
+
category: CATEGORY.MCP_CONFIG,
|
|
17451
|
+
root: "workspace"
|
|
17452
|
+
},
|
|
17453
|
+
{ glob: ".cursor/mcp.json", category: CATEGORY.MCP_CONFIG, root: "home" },
|
|
17297
17454
|
// Home skills (user-level cross-project skills)
|
|
17298
17455
|
{ kind: "skill-bundle", skillsRoot: ".cursor/skills", root: "home" },
|
|
17299
17456
|
{ kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" },
|
|
17300
17457
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
|
|
17301
17458
|
{ kind: "skill-bundle", skillsRoot: ".codex/skills", root: "home" },
|
|
17302
17459
|
// Exclusion
|
|
17303
|
-
{ glob: ".cursorignore", category:
|
|
17460
|
+
{ glob: ".cursorignore", category: CATEGORY.IGNORE, root: "workspace" }
|
|
17304
17461
|
// Note: Cursor's global "Rules for AI" from Settings UI is stored in
|
|
17305
17462
|
// Cursor's internal settings DB. The tracer_ext reads it via VS Code API
|
|
17306
17463
|
// (vscode.workspace.getConfiguration) and includes it as a synthetic entry.
|
|
@@ -17309,42 +17466,46 @@ var SCAN_PATHS = {
|
|
|
17309
17466
|
// Instructions — workspace
|
|
17310
17467
|
{
|
|
17311
17468
|
glob: ".github/copilot-instructions.md",
|
|
17312
|
-
category:
|
|
17469
|
+
category: CATEGORY.RULE,
|
|
17313
17470
|
root: "workspace"
|
|
17314
17471
|
},
|
|
17315
17472
|
{
|
|
17316
17473
|
glob: ".github/instructions/**/*.instructions.md",
|
|
17317
|
-
category:
|
|
17474
|
+
category: CATEGORY.RULE,
|
|
17318
17475
|
root: "workspace"
|
|
17319
17476
|
},
|
|
17320
17477
|
// AGENTS.md / CLAUDE.md family (Copilot reads these via chat.useAgentsMdFile,
|
|
17321
17478
|
// chat.useClaudeMdFile for cross-compat with Claude Code / other agents).
|
|
17322
|
-
{ glob: "AGENTS.md", category:
|
|
17323
|
-
{ glob: "CLAUDE.md", category:
|
|
17324
|
-
{ glob: "CLAUDE.local.md", category:
|
|
17325
|
-
{ glob: ".claude/CLAUDE.md", category:
|
|
17326
|
-
{
|
|
17479
|
+
{ glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17480
|
+
{ glob: "CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17481
|
+
{ glob: "CLAUDE.local.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17482
|
+
{ glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
|
|
17483
|
+
{
|
|
17484
|
+
glob: ".claude/rules/**/*.md",
|
|
17485
|
+
category: CATEGORY.RULE,
|
|
17486
|
+
root: "workspace"
|
|
17487
|
+
},
|
|
17327
17488
|
// Prompts — workspace
|
|
17328
17489
|
{
|
|
17329
17490
|
glob: ".github/prompts/*.prompt.md",
|
|
17330
|
-
category:
|
|
17491
|
+
category: CATEGORY.PROMPT,
|
|
17331
17492
|
root: "workspace"
|
|
17332
17493
|
},
|
|
17333
17494
|
// Custom agents — `.agent.md` is the current format; `.chatmode.md` is the
|
|
17334
17495
|
// legacy naming docs recommend renaming. We scan both for transition.
|
|
17335
17496
|
{
|
|
17336
17497
|
glob: ".github/agents/*.agent.md",
|
|
17337
|
-
category:
|
|
17498
|
+
category: CATEGORY.AGENT_CONFIG,
|
|
17338
17499
|
root: "workspace"
|
|
17339
17500
|
},
|
|
17340
17501
|
{
|
|
17341
17502
|
glob: ".github/chatmodes/*.chatmode.md",
|
|
17342
|
-
category:
|
|
17503
|
+
category: CATEGORY.AGENT_CONFIG,
|
|
17343
17504
|
root: "workspace"
|
|
17344
17505
|
},
|
|
17345
17506
|
{
|
|
17346
17507
|
glob: ".claude/agents/*.md",
|
|
17347
|
-
category:
|
|
17508
|
+
category: CATEGORY.SKILL,
|
|
17348
17509
|
root: "workspace"
|
|
17349
17510
|
},
|
|
17350
17511
|
// Agent skills — Copilot discovers skills in all three roots (VS Code docs:
|
|
@@ -17353,30 +17514,38 @@ var SCAN_PATHS = {
|
|
|
17353
17514
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
|
|
17354
17515
|
{ kind: "skill-bundle", skillsRoot: ".agents/skills", root: "workspace" },
|
|
17355
17516
|
// MCP — VS Code Copilot reads MCP servers from .vscode/mcp.json
|
|
17356
|
-
{
|
|
17517
|
+
{
|
|
17518
|
+
glob: ".vscode/mcp.json",
|
|
17519
|
+
category: CATEGORY.MCP_CONFIG,
|
|
17520
|
+
root: "workspace"
|
|
17521
|
+
},
|
|
17357
17522
|
// Global — home (JetBrains stores global instructions here)
|
|
17358
17523
|
{
|
|
17359
17524
|
glob: ".config/github-copilot/global-copilot-instructions.md",
|
|
17360
|
-
category:
|
|
17525
|
+
category: CATEGORY.RULE,
|
|
17361
17526
|
root: "home"
|
|
17362
17527
|
},
|
|
17363
17528
|
// User-level Copilot customizations (~/.copilot/)
|
|
17364
17529
|
{
|
|
17365
17530
|
glob: ".copilot/instructions/**/*.instructions.md",
|
|
17366
|
-
category:
|
|
17531
|
+
category: CATEGORY.RULE,
|
|
17532
|
+
root: "home"
|
|
17533
|
+
},
|
|
17534
|
+
{
|
|
17535
|
+
glob: ".copilot/prompts/*.prompt.md",
|
|
17536
|
+
category: CATEGORY.PROMPT,
|
|
17367
17537
|
root: "home"
|
|
17368
17538
|
},
|
|
17369
|
-
{ glob: ".copilot/prompts/*.prompt.md", category: "skill", root: "home" },
|
|
17370
17539
|
{
|
|
17371
17540
|
glob: ".copilot/agents/*.agent.md",
|
|
17372
|
-
category:
|
|
17541
|
+
category: CATEGORY.AGENT_CONFIG,
|
|
17373
17542
|
root: "home"
|
|
17374
17543
|
},
|
|
17375
17544
|
{ kind: "skill-bundle", skillsRoot: ".copilot/skills", root: "home" },
|
|
17376
17545
|
// Cross-compat home paths (Copilot reads Claude / generic agent dirs too)
|
|
17377
|
-
{ glob: ".claude/CLAUDE.md", category:
|
|
17378
|
-
{ glob: ".claude/rules/**/*.md", category:
|
|
17379
|
-
{ glob: ".claude/agents/*.md", category:
|
|
17546
|
+
{ glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "home" },
|
|
17547
|
+
{ glob: ".claude/rules/**/*.md", category: CATEGORY.RULE, root: "home" },
|
|
17548
|
+
{ glob: ".claude/agents/*.md", category: CATEGORY.SKILL, root: "home" },
|
|
17380
17549
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
|
|
17381
17550
|
{ kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" }
|
|
17382
17551
|
]
|
|
@@ -17416,7 +17585,7 @@ var COPILOT_CUSTOM_LOCATION_SETTINGS = [
|
|
|
17416
17585
|
{
|
|
17417
17586
|
key: "chat.promptFilesLocations",
|
|
17418
17587
|
kind: "glob",
|
|
17419
|
-
category: "
|
|
17588
|
+
category: "prompt",
|
|
17420
17589
|
glob: "**/*.prompt.md"
|
|
17421
17590
|
},
|
|
17422
17591
|
{
|
|
@@ -17547,11 +17716,11 @@ async function readJsoncSettings(settingsPath) {
|
|
|
17547
17716
|
putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
|
|
17548
17717
|
return payload;
|
|
17549
17718
|
}
|
|
17550
|
-
function putSettingsCache(
|
|
17551
|
-
if (!settingsCache.has(
|
|
17719
|
+
function putSettingsCache(path37, entry) {
|
|
17720
|
+
if (!settingsCache.has(path37) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
|
|
17552
17721
|
settingsCache.delete(settingsCache.keys().next().value);
|
|
17553
17722
|
}
|
|
17554
|
-
settingsCache.set(
|
|
17723
|
+
settingsCache.set(path37, entry);
|
|
17555
17724
|
}
|
|
17556
17725
|
async function readCopilotCustomLocations(workspaceRoot) {
|
|
17557
17726
|
const parsed = await readJsoncSettings(
|
|
@@ -17657,16 +17826,23 @@ function groupSkills(files, root, baseDir) {
|
|
|
17657
17826
|
standalone.push(f);
|
|
17658
17827
|
} else {
|
|
17659
17828
|
const folderName = relFromSkills.slice(0, slashIdx);
|
|
17660
|
-
|
|
17661
|
-
|
|
17829
|
+
const folderKey = rel.slice(
|
|
17830
|
+
0,
|
|
17831
|
+
skillsIdx + skillsMarker.length + folderName.length
|
|
17832
|
+
);
|
|
17833
|
+
let bucket = folderMap.get(folderKey);
|
|
17834
|
+
if (!bucket) {
|
|
17835
|
+
bucket = { folderName, files: [] };
|
|
17836
|
+
folderMap.set(folderKey, bucket);
|
|
17662
17837
|
}
|
|
17663
|
-
|
|
17838
|
+
bucket.files.push(f);
|
|
17664
17839
|
}
|
|
17665
17840
|
}
|
|
17666
17841
|
const groups = [];
|
|
17667
17842
|
for (const f of standalone) {
|
|
17668
17843
|
const name = path14.basename(f.path, path14.extname(f.path));
|
|
17669
|
-
const
|
|
17844
|
+
const standaloneRel = path14.relative(baseDir, f.path).replace(/\\/g, "/");
|
|
17845
|
+
const sessionKey = `skill:${root}:${standaloneRel}`;
|
|
17670
17846
|
groups.push({
|
|
17671
17847
|
name,
|
|
17672
17848
|
root,
|
|
@@ -17677,17 +17853,10 @@ function groupSkills(files, root, baseDir) {
|
|
|
17677
17853
|
sessionKey
|
|
17678
17854
|
});
|
|
17679
17855
|
}
|
|
17680
|
-
for (const [folderName, folderFiles] of folderMap) {
|
|
17856
|
+
for (const [folderKey, { folderName, files: folderFiles }] of folderMap) {
|
|
17681
17857
|
const maxMtimeMs = Math.max(...folderFiles.map((f) => f.mtimeMs));
|
|
17682
|
-
const
|
|
17683
|
-
const
|
|
17684
|
-
const skillsIdx = rel.indexOf("skills/");
|
|
17685
|
-
const skillRelPath = rel.slice(
|
|
17686
|
-
0,
|
|
17687
|
-
skillsIdx + "skills/".length + folderName.length
|
|
17688
|
-
);
|
|
17689
|
-
const skillPath = path14.join(baseDir, skillRelPath);
|
|
17690
|
-
const sessionKey = `skill:${root}:${folderName}`;
|
|
17858
|
+
const skillPath = path14.join(baseDir, folderKey);
|
|
17859
|
+
const sessionKey = `skill:${root}:${folderKey}`;
|
|
17691
17860
|
groups.push({
|
|
17692
17861
|
name: folderName,
|
|
17693
17862
|
root,
|
|
@@ -17801,7 +17970,7 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
|
|
|
17801
17970
|
} catch {
|
|
17802
17971
|
return [];
|
|
17803
17972
|
}
|
|
17804
|
-
return files.map((
|
|
17973
|
+
return files.map((path37) => ({ path: path37, category }));
|
|
17805
17974
|
}
|
|
17806
17975
|
async function enumerateSkillBundle(baseDir, skillsRoot) {
|
|
17807
17976
|
const skillsDir = path14.resolve(baseDir, skillsRoot);
|
|
@@ -17965,6 +18134,12 @@ var Metric = {
|
|
|
17965
18134
|
CHECK_TRIGGERED: "skill_quarantine.check_triggered",
|
|
17966
18135
|
/** The env-var kill switch skipped the run. */
|
|
17967
18136
|
CHECK_DISABLED_ENV: "skill_quarantine.check_disabled_env",
|
|
18137
|
+
/**
|
|
18138
|
+
* T-493 — the per-org opt-in toggle was off for every org the caller
|
|
18139
|
+
* belongs to. Verdict query still ran (useful server-side telemetry);
|
|
18140
|
+
* on-disk enforcement was skipped.
|
|
18141
|
+
*/
|
|
18142
|
+
CHECK_DISABLED_ORG: "skill_quarantine.check_disabled_org",
|
|
17968
18143
|
/** Verdict-query call failed. Fail-open. */
|
|
17969
18144
|
QUERY_ERROR: "skill_quarantine.query_error",
|
|
17970
18145
|
/** Count of skills enumerated in this run. */
|
|
@@ -17986,7 +18161,18 @@ var Metric = {
|
|
|
17986
18161
|
/** Stale partial zip swept (older than grace window). */
|
|
17987
18162
|
SWEPT_PARTIAL: "skill_quarantine.swept_partial",
|
|
17988
18163
|
/** Total run duration including I/O. */
|
|
17989
|
-
DURATION_MS: "skill_quarantine.duration_ms"
|
|
18164
|
+
DURATION_MS: "skill_quarantine.duration_ms",
|
|
18165
|
+
/**
|
|
18166
|
+
* T-492 — Stub-md5 sentinel published successfully. The next heartbeat's
|
|
18167
|
+
* presence check will short-circuit re-quarantining our own stub.
|
|
18168
|
+
*/
|
|
18169
|
+
STUB_PREREGISTERED: "skill_quarantine.stub_preregistered",
|
|
18170
|
+
/**
|
|
18171
|
+
* T-492 — Stub-md5 sentinel write failed. Layer 1 (the scanner LLM
|
|
18172
|
+
* recognising stubs) still protects against the loop; this layer is
|
|
18173
|
+
* defense in depth.
|
|
18174
|
+
*/
|
|
18175
|
+
STUB_PREREGISTER_ERROR: "skill_quarantine.stub_preregister_error"
|
|
17990
18176
|
};
|
|
17991
18177
|
|
|
17992
18178
|
// src/features/analysis/skill_quarantine/quarantineSkill.ts
|
|
@@ -18120,6 +18306,12 @@ async function quarantineSkill(params) {
|
|
|
18120
18306
|
);
|
|
18121
18307
|
return { status: "publish_error", err };
|
|
18122
18308
|
}
|
|
18309
|
+
await preregisterStubMd5(params).catch((err) => {
|
|
18310
|
+
log2.warn(
|
|
18311
|
+
{ err, md5, metric: Metric.STUB_PREREGISTER_ERROR },
|
|
18312
|
+
"skill_quarantine: stub-md5 pre-registration failed; LLM-side recognition remains"
|
|
18313
|
+
);
|
|
18314
|
+
});
|
|
18123
18315
|
log2.info(
|
|
18124
18316
|
{
|
|
18125
18317
|
md5,
|
|
@@ -18133,6 +18325,53 @@ async function quarantineSkill(params) {
|
|
|
18133
18325
|
);
|
|
18134
18326
|
return { status: "quarantined" };
|
|
18135
18327
|
}
|
|
18328
|
+
async function preregisterStubMd5(params) {
|
|
18329
|
+
const { skillPath, isFolder, origName, log: log2 } = params;
|
|
18330
|
+
const stubFilePath = isFolder ? path18.join(skillPath, "SKILL.md") : skillPath;
|
|
18331
|
+
const stubEntryName = isFolder ? `${origName}/SKILL.md` : origName;
|
|
18332
|
+
const content = await readFile2(stubFilePath, "utf-8");
|
|
18333
|
+
const fileStat = await stat2(stubFilePath);
|
|
18334
|
+
const stubFile = {
|
|
18335
|
+
name: stubEntryName,
|
|
18336
|
+
path: stubFilePath,
|
|
18337
|
+
content,
|
|
18338
|
+
sizeBytes: fileStat.size,
|
|
18339
|
+
category: "skill",
|
|
18340
|
+
mtimeMs: fileStat.mtimeMs
|
|
18341
|
+
};
|
|
18342
|
+
const stubGroup = {
|
|
18343
|
+
name: isFolder ? origName : path18.basename(skillPath, path18.extname(skillPath)),
|
|
18344
|
+
root: "workspace",
|
|
18345
|
+
// unused by md5 computation
|
|
18346
|
+
skillPath,
|
|
18347
|
+
files: [stubFile],
|
|
18348
|
+
isFolder,
|
|
18349
|
+
maxMtimeMs: stubFile.mtimeMs,
|
|
18350
|
+
sessionKey: `stub-preregister:${skillPath}`
|
|
18351
|
+
};
|
|
18352
|
+
const { skills } = await processContextFiles([], [stubGroup]);
|
|
18353
|
+
const processed = skills[0];
|
|
18354
|
+
if (!processed) {
|
|
18355
|
+
return;
|
|
18356
|
+
}
|
|
18357
|
+
const { md5: stubMd5, zipBuffer } = processed;
|
|
18358
|
+
const sentinelPath = getQuarantineZipPath(stubMd5);
|
|
18359
|
+
if (await exists(sentinelPath)) {
|
|
18360
|
+
return;
|
|
18361
|
+
}
|
|
18362
|
+
const tmpSentinel = getTmpZipPath(stubMd5, randomUUID());
|
|
18363
|
+
try {
|
|
18364
|
+
await writeFile(tmpSentinel, zipBuffer);
|
|
18365
|
+
await rename(tmpSentinel, sentinelPath);
|
|
18366
|
+
} catch (err) {
|
|
18367
|
+
await unlink(tmpSentinel).catch(ignoreErr);
|
|
18368
|
+
throw err;
|
|
18369
|
+
}
|
|
18370
|
+
log2.info(
|
|
18371
|
+
{ stubMd5, metric: Metric.STUB_PREREGISTERED },
|
|
18372
|
+
"skill_quarantine: stub md5 pre-registered"
|
|
18373
|
+
);
|
|
18374
|
+
}
|
|
18136
18375
|
async function writeStub(params) {
|
|
18137
18376
|
const { skillPath, isFolder, md5, verdict } = params;
|
|
18138
18377
|
const stubContent = renderStub({
|
|
@@ -18242,12 +18481,13 @@ async function addFolderAsync(zip, root, prefix) {
|
|
|
18242
18481
|
// src/features/analysis/skill_quarantine/queryVerdicts.ts
|
|
18243
18482
|
async function queryVerdicts(gqlClient, md5s, log2) {
|
|
18244
18483
|
if (md5s.length === 0) {
|
|
18245
|
-
return /* @__PURE__ */ new Map();
|
|
18484
|
+
return { verdicts: /* @__PURE__ */ new Map(), quarantineEnabled: false };
|
|
18246
18485
|
}
|
|
18247
18486
|
try {
|
|
18248
18487
|
const res = await gqlClient.skillVerdictsByMd5(md5s);
|
|
18488
|
+
const envelope = res.skillVerdictsByMd5;
|
|
18249
18489
|
const out = /* @__PURE__ */ new Map();
|
|
18250
|
-
for (const row of
|
|
18490
|
+
for (const row of envelope.verdicts) {
|
|
18251
18491
|
out.set(row.md5, {
|
|
18252
18492
|
md5: row.md5,
|
|
18253
18493
|
verdict: row.verdict,
|
|
@@ -18257,18 +18497,41 @@ async function queryVerdicts(gqlClient, md5s, log2) {
|
|
|
18257
18497
|
scannedAt: row.scannedAt
|
|
18258
18498
|
});
|
|
18259
18499
|
}
|
|
18260
|
-
return out;
|
|
18500
|
+
return { verdicts: out, quarantineEnabled: envelope.quarantineEnabled };
|
|
18261
18501
|
} catch (err) {
|
|
18262
18502
|
log2.warn(
|
|
18263
18503
|
{ err, md5_count: md5s.length, metric: "skill_quarantine.query_error" },
|
|
18264
18504
|
"skill_quarantine: verdict query failed, failing open"
|
|
18265
18505
|
);
|
|
18266
|
-
return /* @__PURE__ */ new Map();
|
|
18506
|
+
return { verdicts: /* @__PURE__ */ new Map(), quarantineEnabled: false };
|
|
18267
18507
|
}
|
|
18268
18508
|
}
|
|
18269
18509
|
|
|
18270
18510
|
// src/features/analysis/skill_quarantine/runQuarantineCheck.ts
|
|
18511
|
+
var SKILL_PARENT_DIRS = [
|
|
18512
|
+
".claude/skills",
|
|
18513
|
+
".claude/commands",
|
|
18514
|
+
".claude/agents"
|
|
18515
|
+
];
|
|
18516
|
+
async function getSkillDirsMtimeMs(cwd) {
|
|
18517
|
+
const home = homedir4();
|
|
18518
|
+
const dirs = SKILL_PARENT_DIRS.flatMap((d) => [
|
|
18519
|
+
path19.join(cwd, d),
|
|
18520
|
+
path19.join(home, d)
|
|
18521
|
+
]);
|
|
18522
|
+
let max = 0;
|
|
18523
|
+
for (const dir of dirs) {
|
|
18524
|
+
try {
|
|
18525
|
+
const s = await stat3(dir);
|
|
18526
|
+
if (s.mtimeMs > max) max = s.mtimeMs;
|
|
18527
|
+
} catch {
|
|
18528
|
+
}
|
|
18529
|
+
}
|
|
18530
|
+
return max;
|
|
18531
|
+
}
|
|
18271
18532
|
var lastRunAt = /* @__PURE__ */ new Map();
|
|
18533
|
+
var lastDirsMtimeMs = /* @__PURE__ */ new Map();
|
|
18534
|
+
var seenSkillMd5s = /* @__PURE__ */ new Set();
|
|
18272
18535
|
var killSwitchLogged = false;
|
|
18273
18536
|
async function runQuarantineCheckIfNeeded(opts) {
|
|
18274
18537
|
const { sessionId, cwd, gqlClient, log: log2 } = opts;
|
|
@@ -18284,18 +18547,25 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
18284
18547
|
}
|
|
18285
18548
|
const now = Date.now();
|
|
18286
18549
|
const prev = lastRunAt.get(sessionId);
|
|
18287
|
-
|
|
18550
|
+
const withinDebounce = prev !== void 0 && now - prev < HEARTBEAT_DEBOUNCE_MS;
|
|
18551
|
+
lastRunAt.set(sessionId, now);
|
|
18552
|
+
const dirsMtime = await getSkillDirsMtimeMs(cwd);
|
|
18553
|
+
if (withinDebounce && dirsMtime <= (lastDirsMtimeMs.get(sessionId) ?? 0)) {
|
|
18288
18554
|
return;
|
|
18289
18555
|
}
|
|
18290
|
-
|
|
18556
|
+
const installed = await enumerateInstalledSkills(cwd);
|
|
18557
|
+
const hasNewSkills = installed.some((s) => !seenSkillMd5s.has(s.md5));
|
|
18558
|
+
if (!hasNewSkills && withinDebounce) {
|
|
18559
|
+
return;
|
|
18560
|
+
}
|
|
18561
|
+
lastDirsMtimeMs.set(sessionId, dirsMtime);
|
|
18291
18562
|
log2.info(
|
|
18292
|
-
{ sessionId, metric: Metric.CHECK_TRIGGERED },
|
|
18563
|
+
{ sessionId, metric: Metric.CHECK_TRIGGERED, hasNewSkills },
|
|
18293
18564
|
"skill_quarantine: check start"
|
|
18294
18565
|
);
|
|
18295
18566
|
const t0 = Date.now();
|
|
18296
18567
|
try {
|
|
18297
18568
|
await reconcileAndSweep(log2);
|
|
18298
|
-
const installed = await enumerateInstalledSkills(cwd);
|
|
18299
18569
|
log2.info(
|
|
18300
18570
|
{ sessionId, count: installed.length, metric: Metric.SKILLS_CHECKED },
|
|
18301
18571
|
"skill_quarantine: skills enumerated"
|
|
@@ -18303,11 +18573,25 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
18303
18573
|
if (installed.length === 0) {
|
|
18304
18574
|
return;
|
|
18305
18575
|
}
|
|
18306
|
-
const
|
|
18576
|
+
const currentMd5s = new Set(installed.map((s) => s.md5));
|
|
18577
|
+
for (const md5 of seenSkillMd5s) {
|
|
18578
|
+
if (!currentMd5s.has(md5)) seenSkillMd5s.delete(md5);
|
|
18579
|
+
}
|
|
18580
|
+
for (const skill of installed) {
|
|
18581
|
+
seenSkillMd5s.add(skill.md5);
|
|
18582
|
+
}
|
|
18583
|
+
const { verdicts, quarantineEnabled } = await queryVerdicts(
|
|
18307
18584
|
gqlClient,
|
|
18308
18585
|
installed.map((s) => s.md5),
|
|
18309
18586
|
log2
|
|
18310
18587
|
);
|
|
18588
|
+
if (!quarantineEnabled) {
|
|
18589
|
+
log2.info(
|
|
18590
|
+
{ sessionId, metric: Metric.CHECK_DISABLED_ORG },
|
|
18591
|
+
"skill_quarantine: opt-in not enabled for any org of caller; skipping enforcement"
|
|
18592
|
+
);
|
|
18593
|
+
return;
|
|
18594
|
+
}
|
|
18311
18595
|
for (const skill of installed) {
|
|
18312
18596
|
const verdict = verdicts.get(skill.md5);
|
|
18313
18597
|
if (!verdict || verdict.verdict !== MALICIOUS_VERDICT) {
|
|
@@ -18344,7 +18628,7 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
18344
18628
|
// src/features/claude_code/daemon_pid_file.ts
|
|
18345
18629
|
import fs13 from "fs";
|
|
18346
18630
|
import os4 from "os";
|
|
18347
|
-
import
|
|
18631
|
+
import path20 from "path";
|
|
18348
18632
|
|
|
18349
18633
|
// src/features/claude_code/data_collector_constants.ts
|
|
18350
18634
|
var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
|
|
@@ -18365,17 +18649,17 @@ var CONTEXT_SCAN_INTERVAL_MS = 5e3;
|
|
|
18365
18649
|
|
|
18366
18650
|
// src/features/claude_code/daemon_pid_file.ts
|
|
18367
18651
|
function getMobbdevDir() {
|
|
18368
|
-
return
|
|
18652
|
+
return path20.join(os4.homedir(), ".mobbdev");
|
|
18369
18653
|
}
|
|
18370
18654
|
function getDaemonCheckScriptPath() {
|
|
18371
|
-
return
|
|
18655
|
+
return path20.join(getMobbdevDir(), "daemon-check.js");
|
|
18372
18656
|
}
|
|
18373
18657
|
var DaemonPidFile = class {
|
|
18374
18658
|
constructor() {
|
|
18375
18659
|
__publicField(this, "data", null);
|
|
18376
18660
|
}
|
|
18377
18661
|
get filePath() {
|
|
18378
|
-
return
|
|
18662
|
+
return path20.join(getMobbdevDir(), "daemon.pid");
|
|
18379
18663
|
}
|
|
18380
18664
|
/** Ensure ~/.mobbdev/ directory exists. */
|
|
18381
18665
|
ensureDir() {
|
|
@@ -18438,7 +18722,7 @@ var DaemonPidFile = class {
|
|
|
18438
18722
|
import { execFile } from "child_process";
|
|
18439
18723
|
import { createHash as createHash3 } from "crypto";
|
|
18440
18724
|
import { access as access2, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
|
|
18441
|
-
import
|
|
18725
|
+
import path21 from "path";
|
|
18442
18726
|
import { promisify } from "util";
|
|
18443
18727
|
|
|
18444
18728
|
// src/features/analysis/context_file_uploader.ts
|
|
@@ -18703,8 +18987,8 @@ function createConfigstoreStream(store, opts) {
|
|
|
18703
18987
|
heartbeatBuffer.length = 0;
|
|
18704
18988
|
}
|
|
18705
18989
|
}
|
|
18706
|
-
function setScopePath(
|
|
18707
|
-
scopePath =
|
|
18990
|
+
function setScopePath(path37) {
|
|
18991
|
+
scopePath = path37;
|
|
18708
18992
|
}
|
|
18709
18993
|
return { writable, flush, setScopePath };
|
|
18710
18994
|
}
|
|
@@ -18928,18 +19212,24 @@ function createLogger(config2) {
|
|
|
18928
19212
|
|
|
18929
19213
|
// src/features/claude_code/hook_logger.ts
|
|
18930
19214
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
18931
|
-
var CLI_VERSION = true ? "1.4.
|
|
19215
|
+
var CLI_VERSION = true ? "1.4.9" : "unknown";
|
|
18932
19216
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
18933
19217
|
var claudeCodeVersion;
|
|
18934
19218
|
function buildDdTags() {
|
|
18935
|
-
const tags = [
|
|
19219
|
+
const tags = [
|
|
19220
|
+
`version:${CLI_VERSION}`,
|
|
19221
|
+
`platform:claude_code`,
|
|
19222
|
+
`os:${process.platform}`,
|
|
19223
|
+
`arch:${process.arch}`
|
|
19224
|
+
];
|
|
18936
19225
|
if (claudeCodeVersion) {
|
|
18937
19226
|
tags.push(`cc_version:${claudeCodeVersion}`);
|
|
18938
19227
|
}
|
|
18939
19228
|
return tags.join(",");
|
|
18940
19229
|
}
|
|
19230
|
+
var handlingDdError = false;
|
|
18941
19231
|
function createHookLogger(opts) {
|
|
18942
|
-
|
|
19232
|
+
const created = createLogger({
|
|
18943
19233
|
namespace: NAMESPACE,
|
|
18944
19234
|
scopePath: opts?.scopePath,
|
|
18945
19235
|
enableConfigstore: opts?.enableConfigstore,
|
|
@@ -18949,9 +19239,26 @@ function createHookLogger(opts) {
|
|
|
18949
19239
|
service: "mobbdev-cli-hook",
|
|
18950
19240
|
ddtags: buildDdTags(),
|
|
18951
19241
|
hostnameMode: "hashed",
|
|
18952
|
-
unrefTimer: true
|
|
19242
|
+
unrefTimer: true,
|
|
19243
|
+
onError: (error) => {
|
|
19244
|
+
if (handlingDdError) {
|
|
19245
|
+
process.stderr.write(`dd-ship-error: ${String(error)}
|
|
19246
|
+
`);
|
|
19247
|
+
return;
|
|
19248
|
+
}
|
|
19249
|
+
handlingDdError = true;
|
|
19250
|
+
try {
|
|
19251
|
+
created.warn(
|
|
19252
|
+
{ err: String(error), source: "dd-log-shipping" },
|
|
19253
|
+
"Datadog log shipping failed"
|
|
19254
|
+
);
|
|
19255
|
+
} finally {
|
|
19256
|
+
handlingDdError = false;
|
|
19257
|
+
}
|
|
19258
|
+
}
|
|
18953
19259
|
}
|
|
18954
19260
|
});
|
|
19261
|
+
return created;
|
|
18955
19262
|
}
|
|
18956
19263
|
var logger = createHookLogger();
|
|
18957
19264
|
var activeScopedLoggers = [];
|
|
@@ -18981,6 +19288,9 @@ function createScopedHookLog(scopePath, opts) {
|
|
|
18981
19288
|
activeScopedLoggers.push(scoped);
|
|
18982
19289
|
return scoped;
|
|
18983
19290
|
}
|
|
19291
|
+
function getScopedLoggerCount() {
|
|
19292
|
+
return scopedLoggerCache.size;
|
|
19293
|
+
}
|
|
18984
19294
|
|
|
18985
19295
|
// src/features/claude_code/data_collector.ts
|
|
18986
19296
|
var execFileAsync = promisify(execFile);
|
|
@@ -19019,12 +19329,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
19019
19329
|
return transcriptPath;
|
|
19020
19330
|
} catch {
|
|
19021
19331
|
}
|
|
19022
|
-
const filename =
|
|
19023
|
-
const dirName =
|
|
19024
|
-
const projectsDir =
|
|
19332
|
+
const filename = path21.basename(transcriptPath);
|
|
19333
|
+
const dirName = path21.basename(path21.dirname(transcriptPath));
|
|
19334
|
+
const projectsDir = path21.dirname(path21.dirname(transcriptPath));
|
|
19025
19335
|
const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
|
|
19026
19336
|
if (baseDirName !== dirName) {
|
|
19027
|
-
const candidate =
|
|
19337
|
+
const candidate = path21.join(projectsDir, baseDirName, filename);
|
|
19028
19338
|
try {
|
|
19029
19339
|
await access2(candidate);
|
|
19030
19340
|
hookLog.info(
|
|
@@ -19046,7 +19356,7 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
19046
19356
|
const dirs = await readdir3(projectsDir);
|
|
19047
19357
|
for (const dir of dirs) {
|
|
19048
19358
|
if (dir === dirName) continue;
|
|
19049
|
-
const candidate =
|
|
19359
|
+
const candidate = path21.join(projectsDir, dir, filename);
|
|
19050
19360
|
try {
|
|
19051
19361
|
await access2(candidate);
|
|
19052
19362
|
hookLog.info(
|
|
@@ -19075,21 +19385,33 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
19075
19385
|
let fileSize;
|
|
19076
19386
|
let lineIndexOffset;
|
|
19077
19387
|
if (cursor?.byteOffset) {
|
|
19388
|
+
const MAX_TRANSCRIPT_READ_BYTES = 10 * 1024 * 1024;
|
|
19078
19389
|
const fh = await open4(transcriptPath, "r");
|
|
19079
19390
|
try {
|
|
19080
|
-
const
|
|
19081
|
-
fileSize =
|
|
19082
|
-
if (cursor.byteOffset >=
|
|
19391
|
+
const stat5 = await fh.stat();
|
|
19392
|
+
fileSize = stat5.size;
|
|
19393
|
+
if (cursor.byteOffset >= stat5.size) {
|
|
19083
19394
|
hookLog.info({ data: { sessionId } }, "No new data in transcript file");
|
|
19084
19395
|
return {
|
|
19085
19396
|
entries: [],
|
|
19086
19397
|
endByteOffset: fileSize,
|
|
19087
|
-
resolvedTranscriptPath: transcriptPath
|
|
19398
|
+
resolvedTranscriptPath: transcriptPath,
|
|
19399
|
+
transcriptBytesRead: 0
|
|
19088
19400
|
};
|
|
19089
19401
|
}
|
|
19090
|
-
const
|
|
19091
|
-
|
|
19402
|
+
const bytesToRead = Math.min(
|
|
19403
|
+
stat5.size - cursor.byteOffset,
|
|
19404
|
+
MAX_TRANSCRIPT_READ_BYTES
|
|
19405
|
+
);
|
|
19406
|
+
const buf = Buffer.alloc(bytesToRead);
|
|
19407
|
+
await fh.read(buf, 0, bytesToRead, cursor.byteOffset);
|
|
19092
19408
|
content = buf.toString("utf-8");
|
|
19409
|
+
if (bytesToRead < stat5.size - cursor.byteOffset && !content.endsWith("\n")) {
|
|
19410
|
+
const lastNewline = content.lastIndexOf("\n");
|
|
19411
|
+
if (lastNewline > 0) {
|
|
19412
|
+
content = content.substring(0, lastNewline + 1);
|
|
19413
|
+
}
|
|
19414
|
+
}
|
|
19093
19415
|
} finally {
|
|
19094
19416
|
await fh.close();
|
|
19095
19417
|
}
|
|
@@ -19105,12 +19427,34 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
19105
19427
|
"Read transcript file from offset"
|
|
19106
19428
|
);
|
|
19107
19429
|
} else {
|
|
19108
|
-
|
|
19109
|
-
|
|
19430
|
+
const MAX_CWD_READ_BYTES = 2 * 1024 * 1024;
|
|
19431
|
+
const fh = await open4(transcriptPath, "r");
|
|
19432
|
+
try {
|
|
19433
|
+
const stat5 = await fh.stat();
|
|
19434
|
+
fileSize = stat5.size;
|
|
19435
|
+
const bytesToRead = Math.min(stat5.size, MAX_CWD_READ_BYTES);
|
|
19436
|
+
const buf = Buffer.alloc(bytesToRead);
|
|
19437
|
+
await fh.read(buf, 0, bytesToRead, 0);
|
|
19438
|
+
content = buf.toString("utf-8");
|
|
19439
|
+
if (bytesToRead < stat5.size && !content.endsWith("\n")) {
|
|
19440
|
+
const lastNewline = content.lastIndexOf("\n");
|
|
19441
|
+
if (lastNewline > 0) {
|
|
19442
|
+
content = content.substring(0, lastNewline + 1);
|
|
19443
|
+
}
|
|
19444
|
+
}
|
|
19445
|
+
} finally {
|
|
19446
|
+
await fh.close();
|
|
19447
|
+
}
|
|
19110
19448
|
lineIndexOffset = 0;
|
|
19111
19449
|
hookLog.debug(
|
|
19112
|
-
{
|
|
19113
|
-
|
|
19450
|
+
{
|
|
19451
|
+
data: {
|
|
19452
|
+
transcriptPath,
|
|
19453
|
+
totalBytes: fileSize,
|
|
19454
|
+
cappedBytes: content.length
|
|
19455
|
+
}
|
|
19456
|
+
},
|
|
19457
|
+
"Read transcript file (first run)"
|
|
19114
19458
|
);
|
|
19115
19459
|
}
|
|
19116
19460
|
const startOffset = cursor?.byteOffset ?? 0;
|
|
@@ -19175,7 +19519,8 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
19175
19519
|
return {
|
|
19176
19520
|
entries: parsed,
|
|
19177
19521
|
endByteOffset,
|
|
19178
|
-
resolvedTranscriptPath: transcriptPath
|
|
19522
|
+
resolvedTranscriptPath: transcriptPath,
|
|
19523
|
+
transcriptBytesRead: content.length
|
|
19179
19524
|
};
|
|
19180
19525
|
}
|
|
19181
19526
|
var FILTERED_PROGRESS_SUBTYPES = /* @__PURE__ */ new Set([
|
|
@@ -19224,14 +19569,16 @@ async function cleanupStaleSessions(configDir) {
|
|
|
19224
19569
|
if (lastCleanup && Date.now() - lastCleanup < CLEANUP_INTERVAL_MS) {
|
|
19225
19570
|
return;
|
|
19226
19571
|
}
|
|
19227
|
-
const
|
|
19572
|
+
const cleanupStart = Date.now();
|
|
19228
19573
|
const prefix = getSessionFilePrefix();
|
|
19229
19574
|
try {
|
|
19230
19575
|
const files = await readdir3(configDir);
|
|
19576
|
+
const sessionFiles = files.filter(
|
|
19577
|
+
(f) => f.startsWith(prefix) && f.endsWith(".json")
|
|
19578
|
+
);
|
|
19231
19579
|
let deletedCount = 0;
|
|
19232
|
-
for (const file of
|
|
19233
|
-
|
|
19234
|
-
const filePath = path20.join(configDir, file);
|
|
19580
|
+
for (const file of sessionFiles) {
|
|
19581
|
+
const filePath = path21.join(configDir, file);
|
|
19235
19582
|
try {
|
|
19236
19583
|
const content = JSON.parse(await readFile3(filePath, "utf-8"));
|
|
19237
19584
|
let newest = 0;
|
|
@@ -19242,25 +19589,34 @@ async function cleanupStaleSessions(configDir) {
|
|
|
19242
19589
|
if (c?.updatedAt && c.updatedAt > newest) newest = c.updatedAt;
|
|
19243
19590
|
}
|
|
19244
19591
|
}
|
|
19245
|
-
if (newest > 0 &&
|
|
19592
|
+
if (newest > 0 && cleanupStart - newest > STALE_KEY_MAX_AGE_MS) {
|
|
19246
19593
|
await unlink2(filePath);
|
|
19247
19594
|
deletedCount++;
|
|
19248
19595
|
}
|
|
19249
19596
|
} catch {
|
|
19250
19597
|
}
|
|
19251
19598
|
}
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
|
|
19599
|
+
hookLog.info(
|
|
19600
|
+
{
|
|
19601
|
+
heartbeat: true,
|
|
19602
|
+
data: {
|
|
19603
|
+
sessionFileCount: sessionFiles.length,
|
|
19604
|
+
deletedCount,
|
|
19605
|
+
cleanupDurationMs: Date.now() - cleanupStart
|
|
19606
|
+
}
|
|
19607
|
+
},
|
|
19608
|
+
"Session cleanup"
|
|
19609
|
+
);
|
|
19255
19610
|
} catch {
|
|
19256
19611
|
}
|
|
19257
|
-
configStore.set("claudeCode.lastCleanupAt",
|
|
19612
|
+
configStore.set("claudeCode.lastCleanupAt", cleanupStart);
|
|
19258
19613
|
}
|
|
19259
19614
|
async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_CHUNK_SIZE, gqlClientOverride) {
|
|
19260
19615
|
const {
|
|
19261
19616
|
entries: rawEntries,
|
|
19262
19617
|
endByteOffset,
|
|
19263
|
-
resolvedTranscriptPath
|
|
19618
|
+
resolvedTranscriptPath,
|
|
19619
|
+
transcriptBytesRead
|
|
19264
19620
|
} = await log2.timed(
|
|
19265
19621
|
"Read transcript",
|
|
19266
19622
|
() => readNewTranscriptEntries(
|
|
@@ -19347,15 +19703,21 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
19347
19703
|
rawData: rawEntry
|
|
19348
19704
|
};
|
|
19349
19705
|
});
|
|
19350
|
-
|
|
19351
|
-
|
|
19352
|
-
|
|
19706
|
+
let totalRawDataBytes = 0;
|
|
19707
|
+
let maxRecordBytes = 0;
|
|
19708
|
+
for (const r of records) {
|
|
19709
|
+
const size = r.rawData ? JSON.stringify(r.rawData).length : 0;
|
|
19710
|
+
totalRawDataBytes += size;
|
|
19711
|
+
if (size > maxRecordBytes) maxRecordBytes = size;
|
|
19712
|
+
}
|
|
19353
19713
|
log2.info(
|
|
19354
19714
|
{
|
|
19355
19715
|
data: {
|
|
19356
19716
|
count: records.length,
|
|
19357
19717
|
skipped: filteredOut,
|
|
19718
|
+
transcriptBytesRead,
|
|
19358
19719
|
rawDataBytes: totalRawDataBytes,
|
|
19720
|
+
maxRecordBytes,
|
|
19359
19721
|
firstRecordId: records[0]?.recordId,
|
|
19360
19722
|
lastRecordId: records[records.length - 1]?.recordId
|
|
19361
19723
|
}
|
|
@@ -19469,14 +19831,14 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
|
19469
19831
|
import fs14 from "fs";
|
|
19470
19832
|
import fsPromises4 from "fs/promises";
|
|
19471
19833
|
import os6 from "os";
|
|
19472
|
-
import
|
|
19834
|
+
import path22 from "path";
|
|
19473
19835
|
import chalk11 from "chalk";
|
|
19474
19836
|
|
|
19475
19837
|
// src/features/claude_code/daemon-check-shim.tmpl.js
|
|
19476
19838
|
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";
|
|
19477
19839
|
|
|
19478
19840
|
// src/features/claude_code/install_hook.ts
|
|
19479
|
-
var CLAUDE_SETTINGS_PATH =
|
|
19841
|
+
var CLAUDE_SETTINGS_PATH = path22.join(os6.homedir(), ".claude", "settings.json");
|
|
19480
19842
|
var RECOMMENDED_MATCHER = "*";
|
|
19481
19843
|
async function claudeSettingsExists() {
|
|
19482
19844
|
try {
|
|
@@ -19622,18 +19984,18 @@ async function installMobbHooks(options = {}) {
|
|
|
19622
19984
|
}
|
|
19623
19985
|
|
|
19624
19986
|
// src/features/claude_code/transcript_scanner.ts
|
|
19625
|
-
import { open as open5, readdir as readdir4, stat as
|
|
19987
|
+
import { open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
19626
19988
|
import os7 from "os";
|
|
19627
|
-
import
|
|
19989
|
+
import path23 from "path";
|
|
19628
19990
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19629
19991
|
function getClaudeProjectsDirs() {
|
|
19630
19992
|
const dirs = [];
|
|
19631
19993
|
const configDir = process.env["CLAUDE_CONFIG_DIR"];
|
|
19632
19994
|
if (configDir) {
|
|
19633
|
-
dirs.push(
|
|
19995
|
+
dirs.push(path23.join(configDir, "projects"));
|
|
19634
19996
|
}
|
|
19635
|
-
dirs.push(
|
|
19636
|
-
dirs.push(
|
|
19997
|
+
dirs.push(path23.join(os7.homedir(), ".config", "claude", "projects"));
|
|
19998
|
+
dirs.push(path23.join(os7.homedir(), ".claude", "projects"));
|
|
19637
19999
|
return dirs;
|
|
19638
20000
|
}
|
|
19639
20001
|
async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
@@ -19641,12 +20003,12 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
|
19641
20003
|
if (!file.endsWith(".jsonl")) continue;
|
|
19642
20004
|
const sessionId = file.replace(".jsonl", "");
|
|
19643
20005
|
if (!UUID_RE.test(sessionId)) continue;
|
|
19644
|
-
const filePath =
|
|
20006
|
+
const filePath = path23.join(dir, file);
|
|
19645
20007
|
if (seen.has(filePath)) continue;
|
|
19646
20008
|
seen.add(filePath);
|
|
19647
20009
|
let fileStat;
|
|
19648
20010
|
try {
|
|
19649
|
-
fileStat = await
|
|
20011
|
+
fileStat = await stat4(filePath);
|
|
19650
20012
|
} catch {
|
|
19651
20013
|
continue;
|
|
19652
20014
|
}
|
|
@@ -19672,10 +20034,10 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19672
20034
|
continue;
|
|
19673
20035
|
}
|
|
19674
20036
|
for (const projName of projectDirs) {
|
|
19675
|
-
const projPath =
|
|
20037
|
+
const projPath = path23.join(projectsDir, projName);
|
|
19676
20038
|
let projStat;
|
|
19677
20039
|
try {
|
|
19678
|
-
projStat = await
|
|
20040
|
+
projStat = await stat4(projPath);
|
|
19679
20041
|
} catch {
|
|
19680
20042
|
continue;
|
|
19681
20043
|
}
|
|
@@ -19689,9 +20051,9 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19689
20051
|
await collectJsonlFiles(files, projPath, projPath, seen, now, results);
|
|
19690
20052
|
for (const entry of files) {
|
|
19691
20053
|
if (!UUID_RE.test(entry)) continue;
|
|
19692
|
-
const subagentsDir =
|
|
20054
|
+
const subagentsDir = path23.join(projPath, entry, "subagents");
|
|
19693
20055
|
try {
|
|
19694
|
-
const s = await
|
|
20056
|
+
const s = await stat4(subagentsDir);
|
|
19695
20057
|
if (!s.isDirectory()) continue;
|
|
19696
20058
|
const subFiles = await readdir4(subagentsDir);
|
|
19697
20059
|
await collectJsonlFiles(
|
|
@@ -19739,6 +20101,9 @@ async function extractCwdFromTranscript(filePath) {
|
|
|
19739
20101
|
}
|
|
19740
20102
|
return void 0;
|
|
19741
20103
|
}
|
|
20104
|
+
function getCwdCacheSize() {
|
|
20105
|
+
return cwdCache.size;
|
|
20106
|
+
}
|
|
19742
20107
|
|
|
19743
20108
|
// src/features/claude_code/daemon.ts
|
|
19744
20109
|
async function startDaemon() {
|
|
@@ -19779,6 +20144,11 @@ async function startDaemon() {
|
|
|
19779
20144
|
let cleanupConfigDir;
|
|
19780
20145
|
const sessionCwdCache = /* @__PURE__ */ new Map();
|
|
19781
20146
|
let lastContextScanMs = 0;
|
|
20147
|
+
const machineContext = {
|
|
20148
|
+
cpuCount: os8.cpus().length,
|
|
20149
|
+
totalMemGB: Math.round(os8.totalmem() / 1024 ** 3),
|
|
20150
|
+
nodeVersion: process.version
|
|
20151
|
+
};
|
|
19782
20152
|
while (true) {
|
|
19783
20153
|
if (shuttingDown) {
|
|
19784
20154
|
await gracefulExit(0, "signal");
|
|
@@ -19787,19 +20157,44 @@ async function startDaemon() {
|
|
|
19787
20157
|
await gracefulExit(0, "TTL reached");
|
|
19788
20158
|
}
|
|
19789
20159
|
pidFile.updateHeartbeat();
|
|
20160
|
+
const cpuBefore = process.cpuUsage();
|
|
20161
|
+
const heapBefore = process.memoryUsage().heapUsed;
|
|
20162
|
+
const cycleStart = Date.now();
|
|
20163
|
+
let changedCount = 0;
|
|
20164
|
+
let totalUploaded = 0;
|
|
20165
|
+
let totalErrors = 0;
|
|
19790
20166
|
try {
|
|
19791
20167
|
const changed = await detectChangedTranscripts(lastSeen);
|
|
20168
|
+
changedCount = changed.length;
|
|
19792
20169
|
for (const transcript of changed) {
|
|
19793
20170
|
const sessionStore = createSessionConfigStore(transcript.sessionId);
|
|
19794
20171
|
if (!cleanupConfigDir) {
|
|
19795
|
-
cleanupConfigDir =
|
|
20172
|
+
cleanupConfigDir = path24.dirname(sessionStore.path);
|
|
19796
20173
|
}
|
|
19797
|
-
|
|
20174
|
+
pidFile.updateHeartbeat();
|
|
20175
|
+
const drainStart = Date.now();
|
|
20176
|
+
const result = await drainTranscript(
|
|
19798
20177
|
transcript,
|
|
19799
20178
|
sessionStore,
|
|
19800
20179
|
gqlClient,
|
|
19801
20180
|
sessionCwdCache
|
|
19802
20181
|
);
|
|
20182
|
+
totalUploaded += result.uploaded;
|
|
20183
|
+
totalErrors += result.errors;
|
|
20184
|
+
if (result.uploaded > 0 || result.errors > 0) {
|
|
20185
|
+
hookLog.info(
|
|
20186
|
+
{
|
|
20187
|
+
data: {
|
|
20188
|
+
sessionId: transcript.sessionId,
|
|
20189
|
+
drainDurationMs: Date.now() - drainStart,
|
|
20190
|
+
chunksProcessed: result.chunks,
|
|
20191
|
+
entriesUploaded: result.uploaded,
|
|
20192
|
+
errors: result.errors
|
|
20193
|
+
}
|
|
20194
|
+
},
|
|
20195
|
+
"Transcript drained"
|
|
20196
|
+
);
|
|
20197
|
+
}
|
|
19803
20198
|
}
|
|
19804
20199
|
if (lastSeen.size > 0) {
|
|
19805
20200
|
for (const filePath of sessionCwdCache.keys()) {
|
|
@@ -19822,6 +20217,29 @@ async function startDaemon() {
|
|
|
19822
20217
|
} catch (err) {
|
|
19823
20218
|
hookLog.warn({ err }, "Unexpected error in daemon cycle");
|
|
19824
20219
|
}
|
|
20220
|
+
const cpuDelta = process.cpuUsage(cpuBefore);
|
|
20221
|
+
const heapAfter = process.memoryUsage().heapUsed;
|
|
20222
|
+
hookLog.info(
|
|
20223
|
+
{
|
|
20224
|
+
heartbeat: true,
|
|
20225
|
+
data: {
|
|
20226
|
+
cycleDurationMs: Date.now() - cycleStart,
|
|
20227
|
+
cpuUserUs: cpuDelta.user,
|
|
20228
|
+
cpuSystemUs: cpuDelta.system,
|
|
20229
|
+
heapDeltaBytes: heapAfter - heapBefore,
|
|
20230
|
+
heapUsedBytes: heapAfter,
|
|
20231
|
+
daemonUptimeMs: Date.now() - startedAt,
|
|
20232
|
+
changedTranscripts: changedCount,
|
|
20233
|
+
activeTranscripts: lastSeen.size,
|
|
20234
|
+
entriesUploaded: totalUploaded,
|
|
20235
|
+
errors: totalErrors,
|
|
20236
|
+
cwdCacheSize: getCwdCacheSize(),
|
|
20237
|
+
scopedLoggerCount: getScopedLoggerCount(),
|
|
20238
|
+
...machineContext
|
|
20239
|
+
}
|
|
20240
|
+
},
|
|
20241
|
+
"daemon poll cycle"
|
|
20242
|
+
);
|
|
19825
20243
|
await sleep2(DAEMON_POLL_INTERVAL_MS);
|
|
19826
20244
|
}
|
|
19827
20245
|
}
|
|
@@ -19860,6 +20278,9 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
|
|
|
19860
20278
|
const log2 = createScopedHookLog(cwd ?? transcript.projectDir, {
|
|
19861
20279
|
daemonMode: true
|
|
19862
20280
|
});
|
|
20281
|
+
let totalUploaded = 0;
|
|
20282
|
+
let totalErrors = 0;
|
|
20283
|
+
let chunks = 0;
|
|
19863
20284
|
if (cwd) {
|
|
19864
20285
|
sessionCwdCache.set(transcript.filePath, {
|
|
19865
20286
|
sessionId: transcript.sessionId,
|
|
@@ -19869,6 +20290,7 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
|
|
|
19869
20290
|
try {
|
|
19870
20291
|
let hasMore = true;
|
|
19871
20292
|
while (hasMore) {
|
|
20293
|
+
chunks++;
|
|
19872
20294
|
const result = await processTranscript(
|
|
19873
20295
|
{
|
|
19874
20296
|
session_id: transcript.sessionId,
|
|
@@ -19880,8 +20302,10 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
|
|
|
19880
20302
|
DAEMON_CHUNK_SIZE,
|
|
19881
20303
|
gqlClient
|
|
19882
20304
|
);
|
|
20305
|
+
totalUploaded += result.entriesUploaded;
|
|
19883
20306
|
hasMore = result.entriesUploaded + result.entriesSkipped >= DAEMON_CHUNK_SIZE;
|
|
19884
20307
|
if (result.errors > 0) {
|
|
20308
|
+
totalErrors += result.errors;
|
|
19885
20309
|
hookLog.warn(
|
|
19886
20310
|
{
|
|
19887
20311
|
data: {
|
|
@@ -19913,6 +20337,7 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
|
|
|
19913
20337
|
);
|
|
19914
20338
|
});
|
|
19915
20339
|
}
|
|
20340
|
+
return { uploaded: totalUploaded, errors: totalErrors, chunks };
|
|
19916
20341
|
}
|
|
19917
20342
|
async function detectChangedTranscripts(lastSeen) {
|
|
19918
20343
|
const transcripts = await scanForTranscripts();
|
|
@@ -20089,8 +20514,8 @@ var WorkspaceService = class {
|
|
|
20089
20514
|
* Sets a known workspace path that was discovered through successful validation
|
|
20090
20515
|
* @param path The validated workspace path to store
|
|
20091
20516
|
*/
|
|
20092
|
-
static setKnownWorkspacePath(
|
|
20093
|
-
this.knownWorkspacePath =
|
|
20517
|
+
static setKnownWorkspacePath(path37) {
|
|
20518
|
+
this.knownWorkspacePath = path37;
|
|
20094
20519
|
}
|
|
20095
20520
|
/**
|
|
20096
20521
|
* Gets the known workspace path that was previously validated
|
|
@@ -20950,8 +21375,8 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
20950
21375
|
// src/mcp/services/McpUsageService/host.ts
|
|
20951
21376
|
import { execSync as execSync2 } from "child_process";
|
|
20952
21377
|
import fs15 from "fs";
|
|
20953
|
-
import
|
|
20954
|
-
import
|
|
21378
|
+
import os9 from "os";
|
|
21379
|
+
import path25 from "path";
|
|
20955
21380
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
20956
21381
|
var runCommand = (cmd) => {
|
|
20957
21382
|
try {
|
|
@@ -20965,8 +21390,8 @@ var gitInfo = {
|
|
|
20965
21390
|
email: runCommand("git config user.email")
|
|
20966
21391
|
};
|
|
20967
21392
|
var getClaudeWorkspacePaths = () => {
|
|
20968
|
-
const home =
|
|
20969
|
-
const claudeIdePath =
|
|
21393
|
+
const home = os9.homedir();
|
|
21394
|
+
const claudeIdePath = path25.join(home, ".claude", "ide");
|
|
20970
21395
|
const workspacePaths = [];
|
|
20971
21396
|
if (!fs15.existsSync(claudeIdePath)) {
|
|
20972
21397
|
return workspacePaths;
|
|
@@ -20974,7 +21399,7 @@ var getClaudeWorkspacePaths = () => {
|
|
|
20974
21399
|
try {
|
|
20975
21400
|
const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
|
|
20976
21401
|
for (const lockFile of lockFiles) {
|
|
20977
|
-
const lockFilePath =
|
|
21402
|
+
const lockFilePath = path25.join(claudeIdePath, lockFile);
|
|
20978
21403
|
try {
|
|
20979
21404
|
const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
|
|
20980
21405
|
if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
|
|
@@ -20994,29 +21419,29 @@ var getClaudeWorkspacePaths = () => {
|
|
|
20994
21419
|
return workspacePaths;
|
|
20995
21420
|
};
|
|
20996
21421
|
var getMCPConfigPaths = (hostName) => {
|
|
20997
|
-
const home =
|
|
21422
|
+
const home = os9.homedir();
|
|
20998
21423
|
const currentDir = process.env["WORKSPACE_FOLDER_PATHS"] || process.env["PWD"] || process.cwd();
|
|
20999
21424
|
switch (hostName.toLowerCase()) {
|
|
21000
21425
|
case "cursor":
|
|
21001
21426
|
return [
|
|
21002
|
-
|
|
21427
|
+
path25.join(currentDir, ".cursor", "mcp.json"),
|
|
21003
21428
|
// local first
|
|
21004
|
-
|
|
21429
|
+
path25.join(home, ".cursor", "mcp.json")
|
|
21005
21430
|
];
|
|
21006
21431
|
case "windsurf":
|
|
21007
21432
|
return [
|
|
21008
|
-
|
|
21433
|
+
path25.join(currentDir, ".codeium", "mcp_config.json"),
|
|
21009
21434
|
// local first
|
|
21010
|
-
|
|
21435
|
+
path25.join(home, ".codeium", "windsurf", "mcp_config.json")
|
|
21011
21436
|
];
|
|
21012
21437
|
case "webstorm":
|
|
21013
21438
|
return [];
|
|
21014
21439
|
case "visualstudiocode":
|
|
21015
21440
|
case "vscode":
|
|
21016
21441
|
return [
|
|
21017
|
-
|
|
21442
|
+
path25.join(currentDir, ".vscode", "mcp.json"),
|
|
21018
21443
|
// local first
|
|
21019
|
-
process.platform === "win32" ?
|
|
21444
|
+
process.platform === "win32" ? path25.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path25.join(
|
|
21020
21445
|
home,
|
|
21021
21446
|
"Library",
|
|
21022
21447
|
"Application Support",
|
|
@@ -21027,13 +21452,13 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
21027
21452
|
];
|
|
21028
21453
|
case "claude": {
|
|
21029
21454
|
const claudePaths = [
|
|
21030
|
-
|
|
21455
|
+
path25.join(currentDir, ".claude.json"),
|
|
21031
21456
|
// local first
|
|
21032
|
-
|
|
21457
|
+
path25.join(home, ".claude.json")
|
|
21033
21458
|
];
|
|
21034
21459
|
const workspacePaths = getClaudeWorkspacePaths();
|
|
21035
21460
|
for (const workspacePath of workspacePaths) {
|
|
21036
|
-
claudePaths.push(
|
|
21461
|
+
claudePaths.push(path25.join(workspacePath, ".mcp.json"));
|
|
21037
21462
|
}
|
|
21038
21463
|
return claudePaths;
|
|
21039
21464
|
}
|
|
@@ -21084,7 +21509,7 @@ var readMCPConfig = (hostName) => {
|
|
|
21084
21509
|
};
|
|
21085
21510
|
var getRunningProcesses = () => {
|
|
21086
21511
|
try {
|
|
21087
|
-
return
|
|
21512
|
+
return os9.platform() === "win32" ? execSync2("tasklist", { encoding: "utf8" }) : execSync2("ps aux", { encoding: "utf8" });
|
|
21088
21513
|
} catch {
|
|
21089
21514
|
return "";
|
|
21090
21515
|
}
|
|
@@ -21159,7 +21584,7 @@ var versionCommands = {
|
|
|
21159
21584
|
}
|
|
21160
21585
|
};
|
|
21161
21586
|
var getProcessInfo = (pid) => {
|
|
21162
|
-
const platform2 =
|
|
21587
|
+
const platform2 = os9.platform();
|
|
21163
21588
|
try {
|
|
21164
21589
|
if (platform2 === "linux" || platform2 === "darwin") {
|
|
21165
21590
|
const output = execSync2(`ps -o pid=,ppid=,comm= -p ${pid}`, {
|
|
@@ -21194,10 +21619,10 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
21194
21619
|
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
21195
21620
|
for (const ide of IDEs) {
|
|
21196
21621
|
const configPaths = getMCPConfigPaths(ide);
|
|
21197
|
-
configPaths.forEach((
|
|
21622
|
+
configPaths.forEach((path37) => ideConfigPaths.add(path37));
|
|
21198
21623
|
}
|
|
21199
21624
|
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
21200
|
-
(
|
|
21625
|
+
(path37) => !ideConfigPaths.has(path37)
|
|
21201
21626
|
);
|
|
21202
21627
|
for (const ide of IDEs) {
|
|
21203
21628
|
const cfg = readMCPConfig(ide);
|
|
@@ -21278,7 +21703,7 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
21278
21703
|
const config2 = allConfigs[ide] || null;
|
|
21279
21704
|
const ideName = ide.charAt(0).toUpperCase() + ide.slice(1) || "Unknown";
|
|
21280
21705
|
let ideVersion = "Unknown";
|
|
21281
|
-
const platform2 =
|
|
21706
|
+
const platform2 = os9.platform();
|
|
21282
21707
|
const cmds = versionCommands[ideName]?.[platform2] ?? [];
|
|
21283
21708
|
for (const cmd of cmds) {
|
|
21284
21709
|
try {
|
|
@@ -21311,15 +21736,15 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
21311
21736
|
|
|
21312
21737
|
// src/mcp/services/McpUsageService/McpUsageService.ts
|
|
21313
21738
|
import fetch6 from "node-fetch";
|
|
21314
|
-
import
|
|
21739
|
+
import os11 from "os";
|
|
21315
21740
|
import { v4 as uuidv42, v5 as uuidv5 } from "uuid";
|
|
21316
21741
|
init_configs();
|
|
21317
21742
|
|
|
21318
21743
|
// src/mcp/services/McpUsageService/system.ts
|
|
21319
21744
|
init_configs();
|
|
21320
21745
|
import fs16 from "fs";
|
|
21321
|
-
import
|
|
21322
|
-
import
|
|
21746
|
+
import os10 from "os";
|
|
21747
|
+
import path26 from "path";
|
|
21323
21748
|
var MAX_DEPTH = 2;
|
|
21324
21749
|
var patterns = ["mcp", "claude"];
|
|
21325
21750
|
var isFileMatch = (fileName) => {
|
|
@@ -21339,7 +21764,7 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
21339
21764
|
if (depth > MAX_DEPTH) return results;
|
|
21340
21765
|
const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
21341
21766
|
for (const entry of entries) {
|
|
21342
|
-
const fullPath =
|
|
21767
|
+
const fullPath = path26.join(dir, entry.name);
|
|
21343
21768
|
if (entry.isFile() && isFileMatch(entry.name)) {
|
|
21344
21769
|
results.push(fullPath);
|
|
21345
21770
|
} else if (entry.isDirectory()) {
|
|
@@ -21353,17 +21778,17 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
21353
21778
|
};
|
|
21354
21779
|
var findSystemMCPConfigs = async () => {
|
|
21355
21780
|
try {
|
|
21356
|
-
const home =
|
|
21357
|
-
const platform2 =
|
|
21781
|
+
const home = os10.homedir();
|
|
21782
|
+
const platform2 = os10.platform();
|
|
21358
21783
|
const knownDirs = platform2 === "win32" ? [
|
|
21359
|
-
|
|
21360
|
-
|
|
21361
|
-
|
|
21784
|
+
path26.join(home, ".cursor"),
|
|
21785
|
+
path26.join(home, "Documents"),
|
|
21786
|
+
path26.join(home, "Downloads")
|
|
21362
21787
|
] : [
|
|
21363
|
-
|
|
21364
|
-
process.env["XDG_CONFIG_HOME"] ||
|
|
21365
|
-
|
|
21366
|
-
|
|
21788
|
+
path26.join(home, ".cursor"),
|
|
21789
|
+
process.env["XDG_CONFIG_HOME"] || path26.join(home, ".config"),
|
|
21790
|
+
path26.join(home, "Documents"),
|
|
21791
|
+
path26.join(home, "Downloads")
|
|
21367
21792
|
];
|
|
21368
21793
|
const timeoutPromise = new Promise(
|
|
21369
21794
|
(resolve) => setTimeout(() => {
|
|
@@ -21426,7 +21851,7 @@ var McpUsageService = class {
|
|
|
21426
21851
|
generateHostId() {
|
|
21427
21852
|
const stored = configStore.get(this.configKey);
|
|
21428
21853
|
if (stored?.mcpHostId) return stored.mcpHostId;
|
|
21429
|
-
const interfaces =
|
|
21854
|
+
const interfaces = os11.networkInterfaces();
|
|
21430
21855
|
const macs = [];
|
|
21431
21856
|
for (const iface of Object.values(interfaces)) {
|
|
21432
21857
|
if (!iface) continue;
|
|
@@ -21434,7 +21859,7 @@ var McpUsageService = class {
|
|
|
21434
21859
|
if (net.mac && net.mac !== "00:00:00:00:00:00") macs.push(net.mac);
|
|
21435
21860
|
}
|
|
21436
21861
|
}
|
|
21437
|
-
const macString = macs.length ? macs.sort().join(",") : `${
|
|
21862
|
+
const macString = macs.length ? macs.sort().join(",") : `${os11.hostname()}-${uuidv42()}`;
|
|
21438
21863
|
const hostId = uuidv5(macString, uuidv5.DNS);
|
|
21439
21864
|
logDebug("[UsageService] Generated new host ID", { hostId });
|
|
21440
21865
|
return hostId;
|
|
@@ -21457,7 +21882,7 @@ var McpUsageService = class {
|
|
|
21457
21882
|
mcpHostId,
|
|
21458
21883
|
organizationId,
|
|
21459
21884
|
mcpVersion: packageJson.version,
|
|
21460
|
-
mcpOsName:
|
|
21885
|
+
mcpOsName: os11.platform(),
|
|
21461
21886
|
mcps: JSON.stringify(mcps),
|
|
21462
21887
|
status,
|
|
21463
21888
|
userName: user.name,
|
|
@@ -23778,30 +24203,30 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
23778
24203
|
|
|
23779
24204
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
23780
24205
|
import * as fs19 from "fs";
|
|
23781
|
-
import * as
|
|
23782
|
-
import * as
|
|
24206
|
+
import * as os13 from "os";
|
|
24207
|
+
import * as path28 from "path";
|
|
23783
24208
|
|
|
23784
24209
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
23785
24210
|
init_configs();
|
|
23786
24211
|
import * as fs18 from "fs";
|
|
23787
24212
|
import fetch7 from "node-fetch";
|
|
23788
|
-
import * as
|
|
24213
|
+
import * as path27 from "path";
|
|
23789
24214
|
|
|
23790
24215
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
23791
24216
|
import * as fs17 from "fs";
|
|
23792
|
-
import * as
|
|
24217
|
+
import * as os12 from "os";
|
|
23793
24218
|
|
|
23794
24219
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
23795
24220
|
import * as fs20 from "fs";
|
|
23796
|
-
import * as
|
|
23797
|
-
import * as
|
|
24221
|
+
import * as os14 from "os";
|
|
24222
|
+
import * as path29 from "path";
|
|
23798
24223
|
|
|
23799
24224
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
23800
24225
|
import { z as z42 } from "zod";
|
|
23801
24226
|
|
|
23802
24227
|
// src/mcp/services/PathValidation.ts
|
|
23803
24228
|
import fs21 from "fs";
|
|
23804
|
-
import
|
|
24229
|
+
import path30 from "path";
|
|
23805
24230
|
async function validatePath(inputPath) {
|
|
23806
24231
|
logDebug("Validating MCP path", { inputPath });
|
|
23807
24232
|
if (/^\/[a-zA-Z]:\//.test(inputPath)) {
|
|
@@ -23833,7 +24258,7 @@ async function validatePath(inputPath) {
|
|
|
23833
24258
|
logError(error);
|
|
23834
24259
|
return { isValid: false, error, path: inputPath };
|
|
23835
24260
|
}
|
|
23836
|
-
const normalizedPath =
|
|
24261
|
+
const normalizedPath = path30.normalize(inputPath);
|
|
23837
24262
|
if (normalizedPath.includes("..")) {
|
|
23838
24263
|
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
23839
24264
|
logError(error);
|
|
@@ -24485,7 +24910,7 @@ init_configs();
|
|
|
24485
24910
|
import fs22 from "fs/promises";
|
|
24486
24911
|
import nodePath from "path";
|
|
24487
24912
|
var getLocalFiles = async ({
|
|
24488
|
-
path:
|
|
24913
|
+
path: path37,
|
|
24489
24914
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
24490
24915
|
maxFiles,
|
|
24491
24916
|
isAllFilesScan,
|
|
@@ -24493,17 +24918,17 @@ var getLocalFiles = async ({
|
|
|
24493
24918
|
scanRecentlyChangedFiles
|
|
24494
24919
|
}) => {
|
|
24495
24920
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
24496
|
-
path:
|
|
24921
|
+
path: path37,
|
|
24497
24922
|
maxFileSize,
|
|
24498
24923
|
maxFiles,
|
|
24499
24924
|
isAllFilesScan,
|
|
24500
24925
|
scanRecentlyChangedFiles
|
|
24501
24926
|
});
|
|
24502
24927
|
try {
|
|
24503
|
-
const resolvedRepoPath = await fs22.realpath(
|
|
24928
|
+
const resolvedRepoPath = await fs22.realpath(path37);
|
|
24504
24929
|
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
24505
24930
|
resolvedRepoPath,
|
|
24506
|
-
originalPath:
|
|
24931
|
+
originalPath: path37
|
|
24507
24932
|
});
|
|
24508
24933
|
const gitService = new GitService(resolvedRepoPath, log);
|
|
24509
24934
|
const gitValidation = await gitService.validateRepository();
|
|
@@ -24516,7 +24941,7 @@ var getLocalFiles = async ({
|
|
|
24516
24941
|
if (!gitValidation.isValid || isAllFilesScan) {
|
|
24517
24942
|
try {
|
|
24518
24943
|
files = await FileUtils.getLastChangedFiles({
|
|
24519
|
-
dir:
|
|
24944
|
+
dir: path37,
|
|
24520
24945
|
maxFileSize,
|
|
24521
24946
|
maxFiles,
|
|
24522
24947
|
isAllFilesScan
|
|
@@ -24608,7 +25033,7 @@ var getLocalFiles = async ({
|
|
|
24608
25033
|
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
24609
25034
|
error: error instanceof Error ? error.message : String(error),
|
|
24610
25035
|
stack: error instanceof Error ? error.stack : void 0,
|
|
24611
|
-
path:
|
|
25036
|
+
path: path37
|
|
24612
25037
|
});
|
|
24613
25038
|
throw error;
|
|
24614
25039
|
}
|
|
@@ -24618,7 +25043,7 @@ var getLocalFiles = async ({
|
|
|
24618
25043
|
init_client_generates();
|
|
24619
25044
|
init_GitService();
|
|
24620
25045
|
import fs23 from "fs";
|
|
24621
|
-
import
|
|
25046
|
+
import path31 from "path";
|
|
24622
25047
|
import { z as z41 } from "zod";
|
|
24623
25048
|
function extractPathFromPatch(patch) {
|
|
24624
25049
|
const match = patch?.match(/diff --git a\/([^\s]+) b\//);
|
|
@@ -24704,7 +25129,7 @@ var LocalMobbFolderService = class {
|
|
|
24704
25129
|
"[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
|
|
24705
25130
|
);
|
|
24706
25131
|
}
|
|
24707
|
-
const mobbFolderPath =
|
|
25132
|
+
const mobbFolderPath = path31.join(
|
|
24708
25133
|
this.repoPath,
|
|
24709
25134
|
this.defaultMobbFolderName
|
|
24710
25135
|
);
|
|
@@ -24876,7 +25301,7 @@ var LocalMobbFolderService = class {
|
|
|
24876
25301
|
mobbFolderPath,
|
|
24877
25302
|
baseFileName
|
|
24878
25303
|
);
|
|
24879
|
-
const filePath =
|
|
25304
|
+
const filePath = path31.join(mobbFolderPath, uniqueFileName);
|
|
24880
25305
|
await fs23.promises.writeFile(filePath, patch, "utf8");
|
|
24881
25306
|
logInfo("[LocalMobbFolderService] Patch saved successfully", {
|
|
24882
25307
|
filePath,
|
|
@@ -24934,11 +25359,11 @@ var LocalMobbFolderService = class {
|
|
|
24934
25359
|
* @returns Unique filename that doesn't conflict with existing files
|
|
24935
25360
|
*/
|
|
24936
25361
|
getUniqueFileName(folderPath, baseFileName) {
|
|
24937
|
-
const baseName =
|
|
24938
|
-
const extension =
|
|
25362
|
+
const baseName = path31.parse(baseFileName).name;
|
|
25363
|
+
const extension = path31.parse(baseFileName).ext;
|
|
24939
25364
|
let uniqueFileName = baseFileName;
|
|
24940
25365
|
let index = 1;
|
|
24941
|
-
while (fs23.existsSync(
|
|
25366
|
+
while (fs23.existsSync(path31.join(folderPath, uniqueFileName))) {
|
|
24942
25367
|
uniqueFileName = `${baseName}-${index}${extension}`;
|
|
24943
25368
|
index++;
|
|
24944
25369
|
if (index > 1e3) {
|
|
@@ -24969,7 +25394,7 @@ var LocalMobbFolderService = class {
|
|
|
24969
25394
|
logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
|
|
24970
25395
|
try {
|
|
24971
25396
|
const mobbFolderPath = await this.getFolder();
|
|
24972
|
-
const patchInfoPath =
|
|
25397
|
+
const patchInfoPath = path31.join(mobbFolderPath, "patchInfo.md");
|
|
24973
25398
|
const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
|
|
24974
25399
|
let existingContent = "";
|
|
24975
25400
|
if (fs23.existsSync(patchInfoPath)) {
|
|
@@ -25011,7 +25436,7 @@ var LocalMobbFolderService = class {
|
|
|
25011
25436
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
25012
25437
|
const patch = this.extractPatchFromFix(fix);
|
|
25013
25438
|
const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
|
|
25014
|
-
const patchedFilePath = relativePatchedFilePath ?
|
|
25439
|
+
const patchedFilePath = relativePatchedFilePath ? path31.resolve(this.repoPath, relativePatchedFilePath) : null;
|
|
25015
25440
|
const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
|
|
25016
25441
|
let markdown = `# Fix ${fixIdentifier}
|
|
25017
25442
|
|
|
@@ -25355,14 +25780,14 @@ import {
|
|
|
25355
25780
|
} from "fs";
|
|
25356
25781
|
import fs24 from "fs/promises";
|
|
25357
25782
|
import parseDiff2 from "parse-diff";
|
|
25358
|
-
import
|
|
25783
|
+
import path32 from "path";
|
|
25359
25784
|
var PatchApplicationService = class {
|
|
25360
25785
|
/**
|
|
25361
25786
|
* Gets the appropriate comment syntax for a file based on its extension
|
|
25362
25787
|
*/
|
|
25363
25788
|
static getCommentSyntax(filePath) {
|
|
25364
|
-
const ext =
|
|
25365
|
-
const basename2 =
|
|
25789
|
+
const ext = path32.extname(filePath).toLowerCase();
|
|
25790
|
+
const basename2 = path32.basename(filePath);
|
|
25366
25791
|
const commentMap = {
|
|
25367
25792
|
// C-style languages (single line comments)
|
|
25368
25793
|
".js": "//",
|
|
@@ -25570,7 +25995,7 @@ var PatchApplicationService = class {
|
|
|
25570
25995
|
}
|
|
25571
25996
|
);
|
|
25572
25997
|
}
|
|
25573
|
-
const dirPath =
|
|
25998
|
+
const dirPath = path32.dirname(normalizedFilePath);
|
|
25574
25999
|
mkdirSync(dirPath, { recursive: true });
|
|
25575
26000
|
writeFileSync3(normalizedFilePath, finalContent, "utf8");
|
|
25576
26001
|
return normalizedFilePath;
|
|
@@ -25579,9 +26004,9 @@ var PatchApplicationService = class {
|
|
|
25579
26004
|
repositoryPath,
|
|
25580
26005
|
targetPath
|
|
25581
26006
|
}) {
|
|
25582
|
-
const repoRoot =
|
|
25583
|
-
const normalizedPath =
|
|
25584
|
-
const repoRootWithSep = repoRoot.endsWith(
|
|
26007
|
+
const repoRoot = path32.resolve(repositoryPath);
|
|
26008
|
+
const normalizedPath = path32.resolve(repoRoot, targetPath);
|
|
26009
|
+
const repoRootWithSep = repoRoot.endsWith(path32.sep) ? repoRoot : `${repoRoot}${path32.sep}`;
|
|
25585
26010
|
if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
|
|
25586
26011
|
throw new Error(
|
|
25587
26012
|
`Security violation: target path ${targetPath} resolves outside repository`
|
|
@@ -25590,7 +26015,7 @@ var PatchApplicationService = class {
|
|
|
25590
26015
|
return {
|
|
25591
26016
|
repoRoot,
|
|
25592
26017
|
normalizedPath,
|
|
25593
|
-
relativePath:
|
|
26018
|
+
relativePath: path32.relative(repoRoot, normalizedPath)
|
|
25594
26019
|
};
|
|
25595
26020
|
}
|
|
25596
26021
|
/**
|
|
@@ -25872,7 +26297,7 @@ var PatchApplicationService = class {
|
|
|
25872
26297
|
continue;
|
|
25873
26298
|
}
|
|
25874
26299
|
try {
|
|
25875
|
-
const absolutePath =
|
|
26300
|
+
const absolutePath = path32.resolve(repositoryPath, targetFile);
|
|
25876
26301
|
if (existsSync6(absolutePath)) {
|
|
25877
26302
|
const stats = await fs24.stat(absolutePath);
|
|
25878
26303
|
const fileModTime = stats.mtime.getTime();
|
|
@@ -26098,7 +26523,7 @@ var PatchApplicationService = class {
|
|
|
26098
26523
|
fix,
|
|
26099
26524
|
scanContext
|
|
26100
26525
|
});
|
|
26101
|
-
appliedFiles.push(
|
|
26526
|
+
appliedFiles.push(path32.relative(repositoryPath, actualPath));
|
|
26102
26527
|
logDebug(`[${scanContext}] Created new file: ${relativePath}`);
|
|
26103
26528
|
}
|
|
26104
26529
|
/**
|
|
@@ -26147,7 +26572,7 @@ var PatchApplicationService = class {
|
|
|
26147
26572
|
fix,
|
|
26148
26573
|
scanContext
|
|
26149
26574
|
});
|
|
26150
|
-
appliedFiles.push(
|
|
26575
|
+
appliedFiles.push(path32.relative(repositoryPath, actualPath));
|
|
26151
26576
|
logDebug(`[${scanContext}] Modified file: ${relativePath}`);
|
|
26152
26577
|
}
|
|
26153
26578
|
}
|
|
@@ -26344,7 +26769,7 @@ init_configs();
|
|
|
26344
26769
|
// src/mcp/services/FileOperations.ts
|
|
26345
26770
|
init_FileUtils();
|
|
26346
26771
|
import fs25 from "fs";
|
|
26347
|
-
import
|
|
26772
|
+
import path33 from "path";
|
|
26348
26773
|
import AdmZip5 from "adm-zip";
|
|
26349
26774
|
var FileOperations = class {
|
|
26350
26775
|
/**
|
|
@@ -26364,10 +26789,10 @@ var FileOperations = class {
|
|
|
26364
26789
|
let packedFilesCount = 0;
|
|
26365
26790
|
const packedFiles = [];
|
|
26366
26791
|
const excludedFiles = [];
|
|
26367
|
-
const resolvedRepoPath =
|
|
26792
|
+
const resolvedRepoPath = path33.resolve(repositoryPath);
|
|
26368
26793
|
for (const filepath of fileList) {
|
|
26369
|
-
const absoluteFilepath =
|
|
26370
|
-
const resolvedFilePath =
|
|
26794
|
+
const absoluteFilepath = path33.join(repositoryPath, filepath);
|
|
26795
|
+
const resolvedFilePath = path33.resolve(absoluteFilepath);
|
|
26371
26796
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26372
26797
|
const reason = "potential path traversal security risk";
|
|
26373
26798
|
logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
|
|
@@ -26414,11 +26839,11 @@ var FileOperations = class {
|
|
|
26414
26839
|
fileList,
|
|
26415
26840
|
repositoryPath
|
|
26416
26841
|
}) {
|
|
26417
|
-
const resolvedRepoPath =
|
|
26842
|
+
const resolvedRepoPath = path33.resolve(repositoryPath);
|
|
26418
26843
|
const validatedPaths = [];
|
|
26419
26844
|
for (const filepath of fileList) {
|
|
26420
|
-
const absoluteFilepath =
|
|
26421
|
-
const resolvedFilePath =
|
|
26845
|
+
const absoluteFilepath = path33.join(repositoryPath, filepath);
|
|
26846
|
+
const resolvedFilePath = path33.resolve(absoluteFilepath);
|
|
26422
26847
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26423
26848
|
logDebug(
|
|
26424
26849
|
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
@@ -26446,7 +26871,7 @@ var FileOperations = class {
|
|
|
26446
26871
|
for (const absolutePath of filePaths) {
|
|
26447
26872
|
try {
|
|
26448
26873
|
const content = await fs25.promises.readFile(absolutePath);
|
|
26449
|
-
const relativePath =
|
|
26874
|
+
const relativePath = path33.basename(absolutePath);
|
|
26450
26875
|
fileDataArray.push({
|
|
26451
26876
|
relativePath,
|
|
26452
26877
|
absolutePath,
|
|
@@ -26758,14 +27183,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26758
27183
|
* since the last scan.
|
|
26759
27184
|
*/
|
|
26760
27185
|
async scanForSecurityVulnerabilities({
|
|
26761
|
-
path:
|
|
27186
|
+
path: path37,
|
|
26762
27187
|
isAllDetectionRulesScan,
|
|
26763
27188
|
isAllFilesScan,
|
|
26764
27189
|
scanContext
|
|
26765
27190
|
}) {
|
|
26766
27191
|
this.hasAuthenticationFailed = false;
|
|
26767
27192
|
logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
|
|
26768
|
-
path:
|
|
27193
|
+
path: path37
|
|
26769
27194
|
});
|
|
26770
27195
|
if (!this.gqlClient) {
|
|
26771
27196
|
logInfo(`[${scanContext}] No GQL client found, skipping scan`);
|
|
@@ -26781,11 +27206,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26781
27206
|
}
|
|
26782
27207
|
logDebug(
|
|
26783
27208
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
26784
|
-
{ path:
|
|
27209
|
+
{ path: path37 }
|
|
26785
27210
|
);
|
|
26786
27211
|
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
26787
27212
|
const files = await getLocalFiles({
|
|
26788
|
-
path:
|
|
27213
|
+
path: path37,
|
|
26789
27214
|
isAllFilesScan,
|
|
26790
27215
|
scanContext,
|
|
26791
27216
|
scanRecentlyChangedFiles: !isBackgroundScan
|
|
@@ -26811,13 +27236,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26811
27236
|
});
|
|
26812
27237
|
const { fixReportId, projectId } = await scanFiles({
|
|
26813
27238
|
fileList: filesToScan.map((file) => file.relativePath),
|
|
26814
|
-
repositoryPath:
|
|
27239
|
+
repositoryPath: path37,
|
|
26815
27240
|
gqlClient: this.gqlClient,
|
|
26816
27241
|
isAllDetectionRulesScan,
|
|
26817
27242
|
scanContext
|
|
26818
27243
|
});
|
|
26819
27244
|
logInfo(
|
|
26820
|
-
`[${scanContext}] Security scan completed for ${
|
|
27245
|
+
`[${scanContext}] Security scan completed for ${path37} reportId: ${fixReportId} projectId: ${projectId}`
|
|
26821
27246
|
);
|
|
26822
27247
|
if (isAllFilesScan) {
|
|
26823
27248
|
return;
|
|
@@ -27111,13 +27536,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27111
27536
|
});
|
|
27112
27537
|
return scannedFiles.some((file) => file.relativePath === fixFile);
|
|
27113
27538
|
}
|
|
27114
|
-
async getFreshFixes({ path:
|
|
27539
|
+
async getFreshFixes({ path: path37 }) {
|
|
27115
27540
|
const scanContext = ScanContext.USER_REQUEST;
|
|
27116
|
-
logDebug(`[${scanContext}] Getting fresh fixes`, { path:
|
|
27117
|
-
if (this.path !==
|
|
27118
|
-
this.path =
|
|
27541
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path37 });
|
|
27542
|
+
if (this.path !== path37) {
|
|
27543
|
+
this.path = path37;
|
|
27119
27544
|
this.reset();
|
|
27120
|
-
logInfo(`[${scanContext}] Reset service state for new path`, { path:
|
|
27545
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path37 });
|
|
27121
27546
|
}
|
|
27122
27547
|
try {
|
|
27123
27548
|
const loginContext = createMcpLoginContext("check_new_fixes");
|
|
@@ -27136,7 +27561,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27136
27561
|
}
|
|
27137
27562
|
throw error;
|
|
27138
27563
|
}
|
|
27139
|
-
this.triggerScan({ path:
|
|
27564
|
+
this.triggerScan({ path: path37, gqlClient: this.gqlClient });
|
|
27140
27565
|
let isMvsAutoFixEnabled = null;
|
|
27141
27566
|
try {
|
|
27142
27567
|
isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
|
|
@@ -27170,33 +27595,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27170
27595
|
return noFreshFixesPrompt;
|
|
27171
27596
|
}
|
|
27172
27597
|
triggerScan({
|
|
27173
|
-
path:
|
|
27598
|
+
path: path37,
|
|
27174
27599
|
gqlClient
|
|
27175
27600
|
}) {
|
|
27176
|
-
if (this.path !==
|
|
27177
|
-
this.path =
|
|
27601
|
+
if (this.path !== path37) {
|
|
27602
|
+
this.path = path37;
|
|
27178
27603
|
this.reset();
|
|
27179
|
-
logInfo(`Reset service state for new path in triggerScan`, { path:
|
|
27604
|
+
logInfo(`Reset service state for new path in triggerScan`, { path: path37 });
|
|
27180
27605
|
}
|
|
27181
27606
|
this.gqlClient = gqlClient;
|
|
27182
27607
|
if (!this.intervalId) {
|
|
27183
|
-
this.startPeriodicScanning(
|
|
27184
|
-
this.executeInitialScan(
|
|
27185
|
-
void this.executeInitialFullScan(
|
|
27608
|
+
this.startPeriodicScanning(path37);
|
|
27609
|
+
this.executeInitialScan(path37);
|
|
27610
|
+
void this.executeInitialFullScan(path37);
|
|
27186
27611
|
}
|
|
27187
27612
|
}
|
|
27188
|
-
startPeriodicScanning(
|
|
27613
|
+
startPeriodicScanning(path37) {
|
|
27189
27614
|
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
27190
27615
|
logDebug(
|
|
27191
27616
|
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
27192
27617
|
{
|
|
27193
|
-
path:
|
|
27618
|
+
path: path37
|
|
27194
27619
|
}
|
|
27195
27620
|
);
|
|
27196
27621
|
this.intervalId = setInterval(() => {
|
|
27197
|
-
logDebug(`[${scanContext}] Triggering periodic security scan`, { path:
|
|
27622
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path37 });
|
|
27198
27623
|
this.scanForSecurityVulnerabilities({
|
|
27199
|
-
path:
|
|
27624
|
+
path: path37,
|
|
27200
27625
|
scanContext
|
|
27201
27626
|
}).catch((error) => {
|
|
27202
27627
|
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
@@ -27205,45 +27630,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27205
27630
|
});
|
|
27206
27631
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
27207
27632
|
}
|
|
27208
|
-
async executeInitialFullScan(
|
|
27633
|
+
async executeInitialFullScan(path37) {
|
|
27209
27634
|
const scanContext = ScanContext.FULL_SCAN;
|
|
27210
|
-
logDebug(`[${scanContext}] Triggering initial full security scan`, { path:
|
|
27635
|
+
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path37 });
|
|
27211
27636
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
27212
27637
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
27213
27638
|
});
|
|
27214
|
-
if (this.fullScanPathsScanned.includes(
|
|
27639
|
+
if (this.fullScanPathsScanned.includes(path37)) {
|
|
27215
27640
|
logDebug(`[${scanContext}] Full scan already executed for this path`, {
|
|
27216
|
-
path:
|
|
27641
|
+
path: path37
|
|
27217
27642
|
});
|
|
27218
27643
|
return;
|
|
27219
27644
|
}
|
|
27220
27645
|
configStore.set("fullScanPathsScanned", [
|
|
27221
27646
|
...this.fullScanPathsScanned,
|
|
27222
|
-
|
|
27647
|
+
path37
|
|
27223
27648
|
]);
|
|
27224
27649
|
try {
|
|
27225
27650
|
await this.scanForSecurityVulnerabilities({
|
|
27226
|
-
path:
|
|
27651
|
+
path: path37,
|
|
27227
27652
|
isAllFilesScan: true,
|
|
27228
27653
|
isAllDetectionRulesScan: true,
|
|
27229
27654
|
scanContext: ScanContext.FULL_SCAN
|
|
27230
27655
|
});
|
|
27231
|
-
if (!this.fullScanPathsScanned.includes(
|
|
27232
|
-
this.fullScanPathsScanned.push(
|
|
27656
|
+
if (!this.fullScanPathsScanned.includes(path37)) {
|
|
27657
|
+
this.fullScanPathsScanned.push(path37);
|
|
27233
27658
|
configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
|
|
27234
27659
|
}
|
|
27235
|
-
logInfo(`[${scanContext}] Full scan completed`, { path:
|
|
27660
|
+
logInfo(`[${scanContext}] Full scan completed`, { path: path37 });
|
|
27236
27661
|
} catch (error) {
|
|
27237
27662
|
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
27238
27663
|
error
|
|
27239
27664
|
});
|
|
27240
27665
|
}
|
|
27241
27666
|
}
|
|
27242
|
-
executeInitialScan(
|
|
27667
|
+
executeInitialScan(path37) {
|
|
27243
27668
|
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
27244
|
-
logDebug(`[${scanContext}] Triggering initial security scan`, { path:
|
|
27669
|
+
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path37 });
|
|
27245
27670
|
this.scanForSecurityVulnerabilities({
|
|
27246
|
-
path:
|
|
27671
|
+
path: path37,
|
|
27247
27672
|
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
27248
27673
|
}).catch((error) => {
|
|
27249
27674
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
@@ -27340,9 +27765,9 @@ Example payload:
|
|
|
27340
27765
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27341
27766
|
);
|
|
27342
27767
|
}
|
|
27343
|
-
const
|
|
27768
|
+
const path37 = pathValidationResult.path;
|
|
27344
27769
|
const resultText = await this.newFixesService.getFreshFixes({
|
|
27345
|
-
path:
|
|
27770
|
+
path: path37
|
|
27346
27771
|
});
|
|
27347
27772
|
logInfo("CheckForNewAvailableFixesTool execution completed", {
|
|
27348
27773
|
resultText
|
|
@@ -27520,8 +27945,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
27520
27945
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27521
27946
|
);
|
|
27522
27947
|
}
|
|
27523
|
-
const
|
|
27524
|
-
const gitService = new GitService(
|
|
27948
|
+
const path37 = pathValidationResult.path;
|
|
27949
|
+
const gitService = new GitService(path37, log);
|
|
27525
27950
|
const gitValidation = await gitService.validateRepository();
|
|
27526
27951
|
if (!gitValidation.isValid) {
|
|
27527
27952
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -27906,9 +28331,9 @@ Example payload:
|
|
|
27906
28331
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27907
28332
|
);
|
|
27908
28333
|
}
|
|
27909
|
-
const
|
|
28334
|
+
const path37 = pathValidationResult.path;
|
|
27910
28335
|
const files = await getLocalFiles({
|
|
27911
|
-
path:
|
|
28336
|
+
path: path37,
|
|
27912
28337
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
27913
28338
|
maxFiles: args.maxFiles,
|
|
27914
28339
|
scanContext: ScanContext.USER_REQUEST,
|
|
@@ -27928,7 +28353,7 @@ Example payload:
|
|
|
27928
28353
|
try {
|
|
27929
28354
|
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
27930
28355
|
fileList: files.map((file) => file.relativePath),
|
|
27931
|
-
repositoryPath:
|
|
28356
|
+
repositoryPath: path37,
|
|
27932
28357
|
offset: args.offset,
|
|
27933
28358
|
limit: args.limit,
|
|
27934
28359
|
isRescan: args.rescan || !!args.maxFiles
|
|
@@ -28229,10 +28654,10 @@ init_client_generates();
|
|
|
28229
28654
|
init_urlParser2();
|
|
28230
28655
|
|
|
28231
28656
|
// src/features/codeium_intellij/codeium_language_server_grpc_client.ts
|
|
28232
|
-
import
|
|
28657
|
+
import path34 from "path";
|
|
28233
28658
|
import * as grpc from "@grpc/grpc-js";
|
|
28234
28659
|
import * as protoLoader from "@grpc/proto-loader";
|
|
28235
|
-
var PROTO_PATH =
|
|
28660
|
+
var PROTO_PATH = path34.join(
|
|
28236
28661
|
getModuleRootDir(),
|
|
28237
28662
|
"src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
|
|
28238
28663
|
);
|
|
@@ -28244,7 +28669,7 @@ function loadProto() {
|
|
|
28244
28669
|
defaults: true,
|
|
28245
28670
|
oneofs: true,
|
|
28246
28671
|
includeDirs: [
|
|
28247
|
-
|
|
28672
|
+
path34.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
|
|
28248
28673
|
]
|
|
28249
28674
|
});
|
|
28250
28675
|
return grpc.loadPackageDefinition(
|
|
@@ -28299,29 +28724,29 @@ async function getGrpcClient(port, csrf3) {
|
|
|
28299
28724
|
|
|
28300
28725
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
28301
28726
|
import fs27 from "fs";
|
|
28302
|
-
import
|
|
28303
|
-
import
|
|
28727
|
+
import os15 from "os";
|
|
28728
|
+
import path35 from "path";
|
|
28304
28729
|
function getLogsDir() {
|
|
28305
28730
|
if (process.platform === "darwin") {
|
|
28306
|
-
return
|
|
28731
|
+
return path35.join(os15.homedir(), "Library/Logs/JetBrains");
|
|
28307
28732
|
} else if (process.platform === "win32") {
|
|
28308
|
-
return
|
|
28309
|
-
process.env["LOCALAPPDATA"] ||
|
|
28733
|
+
return path35.join(
|
|
28734
|
+
process.env["LOCALAPPDATA"] || path35.join(os15.homedir(), "AppData/Local"),
|
|
28310
28735
|
"JetBrains"
|
|
28311
28736
|
);
|
|
28312
28737
|
} else {
|
|
28313
|
-
return
|
|
28738
|
+
return path35.join(os15.homedir(), ".cache/JetBrains");
|
|
28314
28739
|
}
|
|
28315
28740
|
}
|
|
28316
28741
|
function parseIdeLogDir(ideLogDir) {
|
|
28317
28742
|
const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
|
|
28318
28743
|
name: f,
|
|
28319
|
-
mtime: fs27.statSync(
|
|
28744
|
+
mtime: fs27.statSync(path35.join(ideLogDir, f)).mtimeMs
|
|
28320
28745
|
})).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
|
|
28321
28746
|
let latestCsrf = null;
|
|
28322
28747
|
let latestPort = null;
|
|
28323
28748
|
for (const logFile of logFiles) {
|
|
28324
|
-
const lines = fs27.readFileSync(
|
|
28749
|
+
const lines = fs27.readFileSync(path35.join(ideLogDir, logFile), "utf-8").split("\n");
|
|
28325
28750
|
for (const line of lines) {
|
|
28326
28751
|
if (!line.includes(
|
|
28327
28752
|
"com.codeium.intellij.language_server.LanguageServerProcessHandler"
|
|
@@ -28349,9 +28774,9 @@ function findRunningCodeiumLanguageServers() {
|
|
|
28349
28774
|
const logsDir = getLogsDir();
|
|
28350
28775
|
if (!fs27.existsSync(logsDir)) return results;
|
|
28351
28776
|
for (const ide of fs27.readdirSync(logsDir)) {
|
|
28352
|
-
let ideLogDir =
|
|
28777
|
+
let ideLogDir = path35.join(logsDir, ide);
|
|
28353
28778
|
if (process.platform !== "darwin") {
|
|
28354
|
-
ideLogDir =
|
|
28779
|
+
ideLogDir = path35.join(ideLogDir, "log");
|
|
28355
28780
|
}
|
|
28356
28781
|
if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
|
|
28357
28782
|
continue;
|
|
@@ -28533,11 +28958,11 @@ function processChatStepCodeAction(step) {
|
|
|
28533
28958
|
|
|
28534
28959
|
// src/features/codeium_intellij/install_hook.ts
|
|
28535
28960
|
import fsPromises5 from "fs/promises";
|
|
28536
|
-
import
|
|
28537
|
-
import
|
|
28961
|
+
import os16 from "os";
|
|
28962
|
+
import path36 from "path";
|
|
28538
28963
|
import chalk14 from "chalk";
|
|
28539
28964
|
function getCodeiumHooksPath() {
|
|
28540
|
-
return
|
|
28965
|
+
return path36.join(os16.homedir(), ".codeium", "hooks.json");
|
|
28541
28966
|
}
|
|
28542
28967
|
async function readCodeiumHooks() {
|
|
28543
28968
|
const hooksPath = getCodeiumHooksPath();
|
|
@@ -28550,7 +28975,7 @@ async function readCodeiumHooks() {
|
|
|
28550
28975
|
}
|
|
28551
28976
|
async function writeCodeiumHooks(config2) {
|
|
28552
28977
|
const hooksPath = getCodeiumHooksPath();
|
|
28553
|
-
const dir =
|
|
28978
|
+
const dir = path36.dirname(hooksPath);
|
|
28554
28979
|
await fsPromises5.mkdir(dir, { recursive: true });
|
|
28555
28980
|
await fsPromises5.writeFile(
|
|
28556
28981
|
hooksPath,
|