gitrecon
Version:
This CLI tool is designed to retrieve exposed email addresses from a GitHub user’s public activity and to identify a GitHub username associated with a given email address. It is intended strictly for educational use and ethical security research. Please u
272 lines (241 loc) • 10.7 kB
JavaScript
// command execution logic
const GitHubUser = require('../services/github/githubUser');
const GitHubOrg = require('../services/github/githubOrg');
const GitLabUser = require('../services/gitlab/gitlabUser');
const GitLabGroup = require('../services/gitlab/gitlabGroup');
const EmailSearch = require('../services/emailSearch');
const GitHubApi = require('../services/github/githubApi');
const ColorUtils = require('../utils/colors');
const Validators = require('../utils/validators');
const ConsoleFormatter = require('../formatters/consoleFormatter');
const { updateHeader } = require('../config/settings');
const { setDelay } = require('../config/constants');
class Commands {
constructor() {
this.setupCommands();
}
setupCommands() {
this.commands = {
user: this.runUserRecon.bind(this),
org: this.runOrgRecon.bind(this),
email: this.runEmailSearch.bind(this),
repository: this.runRepositoryRecon.bind(this)
};
}
// Execute command based on parsed arguments - orijinal main function logic
async execute(args) {
try {
// Set the delay between requests - orijinal koddan
if (args.delay) {
setDelay(args.delay);
}
// If token is provided, add it to the headers - orijinal koddan
if (args.token) {
if (args.site === 'github') {
updateHeader({ Authorization: `token ${args.token}` });
} else {
updateHeader({ 'PRIVATE-TOKEN': args.token });
}
console.log(ColorUtils.green(`Using ${args.site} API token`));
}
// Determine which command to run
if (args.email) {
return await this.runEmailSearch(args);
} else if (args.org) {
return await this.runOrgRecon(args);
} else if (args.user) {
if (args.repository) {
return await this.runRepositoryRecon(args);
} else {
return await this.runUserRecon(args);
}
}
} catch (error) {
ConsoleFormatter.displayError('Command execution failed', error.message);
throw error;
}
}
// Run email search command - orijinal koddan
async runEmailSearch(args) {
if (!Validators.isValidEmail(args.email)) {
ConsoleFormatter.displayError('Invalid email address format');
return null;
}
if (args.site === 'github') {
const foundUsername = await EmailSearch.findUsernameByEmail(args.email);
if (foundUsername) {
console.log(ColorUtils.green(`Proceeding with reconnaissance on GitHub user: ${ColorUtils.yellow(foundUsername)}`));
// Update args to run user recon
args.user = foundUsername;
return await this.runUserRecon(args);
} else {
ConsoleFormatter.displayError('No GitHub username found for the provided email');
return null;
}
} else {
ConsoleFormatter.displayWarning('Email search is only supported for GitHub.');
return null;
}
}
// Run organization reconnaissance - orijinal koddan
async runOrgRecon(args) {
console.log(ColorUtils.green(`Starting reconnaissance on ${args.site} organization: ${ColorUtils.yellow(args.org)}`));
let result = null; if (args.site === 'github') {
result = await GitHubOrg.runRecon(args.org, {
downloadAvatarFlag: args.download_avatar,
outputFormat: args.output,
verbose: args.verbose
});
if (!result) {
ConsoleFormatter.displayError(`Failed to retrieve information for organization ${args.org}`);
} } else {
result = await GitLabGroup.runRecon(args.org, {
downloadAvatarFlag: args.download_avatar,
outputFormat: args.output,
verbose: args.verbose
});
if (!result) {
ConsoleFormatter.displayError(`Failed to retrieve information for GitLab group ${args.org}`);
}
}
return result;
}
// Run user reconnaissance - orijinal koddan
async runUserRecon(args) {
console.log(ColorUtils.green(`Starting reconnaissance on ${args.site} user: ${ColorUtils.yellow(args.user)}`));
let result = null; if (args.site === 'github') {
result = await GitHubUser.runRecon(args.user, {
downloadAvatarFlag: args.download_avatar,
saveFork: args.include_forks,
outputFormat: args.output,
verbose: args.verbose
}); } else {
result = await GitLabUser.runRecon(args.user, {
downloadAvatarFlag: args.download_avatar,
outputFormat: args.output,
verbose: args.verbose
});
}
return result;
}
// Run specific repository reconnaissance - orijinal koddan
async runRepositoryRecon(args) {
console.log(ColorUtils.green(`Scanning specific repository: ${ColorUtils.yellow(args.repository)}`));
const { setFound, getFound } = require('../config/settings');
const emailsToName = new Map();
try {
console.info(ColorUtils.green(`Scanning repository "${ColorUtils.yellow(args.repository)}"`));
const emailsToNameNew = await GitHubApi.getEmails(args.user, args.repository);
for (const [email, names] of emailsToNameNew.entries()) {
if (!emailsToName.has(email)) {
emailsToName.set(email, new Set());
}
names.forEach((name) => emailsToName.get(email).add(name));
}
if (emailsToName.size > 0) {
setFound([]); // Reset found array
const maxEmailWidth = Math.max(...Array.from(emailsToName.keys(), (email) => email.length));
console.info(ColorUtils.yellow('Found the following emails:'));
const foundEmails = [];
for (const [email, names] of emailsToName.entries()) {
const namesString = Array.from(names).join('; ');
foundEmails.push({
email: email.padEnd(maxEmailWidth, ' '),
authors: namesString
});
}
setFound(foundEmails);
console.log('\x1b[0m');
console.table(getFound());
// Return structured data
return {
username: args.user,
repository: args.repository,
scan_started_at: new Date().toISOString(),
scan_completed_at: new Date().toISOString(),
leaked_emails: Array.from(emailsToName.keys()),
email_details: Array.from(emailsToName.entries()).map(([email, names]) => ({
email,
names: Array.from(names),
sources: [args.repository]
}))
};
} else {
console.info(ColorUtils.yellow('No emails found in repository'));
return {
username: args.user,
repository: args.repository,
scan_started_at: new Date().toISOString(),
scan_completed_at: new Date().toISOString(),
leaked_emails: [],
email_details: []
};
}
} catch (error) {
ConsoleFormatter.displayError(`An error occurred: ${error.message}`);
throw error;
}
}
// Get available commands
getAvailableCommands() {
return {
user: {
description: 'Scan a user profile for exposed information',
usage: 'gitrecon --user <username> [options]',
examples: [
'gitrecon --user johndoe',
'gitrecon --user johndoe --site gitlab',
'gitrecon --user johndoe --include-forks --verbose'
]
},
org: {
description: 'Scan an organization/group for exposed information',
usage: 'gitrecon --org <organization> [options]',
examples: [
'gitrecon --org mycompany',
'gitrecon --org mygroup --site gitlab',
'gitrecon --org mycompany --output html'
]
},
email: {
description: 'Find GitHub username by email and scan',
usage: 'gitrecon --email <email> [options]',
examples: [
'gitrecon --email user@example.com',
'gitrecon --email user@example.com --verbose'
]
},
repository: {
description: 'Scan a specific repository for exposed emails',
usage: 'gitrecon --user <username> --repository <repo> [options]',
examples: [
'gitrecon --user johndoe --repository myproject',
'gitrecon --user johndoe --repository myproject --output json'
]
}
};
}
// Validate command requirements
validateCommand(args) {
if (args.email && args.site === 'gitlab') {
return {
isValid: false,
error: 'Email search is only supported for GitHub'
};
}
if (args.repository && !args.user) {
return {
isValid: false,
error: 'Repository scanning requires a username (--user)'
};
}
if (!args.user && !args.email && !args.org) {
return {
isValid: false,
error: 'No target specified. Use --user, --email, or --org'
};
}
return { isValid: true };
}
}
module.exports = Commands;