mirror of
https://github.com/anotherhadi/github-recon.git
synced 2026-10-05 19:08:24 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd99649b4e | ||
|
|
be6a9fbc6b | ||
|
|
4b75b6f755 | ||
|
|
d02479748e | ||
|
|
9eee8e60be | ||
|
|
4a3a06fa30 |
@@ -1,5 +1,11 @@
|
|||||||
# GH-Recon
|
# GH-Recon
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://github.com/anotherhadi/gh-recon/releases"><img src="https://img.shields.io/github/release/anotherhadi/gh-recon.svg" alt="Latest Release"></a>
|
||||||
|
<a href="https://pkg.go.dev/github.com/anotherhadi/gh-recon?tab=doc"><img src="https://godoc.org/github.com/anotherhadi/gh-recon?status.svg" alt="GoDoc"></a>
|
||||||
|
<a href="https://goreportcard.com/report/github.com/anotherhadi/gh-recon"><img src="https://goreportcard.com/badge/github.com/anotherhadi/gh-recon" alt="GoReportCard"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
Fetches and aggregates public OSINT data for a GitHub user, leveraging Go and the GitHub API.
|
Fetches and aggregates public OSINT data for a GitHub user, leveraging Go and the GitHub API.
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
package main
|
package ghrecon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
|
||||||
"github.com/google/go-github/v72/github"
|
"github.com/google/go-github/v72/github"
|
||||||
)
|
)
|
||||||
|
|
||||||
func commits(client *github.Client, ctx context.Context, username string) {
|
func (r Recon) Commits(username string) {
|
||||||
// Commits
|
PrintTitle("🐙 Commits")
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+GreenStyle.Render("+")+GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("Commits:\n"),
|
|
||||||
)
|
|
||||||
|
|
||||||
seenNames := make(map[string]struct{})
|
seenNames := make(map[string]struct{})
|
||||||
seenEmails := make(map[string]struct{})
|
seenEmails := make(map[string]struct{})
|
||||||
@@ -22,13 +16,13 @@ func commits(client *github.Client, ctx context.Context, username string) {
|
|||||||
collect := func(order string) error {
|
collect := func(order string) error {
|
||||||
opts := &github.SearchOptions{
|
opts := &github.SearchOptions{
|
||||||
Sort: "author-date",
|
Sort: "author-date",
|
||||||
Order: order, // "desc" ou "asc"
|
Order: order,
|
||||||
ListOptions: github.ListOptions{PerPage: 100},
|
ListOptions: github.ListOptions{PerPage: 100},
|
||||||
}
|
}
|
||||||
for page := 1; page <= 10; page++ {
|
for page := 1; page <= 10; page++ {
|
||||||
opts.ListOptions.Page = page
|
opts.ListOptions.Page = page
|
||||||
result, resp, err := client.Search.Commits(
|
result, resp, err := r.client.Search.Commits(
|
||||||
ctx,
|
r.ctx,
|
||||||
fmt.Sprintf("author:%s", username),
|
fmt.Sprintf("author:%s", username),
|
||||||
opts,
|
opts,
|
||||||
)
|
)
|
||||||
@@ -46,10 +40,22 @@ func commits(client *github.Client, ctx context.Context, username string) {
|
|||||||
if _, seen := seenNames[name]; !seen {
|
if _, seen := seenNames[name]; !seen {
|
||||||
seenNames[name] = struct{}{}
|
seenNames[name] = struct{}{}
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
|
PrintInfo(
|
||||||
|
"Name "+fmt.Sprint(len(names)),
|
||||||
|
name,
|
||||||
|
"from "+item.GetRepository().Owner.GetLogin()+"/"+item.GetRepository().
|
||||||
|
GetName(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if _, seen := seenEmails[email]; !seen {
|
if _, seen := seenEmails[email]; !seen {
|
||||||
seenEmails[email] = struct{}{}
|
seenEmails[email] = struct{}{}
|
||||||
emails = append(emails, email)
|
emails = append(emails, email)
|
||||||
|
PrintInfo(
|
||||||
|
"Email "+fmt.Sprint(len(emails)),
|
||||||
|
email,
|
||||||
|
"from "+item.GetRepository().Owner.GetLogin()+"/"+item.GetRepository().
|
||||||
|
GetName(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,18 +63,9 @@ func commits(client *github.Client, ctx context.Context, username string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := collect("desc"); err != nil {
|
if err := collect("desc"); err != nil {
|
||||||
log.Error("Failed to fetch commits", "err", err)
|
r.logger.Error("Failed to fetch commits", "err", err, "order", "desc")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if err := collect("asc"); err != nil {
|
if err := collect("asc"); err != nil {
|
||||||
log.Error("Failed to fetch commits", "err", err)
|
r.logger.Error("Failed to fetch commits", "err", err, "order", "asc")
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, name := range names {
|
|
||||||
PrintInfo("Name "+fmt.Sprint(i+1), name)
|
|
||||||
}
|
|
||||||
for i, email := range emails {
|
|
||||||
PrintInfo("Email "+fmt.Sprint(i+1), email)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package ghrecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r Recon) SshKeys(username string) {
|
||||||
|
sshKeys, resp, err := r.client.Users.ListKeys(r.ctx, username, nil)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to fetch ssh keys", "err", err)
|
||||||
|
} else if len(sshKeys) == 0 {
|
||||||
|
PrintTitle("🔑 SSH Keys")
|
||||||
|
r.logger.Info("No SSH Keys found\n")
|
||||||
|
} else {
|
||||||
|
PrintTitle("🔑 SSH Keys")
|
||||||
|
for i, key := range sshKeys {
|
||||||
|
PrintInfo("Key n°", fmt.Sprintf("%d", i))
|
||||||
|
PrintInfo("ID", fmt.Sprintf("%d", key.GetID()))
|
||||||
|
PrintInfo("URL", key.GetURL())
|
||||||
|
PrintInfo("Title", key.GetTitle())
|
||||||
|
PrintInfo("Created At", key.GetCreatedAt().String())
|
||||||
|
PrintInfo("Key", key.GetKey())
|
||||||
|
PrintInfo("Read Only", fmt.Sprintf("%t", key.GetReadOnly()))
|
||||||
|
PrintInfo("Verified", fmt.Sprintf("%t", key.GetVerified()))
|
||||||
|
PrintInfo("Last Used", key.GetLastUsed().String())
|
||||||
|
PrintInfo("Added By", key.GetAddedBy())
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WaitForRateLimit(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Recon) GpgKeys(username string) {
|
||||||
|
gpgKeys, resp, err := r.client.Users.ListGPGKeys(r.ctx, username, nil)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to fetch user's gpg keys", "err", err)
|
||||||
|
} else if len(gpgKeys) == 0 {
|
||||||
|
PrintTitle("🗝️ GPG Keys")
|
||||||
|
r.logger.Info("No GPG Keys found\n")
|
||||||
|
} else {
|
||||||
|
PrintTitle("🗝️ GPG Keys")
|
||||||
|
for i, key := range gpgKeys {
|
||||||
|
PrintInfo("Key n°", fmt.Sprintf("%d", i))
|
||||||
|
PrintInfo("ID", fmt.Sprintf("%d", key.GetID()))
|
||||||
|
PrintInfo("Key ID", key.GetKeyID())
|
||||||
|
PrintInfo("Public Key", key.GetPublicKey())
|
||||||
|
PrintInfo("Created At", key.GetCreatedAt().String())
|
||||||
|
PrintInfo("Expires At", key.GetExpiresAt().String())
|
||||||
|
PrintInfo("Can Sign", fmt.Sprintf("%t", key.GetCanSign()))
|
||||||
|
PrintInfo("Can Encrypt Comms", fmt.Sprintf("%t", key.GetCanEncryptComms()))
|
||||||
|
PrintInfo("Can Encrypt Storage", fmt.Sprintf("%t", key.GetCanEncryptStorage()))
|
||||||
|
PrintInfo("Can Certify", fmt.Sprintf("%t", key.GetCanCertify()))
|
||||||
|
PrintInfo("Primary Key ID", fmt.Sprintf("%d", key.GetPrimaryKeyID()))
|
||||||
|
PrintInfo("Raw Key", key.GetRawKey())
|
||||||
|
PrintInfo("Emails", fmt.Sprintf("%d", len(key.Emails)))
|
||||||
|
for j, email := range key.Emails {
|
||||||
|
PrintInfo(" Email n°", fmt.Sprintf("%d", j))
|
||||||
|
PrintInfo(" Email", email.GetEmail())
|
||||||
|
PrintInfo(" Verified", fmt.Sprintf("%t", email.GetVerified()))
|
||||||
|
}
|
||||||
|
PrintInfo("Subkeys", fmt.Sprintf("%d", len(key.Subkeys)))
|
||||||
|
for j, subkey := range key.Subkeys {
|
||||||
|
PrintInfo(" Subkey n°", fmt.Sprintf("%d", j))
|
||||||
|
PrintInfo(" Subkey ID", fmt.Sprintf("%d", subkey.GetID()))
|
||||||
|
PrintInfo(" Subkey Key ID", subkey.GetKeyID())
|
||||||
|
PrintInfo(" Subkey Created At", subkey.GetCreatedAt().String())
|
||||||
|
PrintInfo(" Subkey Expires At", subkey.GetExpiresAt().String())
|
||||||
|
PrintInfo(" Subkey Can Sign", fmt.Sprintf("%t", subkey.GetCanSign()))
|
||||||
|
PrintInfo(
|
||||||
|
" Subkey Can Encrypt Comms",
|
||||||
|
fmt.Sprintf("%t", subkey.GetCanEncryptComms()),
|
||||||
|
)
|
||||||
|
PrintInfo(
|
||||||
|
" Subkey Can Encrypt Storage",
|
||||||
|
fmt.Sprintf("%t", subkey.GetCanEncryptStorage()),
|
||||||
|
)
|
||||||
|
PrintInfo(" Subkey Can Certify", fmt.Sprintf("%t", subkey.GetCanCertify()))
|
||||||
|
PrintInfo(" Subkey Primary Key ID", fmt.Sprintf("%d", subkey.GetPrimaryKeyID()))
|
||||||
|
PrintInfo(" Subkey Raw Key", subkey.GetRawKey())
|
||||||
|
PrintInfo(" Subkey Public Key", subkey.GetPublicKey())
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WaitForRateLimit(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Recon) SshSigningKeys(username string) {
|
||||||
|
signingKeys, resp, err := r.client.Users.ListSSHSigningKeys(
|
||||||
|
r.ctx,
|
||||||
|
username,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to fetch user's ssh signing keys", "err", err)
|
||||||
|
} else if len(signingKeys) == 0 {
|
||||||
|
PrintTitle("📝 SSH Signing Keys")
|
||||||
|
r.logger.Info("No SSH Signing Keys found\n")
|
||||||
|
} else {
|
||||||
|
PrintTitle("📝 SSH Signing Keys")
|
||||||
|
for i, key := range signingKeys {
|
||||||
|
PrintInfo("Key n°", fmt.Sprintf("%d", i))
|
||||||
|
PrintInfo("ID", fmt.Sprintf("%d", key.GetID()))
|
||||||
|
PrintInfo("Key", key.GetKey())
|
||||||
|
PrintInfo("Title", key.GetTitle())
|
||||||
|
PrintInfo("Created At", key.GetCreatedAt().String())
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WaitForRateLimit(resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package ghrecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/log"
|
||||||
|
"github.com/google/go-github/v72/github"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Recon struct {
|
||||||
|
client *github.Client
|
||||||
|
logger *log.Logger
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecon(client *github.Client, logger *log.Logger, ctx context.Context) *Recon {
|
||||||
|
return &Recon{
|
||||||
|
client: client,
|
||||||
|
logger: logger,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package ghrecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r Recon) Orgs(username string) {
|
||||||
|
orgs, resp, err := r.client.Organizations.List(r.ctx, username, nil)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to fetch organizations", "err", err)
|
||||||
|
} else if len(orgs) == 0 {
|
||||||
|
PrintTitle("🏢 Organizations")
|
||||||
|
r.logger.Info("No Organizations found\n")
|
||||||
|
} else {
|
||||||
|
PrintTitle("🏢 Organizations")
|
||||||
|
for i, org := range orgs {
|
||||||
|
PrintInfo("Orgs n°", fmt.Sprintf("%d", i))
|
||||||
|
PrintInfo("Login", org.GetLogin())
|
||||||
|
PrintInfo("ID", fmt.Sprintf("%d", org.GetID()))
|
||||||
|
PrintInfo("Node ID", org.GetNodeID())
|
||||||
|
PrintInfo("URL", org.GetURL())
|
||||||
|
PrintInfo("Repos URL", org.GetReposURL())
|
||||||
|
PrintInfo("Events URL", org.GetEventsURL())
|
||||||
|
PrintInfo("Hooks URL", org.GetHooksURL())
|
||||||
|
PrintInfo("Issues URL", org.GetIssuesURL())
|
||||||
|
PrintInfo("Members URL", org.GetMembersURL())
|
||||||
|
PrintInfo("Public Members URL", org.GetPublicMembersURL())
|
||||||
|
PrintInfo("Avatar URL", org.GetAvatarURL())
|
||||||
|
PrintInfo("Description", org.GetDescription())
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WaitForRateLimit(resp)
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ghrecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r Recon) Socials(username string) {
|
||||||
|
resp, err := FetchGitHubAPI(r.client, "", "/users/"+username+"/social_accounts")
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to fetch socials", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type SocialAccount struct {
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
type SocialAccounts []SocialAccount
|
||||||
|
|
||||||
|
var socialAccounts SocialAccounts
|
||||||
|
err = json.Unmarshal(resp, &socialAccounts)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to unmarshal socials", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(socialAccounts) == 0 {
|
||||||
|
PrintTitle("🐥 Socials")
|
||||||
|
PrintTitle("No Socials found\n")
|
||||||
|
} else {
|
||||||
|
PrintTitle("🐥 Socials")
|
||||||
|
for i, account := range socialAccounts {
|
||||||
|
PrintInfo("Social n°", fmt.Sprintf("%d", i))
|
||||||
|
PrintInfo("Provider", account.Provider)
|
||||||
|
PrintInfo("URL", account.URL)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +1,19 @@
|
|||||||
package main
|
package ghrecon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
|
||||||
"github.com/google/go-github/v72/github"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func userInfo(client *github.Client, ctx context.Context, username string) {
|
func (r Recon) User(username string) {
|
||||||
fmt.Println(
|
user, resp, err := r.client.Users.Get(r.ctx, username)
|
||||||
GreyStyle.Render("[")+
|
|
||||||
GreenStyle.Render("-")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("Fetching user info...\n"),
|
|
||||||
)
|
|
||||||
|
|
||||||
user, resp, err := client.Users.Get(ctx, username)
|
|
||||||
if resp.StatusCode == 404 {
|
if resp.StatusCode == 404 {
|
||||||
fmt.Println(
|
r.logger.Fatal("User not found")
|
||||||
GreyStyle.Render("[")+
|
|
||||||
RedStyle.Render("x")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("Error:"),
|
|
||||||
RedStyle.Render("User not found"),
|
|
||||||
)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to fetch user's information", "err", err)
|
r.logger.Fatal("Failed to fetch user's information", "err", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrintTitle("👤 User informations")
|
||||||
PrintInfo("Username", user.GetLogin())
|
PrintInfo("Username", user.GetLogin())
|
||||||
PrintInfo("ID", fmt.Sprintf("%d", user.GetID()))
|
PrintInfo("ID", fmt.Sprintf("%d", user.GetID()))
|
||||||
PrintInfo("Avatar URL", user.GetAvatarURL())
|
PrintInfo("Avatar URL", user.GetAvatarURL())
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package ghrecon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -22,12 +22,12 @@ var (
|
|||||||
RedStyle = lipgloss.NewStyle().Foreground(Red)
|
RedStyle = lipgloss.NewStyle().Foreground(Red)
|
||||||
)
|
)
|
||||||
|
|
||||||
func header() {
|
func Header() {
|
||||||
asciiArt := " __ \n ___ _/ / _______ _______ ___ \n / _ `/ _ \\/ __/ -_) __/ _ \\/ _ \\\n \\_, /_//_/_/ \\__/\\__/\\___/_//_/\n/___/ "
|
asciiArt := " __ \n ___ _/ / _______ _______ ___ \n / _ `/ _ \\/ __/ -_) __/ _ \\/ _ \\\n \\_, /_//_/_/ \\__/\\__/\\___/_//_/\n/___/ "
|
||||||
fmt.Println(GreyStyle.Render(lipgloss.JoinVertical(lipgloss.Right, asciiArt, "@anotherhadi\n")))
|
fmt.Println(GreyStyle.Render(lipgloss.JoinVertical(lipgloss.Right, asciiArt, "@anotherhadi\n")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUsername(username string) error {
|
func ParseUsername(username string) error {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return fmt.Errorf("username is required")
|
return fmt.Errorf("username is required")
|
||||||
}
|
}
|
||||||
@@ -83,11 +83,24 @@ func FetchGitHubAPI(github *github.Client, token, path string) ([]byte, error) {
|
|||||||
return bodyBytes, nil
|
return bodyBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintInfo(key, value string) {
|
func PrintTitle(title string) {
|
||||||
if value == "" || value == "0" || value == "0001-01-01 00:00:00 +0000 UTC" {
|
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#7287fd"))
|
||||||
|
fmt.Println(style.Render(title) + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintInfo(key, value string, more ...string) {
|
||||||
|
if value == "" || value == "0001-01-01 00:00:00 +0000 UTC" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("%s %s\n", GreyStyle.Render(key+":"), GreenStyle.Render(value))
|
if strings.HasSuffix(key, "n°") {
|
||||||
|
fmt.Printf(" %s %s", GreyStyle.Render(key), value)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" %s %s", GreyStyle.Render(key+":"), value)
|
||||||
|
}
|
||||||
|
if len(more) > 0 {
|
||||||
|
fmt.Printf(" %s", GreyStyle.Render("("+strings.Join(more, ", ")+")"))
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
func WaitForRateLimit(resp *github.Response) {
|
func WaitForRateLimit(resp *github.Response) {
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
|
||||||
"github.com/google/go-github/v72/github"
|
|
||||||
)
|
|
||||||
|
|
||||||
func keys(client *github.Client, ctx context.Context, username string) {
|
|
||||||
sshKeys, resp, err := client.Users.ListKeys(ctx, username, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch user's keys", "err", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if len(sshKeys) == 0 {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
RedStyle.Render("x")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("No keys found for user "+username),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
GreenStyle.Render("+")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("Keys:\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for _, key := range sshKeys {
|
|
||||||
PrintInfo("ID", fmt.Sprintf("%d", key.GetID()))
|
|
||||||
PrintInfo("URL", key.GetURL())
|
|
||||||
PrintInfo("Title", key.GetTitle())
|
|
||||||
PrintInfo("Created At", key.GetCreatedAt().String())
|
|
||||||
PrintInfo("Key", key.GetKey())
|
|
||||||
PrintInfo("Read Only", fmt.Sprintf("%t", key.GetReadOnly()))
|
|
||||||
PrintInfo("Verified", fmt.Sprintf("%t", key.GetVerified()))
|
|
||||||
PrintInfo("Last Used", key.GetLastUsed().String())
|
|
||||||
PrintInfo("Added By", key.GetAddedBy())
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
WaitForRateLimit(resp)
|
|
||||||
|
|
||||||
gpgKeys, resp, err := client.Users.ListGPGKeys(ctx, username, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch user's GPG keys", "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(gpgKeys) == 0 {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
RedStyle.Render("x")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("No GPG keys found\n"),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
GreenStyle.Render("+")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("GPG Keys:\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for _, key := range gpgKeys {
|
|
||||||
PrintInfo("ID", fmt.Sprintf("%d", key.GetID()))
|
|
||||||
PrintInfo("Key ID", key.GetKeyID())
|
|
||||||
PrintInfo("Public Key", key.GetPublicKey())
|
|
||||||
PrintInfo("Created At", key.GetCreatedAt().String())
|
|
||||||
PrintInfo("Expires At", key.GetExpiresAt().String())
|
|
||||||
PrintInfo("Can Sign", fmt.Sprintf("%t", key.GetCanSign()))
|
|
||||||
PrintInfo("Can Encrypt Comms", fmt.Sprintf("%t", key.GetCanEncryptComms()))
|
|
||||||
PrintInfo("Can Encrypt Storage", fmt.Sprintf("%t", key.GetCanEncryptStorage()))
|
|
||||||
PrintInfo("Can Certify", fmt.Sprintf("%t", key.GetCanCertify()))
|
|
||||||
PrintInfo("Primary Key ID", fmt.Sprintf("%d", key.GetPrimaryKeyID()))
|
|
||||||
PrintInfo("Raw Key", key.GetRawKey())
|
|
||||||
PrintInfo("Emails", fmt.Sprintf("%d", len(key.Emails)))
|
|
||||||
for _, email := range key.Emails {
|
|
||||||
PrintInfo("\tEmail", email.GetEmail())
|
|
||||||
PrintInfo("\tVerified", fmt.Sprintf("%t", email.GetVerified()))
|
|
||||||
}
|
|
||||||
PrintInfo("Subkeys", fmt.Sprintf("%d", len(key.Subkeys)))
|
|
||||||
for _, subkey := range key.Subkeys {
|
|
||||||
PrintInfo("\tSubkey ID", fmt.Sprintf("%d", subkey.GetID()))
|
|
||||||
PrintInfo("\tSubkey Key ID", subkey.GetKeyID())
|
|
||||||
PrintInfo("\tSubkey Created At", subkey.GetCreatedAt().String())
|
|
||||||
PrintInfo("\tSubkey Expires At", subkey.GetExpiresAt().String())
|
|
||||||
PrintInfo("\tSubkey Can Sign", fmt.Sprintf("%t", subkey.GetCanSign()))
|
|
||||||
PrintInfo(
|
|
||||||
"\tSubkey Can Encrypt Comms",
|
|
||||||
fmt.Sprintf("%t", subkey.GetCanEncryptComms()),
|
|
||||||
)
|
|
||||||
PrintInfo(
|
|
||||||
"\tSubkey Can Encrypt Storage",
|
|
||||||
fmt.Sprintf("%t", subkey.GetCanEncryptStorage()),
|
|
||||||
)
|
|
||||||
PrintInfo("\tSubkey Can Certify", fmt.Sprintf("%t", subkey.GetCanCertify()))
|
|
||||||
PrintInfo("\tSubkey Primary Key ID", fmt.Sprintf("%d", subkey.GetPrimaryKeyID()))
|
|
||||||
PrintInfo("\tSubkey Raw Key", subkey.GetRawKey())
|
|
||||||
PrintInfo("\tSubkey Public Key", subkey.GetPublicKey())
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
WaitForRateLimit(resp)
|
|
||||||
|
|
||||||
signingKeys, resp, err := client.Users.ListSSHSigningKeys(
|
|
||||||
ctx,
|
|
||||||
username,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch user's signing keys", "err", err)
|
|
||||||
}
|
|
||||||
if len(signingKeys) == 0 {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
RedStyle.Render("x")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("No signing keys found\n"),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
GreenStyle.Render("+")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("Signing Keys:\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for _, key := range signingKeys {
|
|
||||||
PrintInfo("ID", fmt.Sprintf("%d", key.GetID()))
|
|
||||||
PrintInfo("Key", key.GetKey())
|
|
||||||
PrintInfo("Title", key.GetTitle())
|
|
||||||
PrintInfo("Created At", key.GetCreatedAt().String())
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
|
|
||||||
WaitForRateLimit(resp)
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
ghrecon "github.com/anotherhadi/gh-recon/gh-recon"
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
"github.com/google/go-github/v72/github"
|
"github.com/google/go-github/v72/github"
|
||||||
)
|
)
|
||||||
@@ -21,7 +22,7 @@ func main() {
|
|||||||
fmt.Println("Please provide a username with the --username flag")
|
fmt.Println("Please provide a username with the --username flag")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err := parseUsername(username)
|
err := ghrecon.ParseUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Invalid username", "err", err)
|
log.Error("Invalid username", "err", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -38,10 +39,26 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
header()
|
styles := log.DefaultStyles()
|
||||||
userInfo(client, ctx, username)
|
styles.Levels[log.InfoLevel] = styles.Levels[log.InfoLevel].Foreground(ghrecon.Grey)
|
||||||
orgs(client, ctx, username)
|
logger := log.NewWithOptions(os.Stderr, log.Options{
|
||||||
keys(client, ctx, username)
|
ReportCaller: false,
|
||||||
socials(client, username)
|
ReportTimestamp: false,
|
||||||
commits(client, ctx, username)
|
})
|
||||||
|
logger.SetStyles(styles)
|
||||||
|
|
||||||
|
r := ghrecon.NewRecon(
|
||||||
|
client,
|
||||||
|
logger,
|
||||||
|
ctx,
|
||||||
|
)
|
||||||
|
|
||||||
|
ghrecon.Header()
|
||||||
|
r.User(username)
|
||||||
|
r.Orgs(username)
|
||||||
|
r.SshKeys(username)
|
||||||
|
r.GpgKeys(username)
|
||||||
|
r.SshSigningKeys(username)
|
||||||
|
r.Socials(username)
|
||||||
|
r.Commits(username)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
|
||||||
"github.com/google/go-github/v72/github"
|
|
||||||
)
|
|
||||||
|
|
||||||
func orgs(client *github.Client, ctx context.Context, username string) {
|
|
||||||
orgs, resp, err := client.Organizations.List(ctx, username, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch user's organizations", "err", err)
|
|
||||||
}
|
|
||||||
if len(orgs) == 0 {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
RedStyle.Render("x")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("No organizations found for user "+username),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
GreenStyle.Render("+")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("Organizations:\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for _, org := range orgs {
|
|
||||||
PrintInfo("Login", org.GetLogin())
|
|
||||||
PrintInfo("ID", fmt.Sprintf("%d", org.GetID()))
|
|
||||||
PrintInfo("Node ID", org.GetNodeID())
|
|
||||||
PrintInfo("URL", org.GetURL())
|
|
||||||
PrintInfo("Repos URL", org.GetReposURL())
|
|
||||||
PrintInfo("Events URL", org.GetEventsURL())
|
|
||||||
PrintInfo("Hooks URL", org.GetHooksURL())
|
|
||||||
PrintInfo("Issues URL", org.GetIssuesURL())
|
|
||||||
PrintInfo("Members URL", org.GetMembersURL())
|
|
||||||
PrintInfo("Public Members URL", org.GetPublicMembersURL())
|
|
||||||
PrintInfo("Avatar URL", org.GetAvatarURL())
|
|
||||||
PrintInfo("Description", org.GetDescription())
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
WaitForRateLimit(resp)
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/log"
|
|
||||||
"github.com/google/go-github/v72/github"
|
|
||||||
)
|
|
||||||
|
|
||||||
func socials(client *github.Client, username string) {
|
|
||||||
resp, err := FetchGitHubAPI(client, "", "/users/"+username+"/social_accounts")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch user's social media accounts", "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SocialAccount struct {
|
|
||||||
Provider string `json:"provider"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
}
|
|
||||||
type SocialAccounts []SocialAccount
|
|
||||||
|
|
||||||
var socialAccounts SocialAccounts
|
|
||||||
err = json.Unmarshal(resp, &socialAccounts)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to unmarshal social media accounts", "err", err)
|
|
||||||
}
|
|
||||||
if len(socialAccounts) == 0 {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
RedStyle.Render("x")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("No social media accounts found\n"),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
fmt.Println(
|
|
||||||
GreyStyle.Render("[")+
|
|
||||||
GreenStyle.Render("+")+
|
|
||||||
GreyStyle.Render("]"),
|
|
||||||
GreyStyle.Render("Social Media Accounts:\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for _, account := range socialAccounts {
|
|
||||||
PrintInfo("Provider", account.Provider)
|
|
||||||
PrintInfo("URL", account.URL)
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user