Skip to content

Instantly share code, notes, and snippets.

@homanp
Last active March 10, 2026 15:25
Show Gist options
  • Select an option

  • Save homanp/258bc8be96ef7abb74f2a6c1392a1890 to your computer and use it in GitHub Desktop.

Select an option

Save homanp/258bc8be96ef7abb74f2a6c1392a1890 to your computer and use it in GitHub Desktop.
name: Contributor Trust Check
on:
pull_request_target:
types: [opened, reopened, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
check:
name: Scan PR author
runs-on: ubuntu-24.04
concurrency:
group: brin-check-${{ github.event.pull_request.number }}
cancel-in-progress: true
steps:
- name: Query Brin API
id: brin
run: |
AUTHOR="${{ github.event.pull_request.user.login }}"
RESPONSE=$(curl -sf "https://api.brin.sh/contributor/${AUTHOR}?details=true&mode=full" || echo '{}')
echo "response<<BRIN_EOF" >> "$GITHUB_OUTPUT"
echo "$RESPONSE" >> "$GITHUB_OUTPUT"
echo "BRIN_EOF" >> "$GITHUB_OUTPUT"
- name: Apply label and comment
uses: actions/github-script@v7
env:
BRIN_RESPONSE: ${{ steps.brin.outputs.response }}
with:
script: |
const marker = "<!-- brin-check -->";
const pr = context.payload.pull_request;
let data;
try {
data = JSON.parse(process.env.BRIN_RESPONSE);
} catch {
core.warning("Failed to parse Brin API response");
return;
}
if (!data.score && data.score !== 0) {
core.warning("Brin API returned no score");
return;
}
const score = data.score;
const verdict = data.verdict ?? "unknown";
const confidence = data.confidence ?? "unknown";
const isSafe = verdict === "safe";
const labels = [
{ name: "contributor:verified", color: "0969da", description: "Contributor passed trust analysis." },
{ name: "contributor:flagged", color: "e16f24", description: "Contributor flagged for review by trust analysis." },
];
for (const label of labels) {
try {
const { data: existing } = await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
});
if (existing.color !== label.color || (existing.description ?? "") !== label.description) {
await github.rest.issues.updateLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description,
});
}
} catch (error) {
if (error.status !== 404) throw error;
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description,
});
}
}
const nextLabel = isSafe ? "contributor:verified" : "contributor:flagged";
const labelNames = labels.map((l) => l.name);
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
});
const preserved = currentLabels
.map((l) => l.name)
.filter((name) => !labelNames.includes(name));
await github.rest.issues.setLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: [...preserved, nextLabel],
});
core.info(`PR #${pr.number}: ${verdict} -> ${nextLabel}`);
if (isSafe) {
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
per_page: 100,
});
const existing = comments.find((c) => c.body?.includes(marker));
if (existing) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
});
}
return;
}
const sub = data.sub_scores ?? {};
const fmt = (v) => (v != null ? String(Math.round(v)) : "\u2014");
const verdictEmoji = {
caution: "\u26A0\uFE0F",
suspicious: "\u26D4",
dangerous: "\uD83D\uDEA8",
}[verdict] ?? "\u2753";
let body = `${marker}\n`;
body += `### ${verdictEmoji} Contributor Trust Check \u2014 Review Recommended\n\n`;
body += `This contributor's profile shows patterns that may warrant additional review. `;
body += `This is based on their GitHub activity, not the contents of this PR.\n\n`;
body += `**[${data.name}](https://github.com/${data.name})** \u00b7 Score: **${score}**/100\n\n`;
if (data.threats && data.threats.length > 0) {
body += `#### Why was this flagged?\n\n`;
body += `| Signal | Severity | Detail |\n|--------|----------|--------|\n`;
for (const t of data.threats) {
body += `| ${t.type} | ${t.severity} | ${t.detail} |\n`;
}
body += `\n`;
}
body += `<details>\n<summary>Dimension breakdown</summary>\n\n`;
body += `| Dimension | Score | What it measures |\n|-----------|-------|------------------|\n`;
body += `| Identity | ${fmt(sub.identity)} | Account age, contribution history, GPG keys, org memberships |\n`;
body += `| Behavior | ${fmt(sub.behavior)} | PR patterns, unsolicited contribution ratio, activity cadence |\n`;
body += `| Content | ${fmt(sub.content)} | PR body substance, issue linkage, contribution quality |\n`;
body += `| Graph | ${fmt(sub.graph)} | Cross-repo trust, co-contributor relationships |\n\n`;
body += `</details>\n\n`;
body += `<sub>Analyzed by [Brin](https://brin.sh) \u00b7 [Full profile](${data.url}?details=true)</sub>\n`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
per_page: 100,
});
const existingComment = comments.find((c) => c.body?.includes(marker));
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body,
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment