Skip to content

perf(review): retire dry chunks and pipeline verification in the reverse audit #58558

perf(review): retire dry chunks and pipeline verification in the reverse audit

perf(review): retire dry chunks and pipeline verification in the reverse audit #58558

name: 'Comment Attachment Guard'
on:
issue_comment:
types:
- 'created'
- 'edited'
pull_request_review_comment:
types:
- 'created'
- 'edited'
pull_request_review:
types:
- 'submitted'
- 'edited'
permissions:
contents: 'read'
issues: 'write'
pull-requests: 'write'
concurrency:
# One scan per comment, keyed on whichever id this event carries. The scan
# reads the comment's CURRENT body, so when an edit lands while an earlier
# scan is queued the earlier result is already stale — cancelling it loses
# nothing. (Contrast the verify lane, where cancel-in-progress is false
# because a cancelled run destroys evidence.) Falling back to run_id keeps
# unexpected payloads on their own group instead of serialising them all
# into one.
group: >-
attachment-guard-${{
github.event.comment.id || github.event.review.id || github.run_id
}}
cancel-in-progress: true
jobs:
remove-suspicious-attachments:
timeout-minutes: 2
# The trust check used to live inside the script, which means a runner was
# queued, allocated and started before the job could decide it had nothing
# to do. Measured over the 200 most recent comments on this repo: 184 from
# trusted associations and 9 from bots — 96.5% of runs existed only to
# print "Trusted author; skipping". On a saturated hosted pool those waited
# up to 629s each for 2-5s of work.
#
# Hoisted here because GitHub evaluates `if:` BEFORE allocating a runner.
# The script keeps its own copy of these checks: this is an optimisation,
# not the control, and the two must be able to disagree without becoming
# unsafe. Every ambiguity therefore resolves toward RUNNING the scan — an
# unknown payload yields an empty association, which is not in the trusted
# list, so the job runs.
if: >-
github.repository == 'QwenLM/qwen-code' &&
github.event.sender.type != 'Bot' &&
!contains(
fromJSON('["OWNER","MEMBER","COLLABORATOR"]'),
github.event.comment.author_association ||
github.event.review.author_association
)
# Checks out nothing and runs no repository code (comment/review events
# use base-repo YAML), so the persistent ECS pool is safe and skips the
# saturated hosted queue. Kill-switch: MAINTAINER_ECS_RUNNER_DISABLED.
runs-on: '${{ (github.repository == ''QwenLM/qwen-code'' && vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'') && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}'
steps:
- name: 'Remove suspicious attachment comments'
uses: 'actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3' # v9.0.0
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
script: |
const trustedAssociations = new Set([
'OWNER',
'MEMBER',
'COLLABORATOR',
]);
const highRiskExtension =
/\.(?:zip|rar|7z|tar\.gz|tgz|dmg|pkg|exe|msi|bat|ps1|node|dylib)(?![a-zA-Z0-9])/i;
const linkPattern =
/(?:https?:\/\/|www\.|\/\/)[^\s"'<>\]]+|\[[^\]]+\]\((?:[^()\s]|\([^()\s]*\))+\)/gi;
const errorMessage = (error) =>
`${error?.status ? `${error.status} ` : ''}${error?.message ?? error}`;
const decodeTarget = (target) => {
let decoded = target;
for (let i = 0; i < 3; i += 1) {
try {
const next = decodeURIComponent(decoded);
if (next === decoded) {
break;
}
decoded = next;
} catch {
decoded = decoded.replace(/%[0-9a-f]{2}/gi, (match) =>
String.fromCharCode(Number.parseInt(match.slice(1), 16)),
);
}
}
return decoded
.replace(/[\u200B-\u200D\uFEFF\u00AD\u2060\u180E]/g, '')
.normalize('NFKC');
};
const highRiskTarget = (url) => {
let targets = [url];
if (/^(?:https?:\/\/|www\.|\/\/)/i.test(url)) {
try {
const normalizedUrl = /^\/\//.test(url)
? `https:${url}`
: /^www\./i.test(url)
? `https://${url}`
: url;
const parsedUrl = new URL(normalizedUrl);
targets = [
...parsedUrl.pathname.split('/').filter(Boolean),
...parsedUrl.searchParams.values(),
];
} catch {
targets = [url];
}
}
return (
targets
.map(decodeTarget)
.find((segment) => highRiskExtension.test(segment)) ||
decodeTarget(targets[targets.length - 1] || url)
);
};
const { sender } = context.payload;
const comment = context.payload.comment ?? context.payload.review;
const association = comment.author_association ?? '';
const action = context.payload.action ?? '';
const commentAuthor = comment.user?.login ?? 'ghost';
const body = comment.body ?? '';
const scanBody = body
.replace(/```[\s\S]*?```/g, '')
.replace(/`[^`]*`/g, '');
const eventName = context.eventName;
if (
trustedAssociations.has(association) ||
sender?.type === 'Bot' ||
(action === 'edited' &&
sender?.login &&
sender.login !== commentAuthor)
) {
core.info(`Trusted author (${association || sender?.type}); skipping.`);
return;
}
const reasons = [];
const linkSnippets = scanBody.match(linkPattern) ?? [];
const hasHighRiskLink = linkSnippets.some((snippet) => {
const mdMatch = snippet.match(/^\[[^\]]+\]\((.+)\)$/);
const url = mdMatch ? mdMatch[1] : snippet;
return highRiskExtension.test(highRiskTarget(url));
});
if (hasHighRiskLink) {
reasons.push('high-risk file extension in a link or attachment');
}
if (reasons.length === 0) {
core.info('No suspicious attachment pattern found.');
return;
}
const { owner, repo } = context.repo;
const moderationVerb =
eventName === 'pull_request_review' ? 'minimize' : 'delete';
let actionTaken = '';
let moderationErrorMessage = '';
let moderationErrorStatus;
try {
if (eventName === 'issue_comment') {
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: comment.id,
});
actionTaken = 'removed';
} else if (eventName === 'pull_request_review_comment') {
await github.rest.pulls.deleteReviewComment({
owner,
repo,
comment_id: comment.id,
});
actionTaken = 'removed';
} else if (eventName === 'pull_request_review') {
await github.graphql(
`mutation MinimizeComment($id: ID!) {
minimizeComment(input: {subjectId: $id, classifier: SPAM}) {
minimizedComment {
isMinimized
}
}
}`,
{ id: comment.node_id },
);
actionTaken = 'minimized';
}
} catch (error) {
moderationErrorStatus = error?.status;
moderationErrorMessage = `Failed to ${moderationVerb} suspicious comment ${comment.id}: ${errorMessage(error)}`;
core.warning(moderationErrorMessage);
}
try {
await core.summary
.addHeading('Suspicious attachment detected')
.addTable([
[
{ data: 'Field', header: true },
{ data: 'Value', header: true },
],
['Event', eventName],
['Author', commentAuthor],
['Association', association || 'none'],
['Comment ID', String(comment.id)],
['Reason', reasons.join(', ')],
[
'Action',
actionTaken ||
(eventName === 'pull_request_review'
? 'minimize failed'
: 'delete failed'),
],
])
.write();
} catch (error) {
core.warning(
`Failed to write suspicious comment summary: ${errorMessage(error)}`,
);
}
if (moderationErrorMessage && moderationErrorStatus !== 404) {
core.setFailed(moderationErrorMessage);
}