mobbdev 1.2.24 → 1.2.28
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 +17 -5
- package/dist/index.mjs +181 -91
- package/package.json +1 -1
|
@@ -237,6 +237,7 @@ var init_client_generates = __esm({
|
|
|
237
237
|
IssueType_Enum2["DuplicatedStrings"] = "DUPLICATED_STRINGS";
|
|
238
238
|
IssueType_Enum2["ErroneousStringCompare"] = "ERRONEOUS_STRING_COMPARE";
|
|
239
239
|
IssueType_Enum2["ErrorCondtionWithoutAction"] = "ERROR_CONDTION_WITHOUT_ACTION";
|
|
240
|
+
IssueType_Enum2["ExcessiveSecretsExposure"] = "EXCESSIVE_SECRETS_EXPOSURE";
|
|
240
241
|
IssueType_Enum2["FrameableLoginPage"] = "FRAMEABLE_LOGIN_PAGE";
|
|
241
242
|
IssueType_Enum2["FunctionCallWithoutParentheses"] = "FUNCTION_CALL_WITHOUT_PARENTHESES";
|
|
242
243
|
IssueType_Enum2["GhActionsShellInjection"] = "GH_ACTIONS_SHELL_INJECTION";
|
|
@@ -1712,7 +1713,8 @@ var init_getIssueType = __esm({
|
|
|
1712
1713
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: "Action Not Pinned to Commit Sha",
|
|
1713
1714
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: "Django Blank Field Needs Null or Default",
|
|
1714
1715
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check",
|
|
1715
|
-
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions"
|
|
1716
|
+
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions",
|
|
1717
|
+
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure"
|
|
1716
1718
|
};
|
|
1717
1719
|
issueTypeZ = z5.nativeEnum(IssueType_Enum);
|
|
1718
1720
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -4556,7 +4558,8 @@ var fixDetailsData = {
|
|
|
4556
4558
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: void 0,
|
|
4557
4559
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: void 0,
|
|
4558
4560
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0,
|
|
4559
|
-
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0
|
|
4561
|
+
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0,
|
|
4562
|
+
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0
|
|
4560
4563
|
};
|
|
4561
4564
|
|
|
4562
4565
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -7344,8 +7347,8 @@ var openRedaction = new OpenRedaction({
|
|
|
7344
7347
|
"MONERO_ADDRESS",
|
|
7345
7348
|
"RIPPLE_ADDRESS",
|
|
7346
7349
|
// Medical Data (removed PRESCRIPTION_NUMBER - too broad, matches words containing "ription")
|
|
7350
|
+
// Removed MEDICAL_RECORD_NUMBER - too broad, "MR" prefix matches "Merge Request" in SCM contexts (e.g. "MR branches" → "MR br****es")
|
|
7347
7351
|
"NHS_NUMBER",
|
|
7348
|
-
"MEDICAL_RECORD_NUMBER",
|
|
7349
7352
|
"AUSTRALIAN_MEDICARE",
|
|
7350
7353
|
"HEALTH_PLAN_NUMBER",
|
|
7351
7354
|
"PATIENT_ID",
|
|
@@ -7389,10 +7392,10 @@ var openRedaction = new OpenRedaction({
|
|
|
7389
7392
|
"DOCKER_AUTH",
|
|
7390
7393
|
"KUBERNETES_SECRET",
|
|
7391
7394
|
// Government & Legal
|
|
7395
|
+
// Removed CLIENT_ID - too broad, "client" is ubiquitous in code (npm packages like @scope/client-*, class names like ClientSdkOptions)
|
|
7392
7396
|
"POLICE_REPORT_NUMBER",
|
|
7393
7397
|
"IMMIGRATION_NUMBER",
|
|
7394
|
-
"COURT_REPORTER_LICENSE"
|
|
7395
|
-
"CLIENT_ID"
|
|
7398
|
+
"COURT_REPORTER_LICENSE"
|
|
7396
7399
|
]
|
|
7397
7400
|
});
|
|
7398
7401
|
function maskString(str, showStart = 2, showEnd = 2) {
|
|
@@ -7415,6 +7418,15 @@ async function sanitizeDataWithCounts(obj) {
|
|
|
7415
7418
|
...piiDetections.low
|
|
7416
7419
|
];
|
|
7417
7420
|
for (const detection of allDetections) {
|
|
7421
|
+
if (detection.type === "CREDIT_CARD") {
|
|
7422
|
+
const start = detection.position[0];
|
|
7423
|
+
const end = detection.position[1];
|
|
7424
|
+
const charBefore = (start > 0 ? str[start - 1] : "") ?? "";
|
|
7425
|
+
const charAfter = str[end] ?? "";
|
|
7426
|
+
if (charBefore === "." || charBefore >= "0" && charBefore <= "9" || charAfter >= "0" && charAfter <= "9") {
|
|
7427
|
+
continue;
|
|
7428
|
+
}
|
|
7429
|
+
}
|
|
7418
7430
|
counts.detections.total++;
|
|
7419
7431
|
if (detection.severity === "high") counts.detections.high++;
|
|
7420
7432
|
else if (detection.severity === "medium") counts.detections.medium++;
|
package/dist/index.mjs
CHANGED
|
@@ -237,6 +237,7 @@ var init_client_generates = __esm({
|
|
|
237
237
|
IssueType_Enum2["DuplicatedStrings"] = "DUPLICATED_STRINGS";
|
|
238
238
|
IssueType_Enum2["ErroneousStringCompare"] = "ERRONEOUS_STRING_COMPARE";
|
|
239
239
|
IssueType_Enum2["ErrorCondtionWithoutAction"] = "ERROR_CONDTION_WITHOUT_ACTION";
|
|
240
|
+
IssueType_Enum2["ExcessiveSecretsExposure"] = "EXCESSIVE_SECRETS_EXPOSURE";
|
|
240
241
|
IssueType_Enum2["FrameableLoginPage"] = "FRAMEABLE_LOGIN_PAGE";
|
|
241
242
|
IssueType_Enum2["FunctionCallWithoutParentheses"] = "FUNCTION_CALL_WITHOUT_PARENTHESES";
|
|
242
243
|
IssueType_Enum2["GhActionsShellInjection"] = "GH_ACTIONS_SHELL_INJECTION";
|
|
@@ -1412,7 +1413,8 @@ var init_getIssueType = __esm({
|
|
|
1412
1413
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: "Action Not Pinned to Commit Sha",
|
|
1413
1414
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: "Django Blank Field Needs Null or Default",
|
|
1414
1415
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check",
|
|
1415
|
-
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions"
|
|
1416
|
+
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions",
|
|
1417
|
+
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure"
|
|
1416
1418
|
};
|
|
1417
1419
|
issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
1418
1420
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -4284,7 +4286,8 @@ var fixDetailsData = {
|
|
|
4284
4286
|
["ACTION_NOT_PINNED_TO_COMMIT_SHA" /* ActionNotPinnedToCommitSha */]: void 0,
|
|
4285
4287
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: void 0,
|
|
4286
4288
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0,
|
|
4287
|
-
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0
|
|
4289
|
+
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0,
|
|
4290
|
+
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0
|
|
4288
4291
|
};
|
|
4289
4292
|
|
|
4290
4293
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -6241,6 +6244,23 @@ function parseLinearTicket(url, name) {
|
|
|
6241
6244
|
const title = titleSlug.replace(/-/g, " ");
|
|
6242
6245
|
return { name, title, url };
|
|
6243
6246
|
}
|
|
6247
|
+
function isLinearBotComment(comment) {
|
|
6248
|
+
if (!comment.author) return false;
|
|
6249
|
+
const login = comment.author.login.toLowerCase();
|
|
6250
|
+
if (login === "linear[bot]" || login === "linear") return true;
|
|
6251
|
+
if (comment.author.type === "Bot" && login.includes("linear")) return true;
|
|
6252
|
+
return false;
|
|
6253
|
+
}
|
|
6254
|
+
function extractLinearTicketsFromComments(comments) {
|
|
6255
|
+
const tickets = [];
|
|
6256
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6257
|
+
for (const comment of comments) {
|
|
6258
|
+
if (isLinearBotComment(comment)) {
|
|
6259
|
+
tickets.push(...extractLinearTicketsFromBody(comment.body || "", seen));
|
|
6260
|
+
}
|
|
6261
|
+
}
|
|
6262
|
+
return tickets;
|
|
6263
|
+
}
|
|
6244
6264
|
var userNamePattern = /^(https?:\/\/)([^@]+@)?([^/]+\/.+)$/;
|
|
6245
6265
|
var sshPattern = /^git@([\w.-]+):([\w./-]+)$/;
|
|
6246
6266
|
function normalizeUrl(repoUrl) {
|
|
@@ -7124,11 +7144,11 @@ var SCMLib = class {
|
|
|
7124
7144
|
}
|
|
7125
7145
|
/**
|
|
7126
7146
|
* Extract Linear ticket links from PR/MR comments.
|
|
7127
|
-
*
|
|
7147
|
+
* Uses shared isLinearBotComment() for unified bot detection across all providers.
|
|
7128
7148
|
* Public so it can be reused by backend services.
|
|
7129
7149
|
*/
|
|
7130
|
-
extractLinearTicketsFromComments(
|
|
7131
|
-
return
|
|
7150
|
+
extractLinearTicketsFromComments(comments) {
|
|
7151
|
+
return extractLinearTicketsFromComments(comments);
|
|
7132
7152
|
}
|
|
7133
7153
|
_validateAccessTokenAndUrl() {
|
|
7134
7154
|
this._validateAccessToken();
|
|
@@ -8829,7 +8849,7 @@ function determinePrStatus(state, isDraft) {
|
|
|
8829
8849
|
return isDraft ? "DRAFT" /* Draft */ : "ACTIVE" /* Active */;
|
|
8830
8850
|
}
|
|
8831
8851
|
}
|
|
8832
|
-
var GithubSCMLib = class
|
|
8852
|
+
var GithubSCMLib = class extends SCMLib {
|
|
8833
8853
|
// we don't always need a url, what's important is that we have an access token
|
|
8834
8854
|
constructor(url, accessToken, scmOrg) {
|
|
8835
8855
|
super(url, accessToken, scmOrg);
|
|
@@ -9301,27 +9321,6 @@ var GithubSCMLib = class _GithubSCMLib extends SCMLib {
|
|
|
9301
9321
|
commentIds
|
|
9302
9322
|
};
|
|
9303
9323
|
}
|
|
9304
|
-
/**
|
|
9305
|
-
* Extract Linear ticket links from pre-fetched comments (pure function, no API calls)
|
|
9306
|
-
* Instance method that overrides base class - can also be called statically for backwards compatibility.
|
|
9307
|
-
*/
|
|
9308
|
-
extractLinearTicketsFromComments(comments) {
|
|
9309
|
-
return _GithubSCMLib._extractLinearTicketsFromCommentsImpl(comments);
|
|
9310
|
-
}
|
|
9311
|
-
/**
|
|
9312
|
-
* Static implementation for backwards compatibility and reuse.
|
|
9313
|
-
* Called by both the instance method and direct static calls.
|
|
9314
|
-
*/
|
|
9315
|
-
static _extractLinearTicketsFromCommentsImpl(comments) {
|
|
9316
|
-
const tickets = [];
|
|
9317
|
-
const seen = /* @__PURE__ */ new Set();
|
|
9318
|
-
for (const comment of comments) {
|
|
9319
|
-
if (comment.author?.login === "linear[bot]" || comment.author?.type === "Bot") {
|
|
9320
|
-
tickets.push(...extractLinearTicketsFromBody(comment.body || "", seen));
|
|
9321
|
-
}
|
|
9322
|
-
}
|
|
9323
|
-
return tickets;
|
|
9324
|
-
}
|
|
9325
9324
|
};
|
|
9326
9325
|
|
|
9327
9326
|
// src/features/analysis/scm/gitlab/gitlab.ts
|
|
@@ -9565,34 +9564,74 @@ async function getGitlabIsRemoteBranch({
|
|
|
9565
9564
|
return false;
|
|
9566
9565
|
}
|
|
9567
9566
|
}
|
|
9568
|
-
async function
|
|
9567
|
+
async function searchGitlabProjects({
|
|
9568
|
+
url,
|
|
9569
|
+
accessToken,
|
|
9570
|
+
perPage = 20,
|
|
9571
|
+
page = 1
|
|
9572
|
+
}) {
|
|
9573
|
+
if (perPage > GITLAB_MAX_PER_PAGE) {
|
|
9574
|
+
throw new Error(
|
|
9575
|
+
`perPage ${perPage} exceeds GitLab maximum of ${GITLAB_MAX_PER_PAGE}`
|
|
9576
|
+
);
|
|
9577
|
+
}
|
|
9569
9578
|
const api2 = getGitBeaker({ url, gitlabAuthToken: accessToken });
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
})
|
|
9595
|
-
|
|
9579
|
+
let response;
|
|
9580
|
+
try {
|
|
9581
|
+
response = await api2.Projects.all({
|
|
9582
|
+
membership: true,
|
|
9583
|
+
orderBy: "last_activity_at",
|
|
9584
|
+
sort: "desc",
|
|
9585
|
+
pagination: "offset",
|
|
9586
|
+
perPage,
|
|
9587
|
+
page,
|
|
9588
|
+
showExpanded: true
|
|
9589
|
+
});
|
|
9590
|
+
} catch (e) {
|
|
9591
|
+
debug4(
|
|
9592
|
+
"[searchGitlabProjects] order_by=last_activity_at failed, falling back to created_at: %s",
|
|
9593
|
+
e instanceof Error ? e.message : String(e)
|
|
9594
|
+
);
|
|
9595
|
+
response = await api2.Projects.all({
|
|
9596
|
+
membership: true,
|
|
9597
|
+
orderBy: "created_at",
|
|
9598
|
+
sort: "desc",
|
|
9599
|
+
pagination: "offset",
|
|
9600
|
+
perPage,
|
|
9601
|
+
page,
|
|
9602
|
+
showExpanded: true
|
|
9603
|
+
});
|
|
9604
|
+
}
|
|
9605
|
+
const projects = response.data.map((p) => ({
|
|
9606
|
+
id: p.id,
|
|
9607
|
+
path: p.path,
|
|
9608
|
+
web_url: p.web_url,
|
|
9609
|
+
namespace_name: p.namespace?.name ?? "",
|
|
9610
|
+
visibility: p.visibility,
|
|
9611
|
+
last_activity_at: p.last_activity_at
|
|
9612
|
+
}));
|
|
9613
|
+
return {
|
|
9614
|
+
projects,
|
|
9615
|
+
hasMore: response.paginationInfo.next !== null
|
|
9616
|
+
};
|
|
9617
|
+
}
|
|
9618
|
+
async function getGitlabProjectLanguages({
|
|
9619
|
+
url,
|
|
9620
|
+
accessToken,
|
|
9621
|
+
projectId
|
|
9622
|
+
}) {
|
|
9623
|
+
try {
|
|
9624
|
+
const api2 = getGitBeaker({ url, gitlabAuthToken: accessToken });
|
|
9625
|
+
const languages3 = await api2.Projects.showLanguages(projectId);
|
|
9626
|
+
return Object.keys(languages3);
|
|
9627
|
+
} catch (e) {
|
|
9628
|
+
debug4(
|
|
9629
|
+
"[getGitlabProjectLanguages] Failed for project %d: %s",
|
|
9630
|
+
projectId,
|
|
9631
|
+
e instanceof Error ? e.message : String(e)
|
|
9632
|
+
);
|
|
9633
|
+
return [];
|
|
9634
|
+
}
|
|
9596
9635
|
}
|
|
9597
9636
|
async function getGitlabBranchList({
|
|
9598
9637
|
accessToken,
|
|
@@ -9662,6 +9701,11 @@ async function searchGitlabMergeRequests({
|
|
|
9662
9701
|
perPage = GITLAB_PER_PAGE,
|
|
9663
9702
|
page = 1
|
|
9664
9703
|
}) {
|
|
9704
|
+
if (perPage > GITLAB_MAX_PER_PAGE) {
|
|
9705
|
+
throw new Error(
|
|
9706
|
+
`perPage ${perPage} exceeds GitLab maximum of ${GITLAB_MAX_PER_PAGE}`
|
|
9707
|
+
);
|
|
9708
|
+
}
|
|
9665
9709
|
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
9666
9710
|
debug4(
|
|
9667
9711
|
"[searchGitlabMergeRequests] Fetching MRs for %s (page=%d, perPage=%d)",
|
|
@@ -9673,16 +9717,17 @@ async function searchGitlabMergeRequests({
|
|
|
9673
9717
|
url: repoUrl,
|
|
9674
9718
|
gitlabAuthToken: accessToken
|
|
9675
9719
|
});
|
|
9676
|
-
const
|
|
9720
|
+
const response = await api2.MergeRequests.all({
|
|
9677
9721
|
projectId: projectPath,
|
|
9678
9722
|
state: state === "all" ? void 0 : state,
|
|
9679
9723
|
updatedAfter: updatedAfter?.toISOString(),
|
|
9680
9724
|
orderBy,
|
|
9681
9725
|
sort,
|
|
9682
9726
|
perPage,
|
|
9683
|
-
page
|
|
9727
|
+
page,
|
|
9728
|
+
showExpanded: true
|
|
9684
9729
|
});
|
|
9685
|
-
const items =
|
|
9730
|
+
const items = response.data.map((mr) => ({
|
|
9686
9731
|
iid: mr.iid,
|
|
9687
9732
|
title: mr.title,
|
|
9688
9733
|
state: mr.state,
|
|
@@ -9700,7 +9745,7 @@ async function searchGitlabMergeRequests({
|
|
|
9700
9745
|
);
|
|
9701
9746
|
return {
|
|
9702
9747
|
items,
|
|
9703
|
-
hasMore:
|
|
9748
|
+
hasMore: response.paginationInfo.next !== null
|
|
9704
9749
|
};
|
|
9705
9750
|
}
|
|
9706
9751
|
var GITLAB_API_CONCURRENCY = 5;
|
|
@@ -9765,7 +9810,7 @@ async function getGitlabMrDataBatch({
|
|
|
9765
9810
|
const comments = notes.map((note) => ({
|
|
9766
9811
|
author: note.author ? {
|
|
9767
9812
|
login: note.author.username,
|
|
9768
|
-
type: note.author.username.endsWith("[bot]")
|
|
9813
|
+
type: note.author.username.endsWith("[bot]") ? "Bot" : "User"
|
|
9769
9814
|
} : null,
|
|
9770
9815
|
body: note.body
|
|
9771
9816
|
}));
|
|
@@ -9897,7 +9942,8 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
|
|
|
9897
9942
|
return { owner: organization, repo: repoName, projectPath };
|
|
9898
9943
|
}
|
|
9899
9944
|
var GITLAB_MAX_RESULTS_LIMIT = 1024;
|
|
9900
|
-
var
|
|
9945
|
+
var GITLAB_MAX_PER_PAGE = 100;
|
|
9946
|
+
var GITLAB_PER_PAGE = GITLAB_MAX_PER_PAGE;
|
|
9901
9947
|
async function getGitlabRecentCommits({
|
|
9902
9948
|
repoUrl,
|
|
9903
9949
|
accessToken,
|
|
@@ -9906,14 +9952,17 @@ async function getGitlabRecentCommits({
|
|
|
9906
9952
|
const { projectPath } = parseGitlabOwnerAndRepo(repoUrl);
|
|
9907
9953
|
const api2 = getGitBeaker({ url: repoUrl, gitlabAuthToken: accessToken });
|
|
9908
9954
|
const allCommits = [];
|
|
9955
|
+
const perPage = GITLAB_PER_PAGE;
|
|
9909
9956
|
let page = 1;
|
|
9910
9957
|
let hasMore = true;
|
|
9911
9958
|
while (hasMore && allCommits.length < GITLAB_MAX_RESULTS_LIMIT) {
|
|
9912
|
-
const
|
|
9959
|
+
const response = await api2.Commits.all(projectPath, {
|
|
9913
9960
|
since,
|
|
9914
|
-
perPage
|
|
9915
|
-
page
|
|
9961
|
+
perPage,
|
|
9962
|
+
page,
|
|
9963
|
+
showExpanded: true
|
|
9916
9964
|
});
|
|
9965
|
+
const commits = response.data;
|
|
9917
9966
|
if (commits.length === 0) {
|
|
9918
9967
|
hasMore = false;
|
|
9919
9968
|
break;
|
|
@@ -9935,11 +9984,8 @@ async function getGitlabRecentCommits({
|
|
|
9935
9984
|
parents: commit.parent_ids?.map((sha) => ({ sha })) || []
|
|
9936
9985
|
});
|
|
9937
9986
|
}
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
} else {
|
|
9941
|
-
page++;
|
|
9942
|
-
}
|
|
9987
|
+
hasMore = response.paginationInfo.next !== null;
|
|
9988
|
+
page++;
|
|
9943
9989
|
}
|
|
9944
9990
|
if (allCommits.length >= GITLAB_MAX_RESULTS_LIMIT) {
|
|
9945
9991
|
contextLogger.warn("[getGitlabRecentCommits] Hit commit pagination limit", {
|
|
@@ -10064,7 +10110,20 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
10064
10110
|
contextLogger.warn("[GitlabSCMLib.getRepoList] No access token provided");
|
|
10065
10111
|
throw new Error("no access token");
|
|
10066
10112
|
}
|
|
10067
|
-
|
|
10113
|
+
const allRepos = [];
|
|
10114
|
+
let cursor;
|
|
10115
|
+
let hasMore = true;
|
|
10116
|
+
while (hasMore) {
|
|
10117
|
+
const page = await this.searchRepos({
|
|
10118
|
+
scmOrg: _scmOrg,
|
|
10119
|
+
limit: 100,
|
|
10120
|
+
cursor
|
|
10121
|
+
});
|
|
10122
|
+
allRepos.push(...page.results);
|
|
10123
|
+
hasMore = page.hasMore;
|
|
10124
|
+
cursor = page.nextCursor;
|
|
10125
|
+
}
|
|
10126
|
+
return allRepos;
|
|
10068
10127
|
}
|
|
10069
10128
|
async getBranchList() {
|
|
10070
10129
|
this._validateAccessTokenAndUrl();
|
|
@@ -10294,8 +10353,47 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
10294
10353
|
mrNumbers: prNumbers
|
|
10295
10354
|
});
|
|
10296
10355
|
}
|
|
10297
|
-
async searchRepos(
|
|
10298
|
-
|
|
10356
|
+
async searchRepos(params) {
|
|
10357
|
+
if (!this.accessToken) {
|
|
10358
|
+
throw new Error("no access token");
|
|
10359
|
+
}
|
|
10360
|
+
const page = parseCursorSafe(params.cursor, 1);
|
|
10361
|
+
const perPage = params.limit || 10;
|
|
10362
|
+
const { projects, hasMore } = await searchGitlabProjects({
|
|
10363
|
+
url: this.url,
|
|
10364
|
+
accessToken: this.accessToken,
|
|
10365
|
+
perPage,
|
|
10366
|
+
page
|
|
10367
|
+
});
|
|
10368
|
+
const includeLanguages = params.includeLanguages !== false;
|
|
10369
|
+
const languageMap = /* @__PURE__ */ new Map();
|
|
10370
|
+
if (includeLanguages && projects.length > 0) {
|
|
10371
|
+
const languageResults = await Promise.all(
|
|
10372
|
+
projects.map(
|
|
10373
|
+
(p) => getGitlabProjectLanguages({
|
|
10374
|
+
url: this.url,
|
|
10375
|
+
accessToken: this.accessToken,
|
|
10376
|
+
projectId: p.id
|
|
10377
|
+
}).then((langs) => [p.id, langs])
|
|
10378
|
+
)
|
|
10379
|
+
);
|
|
10380
|
+
for (const [id, langs] of languageResults) {
|
|
10381
|
+
languageMap.set(id, langs);
|
|
10382
|
+
}
|
|
10383
|
+
}
|
|
10384
|
+
const results = projects.map((p) => ({
|
|
10385
|
+
repoName: p.path,
|
|
10386
|
+
repoUrl: p.web_url,
|
|
10387
|
+
repoOwner: p.namespace_name,
|
|
10388
|
+
repoLanguages: languageMap.get(p.id) || [],
|
|
10389
|
+
repoIsPublic: p.visibility === "public",
|
|
10390
|
+
repoUpdatedAt: p.last_activity_at
|
|
10391
|
+
}));
|
|
10392
|
+
return {
|
|
10393
|
+
results,
|
|
10394
|
+
nextCursor: hasMore ? String(page + 1) : void 0,
|
|
10395
|
+
hasMore
|
|
10396
|
+
};
|
|
10299
10397
|
}
|
|
10300
10398
|
async getPullRequestMetrics(prNumber) {
|
|
10301
10399
|
this._validateAccessTokenAndUrl();
|
|
@@ -10341,23 +10439,6 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
10341
10439
|
accessToken: this.accessToken
|
|
10342
10440
|
});
|
|
10343
10441
|
}
|
|
10344
|
-
/**
|
|
10345
|
-
* Extract Linear ticket links from pre-fetched comments (pure function, no API calls).
|
|
10346
|
-
* Linear bot uses the same comment format on GitLab as on GitHub.
|
|
10347
|
-
* Bot username may be 'linear' or 'linear[bot]' on GitLab.
|
|
10348
|
-
*/
|
|
10349
|
-
extractLinearTicketsFromComments(comments) {
|
|
10350
|
-
const tickets = [];
|
|
10351
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10352
|
-
for (const comment of comments) {
|
|
10353
|
-
const authorLogin = comment.author?.login?.toLowerCase() || "";
|
|
10354
|
-
const isLinearBot = authorLogin === "linear" || authorLogin === "linear[bot]" || comment.author?.type === "Bot" && authorLogin.includes("linear");
|
|
10355
|
-
if (isLinearBot) {
|
|
10356
|
-
tickets.push(...extractLinearTicketsFromBody(comment.body || "", seen));
|
|
10357
|
-
}
|
|
10358
|
-
}
|
|
10359
|
-
return tickets;
|
|
10360
|
-
}
|
|
10361
10442
|
};
|
|
10362
10443
|
|
|
10363
10444
|
// src/features/analysis/scm/scmFactory.ts
|
|
@@ -14948,8 +15029,8 @@ var openRedaction = new OpenRedaction({
|
|
|
14948
15029
|
"MONERO_ADDRESS",
|
|
14949
15030
|
"RIPPLE_ADDRESS",
|
|
14950
15031
|
// Medical Data (removed PRESCRIPTION_NUMBER - too broad, matches words containing "ription")
|
|
15032
|
+
// Removed MEDICAL_RECORD_NUMBER - too broad, "MR" prefix matches "Merge Request" in SCM contexts (e.g. "MR branches" → "MR br****es")
|
|
14951
15033
|
"NHS_NUMBER",
|
|
14952
|
-
"MEDICAL_RECORD_NUMBER",
|
|
14953
15034
|
"AUSTRALIAN_MEDICARE",
|
|
14954
15035
|
"HEALTH_PLAN_NUMBER",
|
|
14955
15036
|
"PATIENT_ID",
|
|
@@ -14993,10 +15074,10 @@ var openRedaction = new OpenRedaction({
|
|
|
14993
15074
|
"DOCKER_AUTH",
|
|
14994
15075
|
"KUBERNETES_SECRET",
|
|
14995
15076
|
// Government & Legal
|
|
15077
|
+
// Removed CLIENT_ID - too broad, "client" is ubiquitous in code (npm packages like @scope/client-*, class names like ClientSdkOptions)
|
|
14996
15078
|
"POLICE_REPORT_NUMBER",
|
|
14997
15079
|
"IMMIGRATION_NUMBER",
|
|
14998
|
-
"COURT_REPORTER_LICENSE"
|
|
14999
|
-
"CLIENT_ID"
|
|
15080
|
+
"COURT_REPORTER_LICENSE"
|
|
15000
15081
|
]
|
|
15001
15082
|
});
|
|
15002
15083
|
function maskString(str, showStart = 2, showEnd = 2) {
|
|
@@ -15019,6 +15100,15 @@ async function sanitizeDataWithCounts(obj) {
|
|
|
15019
15100
|
...piiDetections.low
|
|
15020
15101
|
];
|
|
15021
15102
|
for (const detection of allDetections) {
|
|
15103
|
+
if (detection.type === "CREDIT_CARD") {
|
|
15104
|
+
const start = detection.position[0];
|
|
15105
|
+
const end = detection.position[1];
|
|
15106
|
+
const charBefore = (start > 0 ? str[start - 1] : "") ?? "";
|
|
15107
|
+
const charAfter = str[end] ?? "";
|
|
15108
|
+
if (charBefore === "." || charBefore >= "0" && charBefore <= "9" || charAfter >= "0" && charAfter <= "9") {
|
|
15109
|
+
continue;
|
|
15110
|
+
}
|
|
15111
|
+
}
|
|
15022
15112
|
counts.detections.total++;
|
|
15023
15113
|
if (detection.severity === "high") counts.detections.high++;
|
|
15024
15114
|
else if (detection.severity === "medium") counts.detections.medium++;
|