Skip to content

Commit 6ec3b3c

Browse files
committed
Initial Commit
0 parents  commit 6ec3b3c

5 files changed

Lines changed: 197 additions & 0 deletions

File tree

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# GithubID
2+
3+
GithubID is an OSINT tool that can be used to retrieve the identities (name/email) that a given Github user has used in their public commits.
4+
5+
Under the hood, this uses Github's GraphQL API to query for the commits a user has pushed and then extracts their contact information from them.
6+
7+
![Screenshot highlighting basic usage of this tool](./img/screenshot.png)
8+
9+
## Usage
10+
11+
You'll need a Github Token to use this tool. You can generate a Personal Access Token [here](https://github.com/settings/tokens).
12+
13+
You can set your token as the `GH_TOKEN` environment variable or pass it through a flag:
14+
```bash
15+
$ githubid -user torvalds -token <your github token>
16+
```
17+
18+
## TODO
19+
20+
- [ ] Add pagination support for the GraphQL call;
21+
- [ ] Add [gharchive.org](https://www.gharchive.org/)/bigquery support to allow finding deleted commits (accidentally got a $100 bill from google cloud and am too depressed to do this now);

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/beescuit/githubid
2+
3+
go 1.22.4
4+
5+
require (
6+
github.com/machinebox/graphql v0.2.2 // indirect
7+
github.com/pkg/errors v0.9.1 // indirect
8+
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo=
2+
github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
3+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
4+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

img/screenshot.png

14.1 KB
Loading

main.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"flag"
7+
"fmt"
8+
"log"
9+
"net/http"
10+
"os"
11+
12+
"github.com/machinebox/graphql"
13+
)
14+
15+
var query = `
16+
query($userName:String!, $id:ID) {
17+
user(login: $userName){
18+
repositoriesContributedTo(includeUserRepositories: true, contributionTypes: COMMIT, first: 100) {
19+
pageInfo {
20+
hasNextPage
21+
endCursor
22+
}
23+
nodes {
24+
defaultBranchRef {
25+
target {
26+
... on Commit {
27+
history(author: {id: $id}) {
28+
pageInfo {
29+
hasNextPage
30+
endCursor
31+
}
32+
nodes {
33+
commitUrl
34+
author {
35+
email
36+
name
37+
}
38+
}
39+
}
40+
}
41+
}
42+
}
43+
}
44+
}
45+
}
46+
}`
47+
48+
func main() {
49+
username := flag.String("user", "", "(REQUIRED) Username of the target github account")
50+
printsource := flag.Bool("source", false, "Print commit URLs alongside discovered identities")
51+
showall := flag.Bool("all", false, "Print all commits (will repeat duplicate identities)")
52+
flagtoken := flag.String("token", "", "Github API Bearer token (can also be set from the GH_TOKEN env variable)")
53+
54+
flag.Parse()
55+
56+
if *username == "" {
57+
flag.PrintDefaults()
58+
os.Exit(0)
59+
}
60+
61+
var token = ""
62+
63+
if *flagtoken == "" {
64+
token = os.Getenv("GH_TOKEN")
65+
} else {
66+
token = *flagtoken
67+
}
68+
69+
if token == "" {
70+
fmt.Println("Github token missing. Please generate one and set it through the -token flag or the GH_TOKEN environment variable")
71+
os.Exit(0)
72+
}
73+
74+
userreq, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/users/%s", *username), nil)
75+
if err != nil {
76+
panic(err)
77+
}
78+
79+
userreq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
80+
81+
httpclient := http.Client{}
82+
res, err := httpclient.Do(userreq)
83+
if err != nil {
84+
fmt.Printf("Error fetching user ID: %s\n", err)
85+
os.Exit(1)
86+
}
87+
88+
if res.StatusCode == 401 {
89+
fmt.Println("Your Github token seems to be invalid.")
90+
os.Exit(1)
91+
}
92+
93+
var userres struct {
94+
NodeID string `json:"node_id"`
95+
}
96+
97+
err = json.NewDecoder(res.Body).Decode(&userres)
98+
if err != nil {
99+
fmt.Printf("Error parsing github api response: %s\n", err)
100+
os.Exit(1)
101+
}
102+
103+
userid := userres.NodeID
104+
105+
client := graphql.NewClient("https://api.github.com/graphql")
106+
107+
req := graphql.NewRequest(query)
108+
109+
req.Var("userName", username)
110+
req.Var("id", userid)
111+
112+
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
113+
114+
var respData struct {
115+
User struct {
116+
RepositoriesContributedTo struct {
117+
PageInfo struct {
118+
HasNextPage bool
119+
EndCursor string
120+
}
121+
Nodes []struct {
122+
DefaultBranchRef struct {
123+
Target struct {
124+
History struct {
125+
PageInfo struct {
126+
HasNextPage bool
127+
EndCursor string
128+
}
129+
Nodes []struct {
130+
CommitURL string
131+
Author struct {
132+
Email string
133+
Name string
134+
}
135+
}
136+
}
137+
}
138+
}
139+
}
140+
}
141+
}
142+
}
143+
144+
err = client.Run(context.Background(), req, &respData)
145+
if err != nil {
146+
log.Fatalf("Failed to execute request: %v", err)
147+
}
148+
149+
unique := make(map[string]bool)
150+
151+
for _, repo := range respData.User.RepositoriesContributedTo.Nodes {
152+
for _, commit := range repo.DefaultBranchRef.Target.History.Nodes {
153+
identity := fmt.Sprintf("%s <%s>", commit.Author.Name, commit.Author.Email)
154+
if _, exists := unique[identity]; *showall || !exists {
155+
unique[identity] = true
156+
if *printsource {
157+
fmt.Printf("%s - %s\n", identity, commit.CommitURL)
158+
} else {
159+
fmt.Println(identity)
160+
}
161+
}
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)