-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 6ec3b3c
Showing
5 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# GithubID | ||
|
||
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. | ||
|
||
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. | ||
|
||
![Screenshot highlighting basic usage of this tool](./img/screenshot.png) | ||
|
||
## Usage | ||
|
||
You'll need a Github Token to use this tool. You can generate a Personal Access Token [here](https://github.com/settings/tokens). | ||
|
||
You can set your token as the `GH_TOKEN` environment variable or pass it through a flag: | ||
```bash | ||
$ githubid -user torvalds -token <your github token> | ||
``` | ||
|
||
## TODO | ||
|
||
- [ ] Add pagination support for the GraphQL call; | ||
- [ ] 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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module github.com/beescuit/githubid | ||
|
||
go 1.22.4 | ||
|
||
require ( | ||
github.com/machinebox/graphql v0.2.2 // indirect | ||
github.com/pkg/errors v0.9.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo= | ||
github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA= | ||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/machinebox/graphql" | ||
) | ||
|
||
var query = ` | ||
query($userName:String!, $id:ID) { | ||
user(login: $userName){ | ||
repositoriesContributedTo(includeUserRepositories: true, contributionTypes: COMMIT, first: 100) { | ||
pageInfo { | ||
hasNextPage | ||
endCursor | ||
} | ||
nodes { | ||
defaultBranchRef { | ||
target { | ||
... on Commit { | ||
history(author: {id: $id}) { | ||
pageInfo { | ||
hasNextPage | ||
endCursor | ||
} | ||
nodes { | ||
commitUrl | ||
author { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}` | ||
|
||
func main() { | ||
username := flag.String("user", "", "(REQUIRED) Username of the target github account") | ||
printsource := flag.Bool("source", false, "Print commit URLs alongside discovered identities") | ||
showall := flag.Bool("all", false, "Print all commits (will repeat duplicate identities)") | ||
flagtoken := flag.String("token", "", "Github API Bearer token (can also be set from the GH_TOKEN env variable)") | ||
|
||
flag.Parse() | ||
|
||
if *username == "" { | ||
flag.PrintDefaults() | ||
os.Exit(0) | ||
} | ||
|
||
var token = "" | ||
|
||
if *flagtoken == "" { | ||
token = os.Getenv("GH_TOKEN") | ||
} else { | ||
token = *flagtoken | ||
} | ||
|
||
if token == "" { | ||
fmt.Println("Github token missing. Please generate one and set it through the -token flag or the GH_TOKEN environment variable") | ||
os.Exit(0) | ||
} | ||
|
||
userreq, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/users/%s", *username), nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
userreq.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) | ||
|
||
httpclient := http.Client{} | ||
res, err := httpclient.Do(userreq) | ||
if err != nil { | ||
fmt.Printf("Error fetching user ID: %s\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
if res.StatusCode == 401 { | ||
fmt.Println("Your Github token seems to be invalid.") | ||
os.Exit(1) | ||
} | ||
|
||
var userres struct { | ||
NodeID string `json:"node_id"` | ||
} | ||
|
||
err = json.NewDecoder(res.Body).Decode(&userres) | ||
if err != nil { | ||
fmt.Printf("Error parsing github api response: %s\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
userid := userres.NodeID | ||
|
||
client := graphql.NewClient("https://api.github.com/graphql") | ||
|
||
req := graphql.NewRequest(query) | ||
|
||
req.Var("userName", username) | ||
req.Var("id", userid) | ||
|
||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) | ||
|
||
var respData struct { | ||
User struct { | ||
RepositoriesContributedTo struct { | ||
PageInfo struct { | ||
HasNextPage bool | ||
EndCursor string | ||
} | ||
Nodes []struct { | ||
DefaultBranchRef struct { | ||
Target struct { | ||
History struct { | ||
PageInfo struct { | ||
HasNextPage bool | ||
EndCursor string | ||
} | ||
Nodes []struct { | ||
CommitURL string | ||
Author struct { | ||
Email string | ||
Name string | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
err = client.Run(context.Background(), req, &respData) | ||
if err != nil { | ||
log.Fatalf("Failed to execute request: %v", err) | ||
} | ||
|
||
unique := make(map[string]bool) | ||
|
||
for _, repo := range respData.User.RepositoriesContributedTo.Nodes { | ||
for _, commit := range repo.DefaultBranchRef.Target.History.Nodes { | ||
identity := fmt.Sprintf("%s <%s>", commit.Author.Name, commit.Author.Email) | ||
if _, exists := unique[identity]; *showall || !exists { | ||
unique[identity] = true | ||
if *printsource { | ||
fmt.Printf("%s - %s\n", identity, commit.CommitURL) | ||
} else { | ||
fmt.Println(identity) | ||
} | ||
} | ||
} | ||
} | ||
} |