
























@@ -2,7 +2,7 @@
22// Secret scanning alert handler for OpenClaw maintainers.
33// Usage: node secret-scanning.mjs <command> [options]
445-import { execFileSync, spawnSync } from "node:child_process";
5+import { spawnSync } from "node:child_process";
66import crypto from "node:crypto";
77import fs from "node:fs";
88import os from "node:os";
@@ -39,7 +39,9 @@ function gh(args, { json = true, allowFailure = false } = {}) {
3939stderr: proc.stderr,
4040};
4141}
42-if (!json) return proc.stdout;
42+if (!json) {
43+return proc.stdout;
44+}
4345try {
4446return JSON.parse(proc.stdout);
4547} catch {
@@ -70,7 +72,9 @@ export function loadBodyRedactionResult(locationType, resultFile) {
7072if (!resultFile) {
7173fail("Body notifications require a redaction result file from redact-body-if-needed");
7274}
73-if (!fs.existsSync(resultFile)) fail(`File not found: ${resultFile}`);
75+if (!fs.existsSync(resultFile)) {
76+fail(`File not found: ${resultFile}`);
77+}
74787579const result = JSON.parse(fs.readFileSync(resultFile, "utf8"));
7680if (typeof result.notify_required !== "boolean") {
@@ -182,10 +186,11 @@ function fetchDiscussionComment(discussionNumber, discussionCommentDbId) {
182186failOnGraphQLFailure(gql, `Failed to fetch discussion #${discussionNumber}`);
183187184188const discussion = gql?.data?.repository?.discussion;
185-if (!discussion)
189+if (!discussion) {
186190fail(
187191`Discussion #${discussionNumber} not found — it may have been deleted. The alert cannot be processed via this skill.`,
188192);
193+}
189194190195discussionId = discussion.id;
191196@@ -205,15 +210,18 @@ function fetchDiscussionComment(discussionNumber, discussionCommentDbId) {
205210`Failed to fetch replies for discussion comment ${topLevelComment.id}`,
206211);
207212const replies = replyPage?.data?.node?.replies;
208-if (!replies)
213+if (!replies) {
209214fail(`Failed to paginate replies for discussion comment ${topLevelComment.id}`);
215+}
210216211217reply = findDiscussionCommentNode(replies.nodes, discussionCommentDbId);
212218hasMoreReplies = replies.pageInfo.hasNextPage;
213219replyCursor = replies.pageInfo.endCursor;
214220}
215221216-if (reply) return { discussionId, comment: reply };
222+if (reply) {
223+return { discussionId, comment: reply };
224+}
217225}
218226219227hasNextPage = discussion.comments.pageInfo.hasNextPage;
@@ -241,7 +249,9 @@ function createDiscussionComment(discussionNodeId, body, replyToNodeId) {
241249 * Fetch alert metadata + locations. Never exposes .secret.
242250 */
243251function cmdFetchAlert(alertNumber) {
244-if (!alertNumber) fail("Usage: fetch-alert <number>");
252+if (!alertNumber) {
253+fail("Usage: fetch-alert <number>");
254+}
245255246256const alert = gh(["api", `repos/${REPO}/secret-scanning/alerts/${alertNumber}?hide_secret=true`]);
247257@@ -280,28 +290,35 @@ function cmdFetchAlert(alertNumber) {
280290 * Saves full body to a temp file. Prints metadata + file path to stdout.
281291 */
282292function cmdFetchContent(locationJson) {
283-if (!locationJson) fail("Usage: fetch-content '<location-json>'");
293+if (!locationJson) {
294+fail("Usage: fetch-content '<location-json>'");
295+}
284296const location = JSON.parse(locationJson);
285297const type = location.type;
286298const details = location.details;
287299288300if (type === "discussion_comment") {
289301const commentUrl = details.discussion_comment_url;
290-if (!commentUrl) fail("No discussion_comment_url in location details");
302+if (!commentUrl) {
303+fail("No discussion_comment_url in location details");
304+}
291305292306const urlMatch = commentUrl.match(/discussions\/(\d+)#discussioncomment-(\d+)/);
293-if (!urlMatch) fail(`Cannot parse discussion comment URL: ${commentUrl}`);
307+if (!urlMatch) {
308+fail(`Cannot parse discussion comment URL: ${commentUrl}`);
309+}
294310const discussionNumber = urlMatch[1];
295311const discussionCommentDbId = urlMatch[2];
296312297313const { discussionId, comment } = fetchDiscussionComment(
298314discussionNumber,
299315discussionCommentDbId,
300316);
301-if (!comment)
317+if (!comment) {
302318fail(
303319`Discussion comment #${discussionCommentDbId} not found in discussion #${discussionNumber}`,
304320);
321+}
305322306323const bodyFile = tmpFile("body.md");
307324fs.writeFileSync(bodyFile, comment.body || "");
@@ -334,7 +351,9 @@ function cmdFetchContent(locationJson) {
334351details.issue_comment_url ||
335352details.pull_request_comment_url ||
336353details.pull_request_review_comment_url;
337-if (!commentUrl) fail(`No comment URL in location details`);
354+if (!commentUrl) {
355+fail(`No comment URL in location details`);
356+}
338357339358const comment = gh(["api", commentUrl]);
340359const bodyFile = tmpFile("body.md");
@@ -378,7 +397,9 @@ function cmdFetchContent(locationJson) {
378397);
379398} else if (type === "issue_body") {
380399const issueUrl = details.issue_body_url || details.issue_url;
381-if (!issueUrl) fail("No issue URL in location details");
400+if (!issueUrl) {
401+fail("No issue URL in location details");
402+}
382403383404const issue = gh(["api", issueUrl]);
384405const bodyFile = tmpFile("body.md");
@@ -414,7 +435,9 @@ function cmdFetchContent(locationJson) {
414435);
415436} else if (type === "pull_request_body") {
416437const prUrl = details.pull_request_body_url || details.pull_request_url;
417-if (!prUrl) fail("No PR URL in location details");
438+if (!prUrl) {
439+fail("No PR URL in location details");
440+}
418441419442const pr = gh(["api", prUrl]);
420443const bodyFile = tmpFile("body.md");
@@ -490,7 +513,9 @@ function cmdRedactBody(kind, number, bodyFile) {
490513if (!kind || !number || !bodyFile) {
491514fail("Usage: redact-body <issue|pr> <number> <redacted-body-file>");
492515}
493-if (!fs.existsSync(bodyFile)) fail(`File not found: ${bodyFile}`);
516+if (!fs.existsSync(bodyFile)) {
517+fail(`File not found: ${bodyFile}`);
518+}
494519495520const endpoint =
496521kind === "pr" ? `repos/${REPO}/pulls/${number}` : `repos/${REPO}/issues/${number}`;
@@ -509,8 +534,12 @@ function cmdRedactBodyIfNeeded(kind, number, currentBodyFile, redactedBodyFile,
509534"Usage: redact-body-if-needed <issue|pr> <number> <current-body-file> <redacted-body-file> <result-file>",
510535);
511536}
512-if (!fs.existsSync(currentBodyFile)) fail(`File not found: ${currentBodyFile}`);
513-if (!fs.existsSync(redactedBodyFile)) fail(`File not found: ${redactedBodyFile}`);
537+if (!fs.existsSync(currentBodyFile)) {
538+fail(`File not found: ${currentBodyFile}`);
539+}
540+if (!fs.existsSync(redactedBodyFile)) {
541+fail(`File not found: ${redactedBodyFile}`);
542+}
514543515544const currentBody = fs.readFileSync(currentBodyFile, "utf8");
516545const redactedBody = fs.readFileSync(redactedBodyFile, "utf8");
@@ -541,7 +570,9 @@ function cmdRedactBodyIfNeeded(kind, number, currentBodyFile, redactedBodyFile,
541570 * Delete a comment (and all its edit history).
542571 */
543572function cmdDeleteComment(commentId) {
544-if (!commentId) fail("Usage: delete-comment <comment-id>");
573+if (!commentId) {
574+fail("Usage: delete-comment <comment-id>");
575+}
545576gh(["api", `repos/${REPO}/issues/comments/${commentId}`, "-X", "DELETE"], { json: false });
546577console.log(JSON.stringify({ ok: true, deleted_comment_id: Number(commentId) }));
547578}
@@ -551,7 +582,9 @@ function cmdDeleteComment(commentId) {
551582 * Delete a discussion comment via GraphQL (and all its edit history).
552583 */
553584function cmdDeleteDiscussionComment(nodeId) {
554-if (!nodeId) fail("Usage: delete-discussion-comment <node-id>");
585+if (!nodeId) {
586+fail("Usage: delete-discussion-comment <node-id>");
587+}
555588const result = ghGraphQL(
556589`mutation { deleteDiscussionComment(input: { id: "${nodeId}" }) { comment { id } } }`,
557590);
@@ -566,9 +599,12 @@ function cmdDeleteDiscussionComment(nodeId) {
566599 * Create a new discussion comment via GraphQL.
567600 */
568601function cmdRecreateDiscussionComment(discussionNodeId, bodyFile, replyToNodeId) {
569-if (!discussionNodeId || !bodyFile)
602+if (!discussionNodeId || !bodyFile) {
570603fail("Usage: recreate-discussion-comment <discussion-node-id> <body-file> [reply-to-node-id]");
571-if (!fs.existsSync(bodyFile)) fail(`File not found: ${bodyFile}`);
604+}
605+if (!fs.existsSync(bodyFile)) {
606+fail(`File not found: ${bodyFile}`);
607+}
572608573609const body = fs.readFileSync(bodyFile, "utf8");
574610const newComment = createDiscussionComment(discussionNodeId, body, replyToNodeId);
@@ -586,8 +622,12 @@ function cmdRecreateDiscussionComment(discussionNodeId, bodyFile, replyToNodeId)
586622 * Create a new comment from a file.
587623 */
588624function cmdRecreateComment(issueNumber, bodyFile) {
589-if (!issueNumber || !bodyFile) fail("Usage: recreate-comment <issue-number> <body-file>");
590-if (!fs.existsSync(bodyFile)) fail(`File not found: ${bodyFile}`);
625+if (!issueNumber || !bodyFile) {
626+fail("Usage: recreate-comment <issue-number> <body-file>");
627+}
628+if (!fs.existsSync(bodyFile)) {
629+fail(`File not found: ${bodyFile}`);
630+}
591631592632const result = gh([
593633"api",
@@ -715,7 +755,9 @@ function cmdNotify(target, author, locationType, secretTypes, replyToNodeId) {
715755 * Close a secret scanning alert.
716756 */
717757function cmdResolve(alertNumber, resolution, comment) {
718-if (!alertNumber) fail("Usage: resolve <alert-number> [resolution] [comment]");
758+if (!alertNumber) {
759+fail("Usage: resolve <alert-number> [resolution] [comment]");
760+}
719761720762const res = resolution || "revoked";
721763const resComment = comment || "Content redacted and author notified to rotate credentials.";
@@ -773,8 +815,12 @@ function cmdListOpen() {
773815 * Print a formatted summary table from a JSON results file.
774816 */
775817function cmdSummary(jsonFile) {
776-if (!jsonFile) fail("Usage: summary <json-file>");
777-if (!fs.existsSync(jsonFile)) fail(`File not found: ${jsonFile}`);
818+if (!jsonFile) {
819+fail("Usage: summary <json-file>");
820+}
821+if (!fs.existsSync(jsonFile)) {
822+fail(`File not found: ${jsonFile}`);
823+}
778824779825const results = JSON.parse(fs.readFileSync(jsonFile, "utf8"));
780826const lines = [];
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。