Skip to content

Commit 5a2f45c

Browse files
iamtorukToruk_Studio
authored andcommitted
Initial release -- GitHub Action for PR contributor trust assessment
Analyzes PR author profiles (account age, repos, contribution history, profile completeness, commit signatures, security-critical file touches) and posts a trust assessment comment with tier labels on every PR. Zero-config with sensible defaults. Skips bots and collaborators.
0 parents  commit 5a2f45c

14 files changed

Lines changed: 959 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-node@v4
15+
with:
16+
node-version: 20
17+
- run: npm install
18+
- run: npm run build
19+
- name: Verify dist is up to date
20+
run: |
21+
git diff --exit-code dist/ || (echo "::error::dist/ is out of date. Run npm run build and commit." && exit 1)

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
*.log
3+
.DS_Store
4+
demo/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 AgentSeal
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# firstlook
2+
3+
Know who's opening pull requests before you review them.
4+
5+
**firstlook** is a GitHub Action that profiles PR contributors and posts a trust assessment on every pull request. It checks account age, contribution history, profile signals, commit signatures, and whether the PR touches security-critical files -- then leaves a clear summary right on the PR.
6+
7+
The xz-utils backdoor was merged by an account that spent two years building credibility through small, legitimate contributions. GitHub's built-in "first-time contributor" label wouldn't have caught it. firstlook gives you the full picture in seconds.
8+
9+
## Quick start
10+
11+
Create `.github/workflows/firstlook.yml`:
12+
13+
```yaml
14+
name: firstlook
15+
on:
16+
pull_request:
17+
types: [opened, reopened, synchronize]
18+
19+
jobs:
20+
assess:
21+
runs-on: ubuntu-latest
22+
permissions:
23+
pull-requests: write
24+
contents: read
25+
steps:
26+
- uses: getagentseal/firstlook@v1
27+
```
28+
29+
No configuration needed. It works out of the box.
30+
31+
## What you get
32+
33+
Every PR gets a comment like this:
34+
35+
> ### firstlook
36+
>
37+
> | Signal | Detail | |
38+
> |--------|--------|---|
39+
> | **Account** | Created 4y 2mo ago | :white_check_mark: |
40+
> | **Repos** | 36 public | :white_check_mark: |
41+
> | **Profile** | Bio, Company, Blog | :white_check_mark: |
42+
> | **History** | 89 merged PRs elsewhere | :white_check_mark: |
43+
> | **Followers** | 142 | :white_check_mark: |
44+
> | **Signed** | Yes | :white_check_mark: |
45+
>
46+
> **Trusted** (score: 92/100) -- Established contributor with a verified presence across GitHub.
47+
48+
And a label like `firstlook: trusted` gets applied to the PR.
49+
50+
When something needs attention:
51+
52+
> ### firstlook
53+
>
54+
> | Signal | Detail | |
55+
> |--------|--------|---|
56+
> | **Account** | Created 12 days ago | :warning: |
57+
> | **Repos** | 0 public | :warning: |
58+
> | **Profile** | None provided | :warning: |
59+
> | **History** | 0 merged PRs elsewhere | :warning: |
60+
> | **Followers** | 0 | :warning: |
61+
> | **Signed** | No | :warning: |
62+
> | **Security paths** | `.github/workflows/ci.yml` | :rotating_light: |
63+
>
64+
> **Unknown Contributor** (score: 0/100) -- Very new or empty account. Touching security-critical paths -- thorough review strongly recommended.
65+
66+
## Trust tiers
67+
68+
| Tier | Score | Meaning |
69+
|------|-------|---------|
70+
| **Trusted** | 65+ | Established account, verified presence, broad contribution history |
71+
| **Familiar** | 40-64 | Some history and profile signals. Standard review |
72+
| **Caution** | 15-39 | Limited history or newer account. Extra scrutiny recommended |
73+
| **Unknown** | 0-14 | Very new or empty account. Thorough review needed |
74+
75+
## Signals checked
76+
77+
- **Account age** -- how long the GitHub account has existed
78+
- **Public repos** -- number of public repositories
79+
- **Profile completeness** -- bio, company, blog, twitter, email
80+
- **Contribution history** -- merged PRs to other repositories (excludes your repo)
81+
- **Followers** -- community recognition
82+
- **Commit signatures** -- whether commits in this PR are signed and verified
83+
- **Security-critical files** -- whether the PR touches CI, lockfiles, build scripts, or dependency manifests
84+
85+
## Configuration
86+
87+
All inputs are optional. Defaults work for most projects.
88+
89+
```yaml
90+
- uses: getagentseal/firstlook@v1
91+
with:
92+
# Token for GitHub API calls (default: automatic)
93+
github-token: ${{ secrets.GITHUB_TOKEN }}
94+
95+
# Paths treated as security-critical (comma-separated substrings)
96+
security-paths: '.github/workflows/,package.json,Makefile,Dockerfile,scripts/'
97+
98+
# Prefix for PR labels (default: firstlook)
99+
label-prefix: 'firstlook'
100+
101+
# Post assessment comment (default: true)
102+
post-comment: 'true'
103+
104+
# Apply trust-tier labels (default: true)
105+
apply-labels: 'true'
106+
107+
# Skip repo collaborators with write+ access (default: true)
108+
skip-collaborators: 'true'
109+
110+
# Usernames to always skip (comma-separated)
111+
skip-users: 'dependabot,renovate'
112+
```
113+
114+
## Outputs
115+
116+
Use outputs in downstream workflow steps:
117+
118+
```yaml
119+
steps:
120+
- uses: getagentseal/firstlook@v1
121+
id: fl
122+
- run: echo "Tier is ${{ steps.fl.outputs.tier }}"
123+
```
124+
125+
| Output | Description |
126+
|--------|-------------|
127+
| `tier` | `trusted`, `familiar`, `caution`, or `unknown` |
128+
| `score` | Numeric score (0-100) |
129+
| `account-age-days` | Account age in days |
130+
131+
### Block merges on untrusted contributors
132+
133+
```yaml
134+
- uses: getagentseal/firstlook@v1
135+
id: fl
136+
- name: Gate on trust tier
137+
if: steps.fl.outputs.tier == 'unknown'
138+
run: |
139+
echo "::error::PR author is an unknown contributor. Manual approval required."
140+
exit 1
141+
```
142+
143+
## What it skips
144+
145+
- **Bot accounts** -- dependabot, renovate, etc. are skipped automatically
146+
- **Repo collaborators** -- users with write, maintain, or admin access are skipped by default
147+
- **Allow-listed users** -- anyone in the `skip-users` list
148+
149+
## Built by
150+
151+
[AgentSeal](https://agentseal.org) -- security tooling for the AI agent ecosystem.
152+
153+
Born from maintaining [CodeBurn](https://github.com/getagentseal/codeburn), where every PR from an unknown account meant 5 minutes of manual profile checking. firstlook automates the first 60 seconds of that process.
154+
155+
## License
156+
157+
MIT

action.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: 'firstlook'
2+
description: 'Instant PR contributor trust assessment. Know who is opening pull requests before you review.'
3+
author: 'AgentSeal'
4+
branding:
5+
icon: 'eye'
6+
color: 'blue'
7+
8+
inputs:
9+
github-token:
10+
description: 'GitHub token for API calls'
11+
required: false
12+
default: ${{ github.token }}
13+
security-paths:
14+
description: 'Comma-separated path patterns treated as security-critical'
15+
required: false
16+
default: '.github/workflows/,package.json,package-lock.json,yarn.lock,pnpm-lock.yaml,Makefile,Dockerfile,scripts/,.npmrc,setup.py,pyproject.toml,Cargo.toml,go.sum,Gemfile.lock'
17+
label-prefix:
18+
description: 'Prefix for trust-tier labels applied to the PR'
19+
required: false
20+
default: 'firstlook'
21+
post-comment:
22+
description: 'Whether to post an assessment comment on the PR'
23+
required: false
24+
default: 'true'
25+
apply-labels:
26+
description: 'Whether to apply trust-tier labels to the PR'
27+
required: false
28+
default: 'true'
29+
skip-collaborators:
30+
description: 'Skip analysis for repo collaborators with write access or above'
31+
required: false
32+
default: 'true'
33+
skip-users:
34+
description: 'Comma-separated usernames to always skip'
35+
required: false
36+
default: ''
37+
38+
outputs:
39+
tier:
40+
description: 'Trust tier: trusted, familiar, caution, or unknown'
41+
score:
42+
description: 'Numeric trust score (0-100)'
43+
account-age-days:
44+
description: 'Age of the contributor account in days'
45+
46+
runs:
47+
using: 'node20'
48+
main: 'dist/index.js'

dist/index.js

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)