LinkFinder Cheat Sheet
Overview
LinkFinder is a Python script designed to discover endpoints and their parameters in JavaScript files. It's particularly useful for bug bounty hunters and penetration testers who need to find hidden endpoints that might not be documented or easily discoverable through traditional crawling methods. LinkFinder uses regular expressions to extract relative and absolute URLs from JavaScript source code.
💡 Key Features: Endpoint extraction from JavaScript, parameter discovery, relative URL detection, Burp Suite integration, bulk processing capabilities, and comprehensive output formatting.
Installation and Setup
Python Installation
# Clone the repository
git clone https://github.com/GerbenJavado/LinkFinder.git
cd LinkFinder
# Install Python dependencies
pip install -r requirements.txt
# Make the script executable
chmod +x linkfinder.py
# Create symbolic link for global access
sudo ln -s $(pwd)/linkfinder.py /usr/local/bin/linkfinder
# Verify installation
linkfinder --help
python3 linkfinder.py --help
Virtual Environment Setup
# Create virtual environment
python3 -m venv linkfinder-env
source linkfinder-env/bin/activate
# Install dependencies
pip install requests argparse jsbeautifier
# Clone and setup LinkFinder
git clone https://github.com/GerbenJavado/LinkFinder.git
cd LinkFinder
pip install -r requirements.txt
# Test installation
python3 linkfinder.py --help
# Create activation script
cat > activate_linkfinder.sh << 'EOF'
#!/bin/bash
cd /path/to/LinkFinder
source linkfinder-env/bin/activate
python3 linkfinder.py "$@"
EOF
chmod +x activate_linkfinder.sh
Docker Installation
# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM python:3.9-alpine
RUN apk add --no-cache git
WORKDIR /app
RUN git clone https://github.com/GerbenJavado/LinkFinder.git .
RUN pip install -r requirements.txt
ENTRYPOINT ["python3", "linkfinder.py"]
EOF
# Build Docker image
docker build -t linkfinder .
# Run LinkFinder in Docker
docker run --rm linkfinder --help
# Run with volume mount for output
docker run --rm -v $(pwd):/output linkfinder -i /output/script.js -o /output/results.html
# Create alias for easier usage
echo 'alias linkfinder="docker run --rm -v $(pwd):/output linkfinder"' >> ~/.bashrc
source ~/.bashrc
Dependencies and Requirements
# Install required Python packages
pip install requests argparse jsbeautifier colorama
# Install additional useful packages
pip install beautifulsoup4 urllib3 tldextract
# Create requirements file
cat > requirements.txt << 'EOF'
requests>=2.25.1
argparse>=1.4.0
jsbeautifier>=1.14.0
colorama>=0.4.4
beautifulsoup4>=4.9.3
urllib3>=1.26.5
tldextract>=3.1.0
EOF
# Install from requirements
pip install -r requirements.txt
# Verify all dependencies
python3 -c "import requests, argparse, jsbeautifier; print('All dependencies installed successfully')"
Configuration and Setup
# Create configuration directory
mkdir -p ~/.linkfinder
# Create configuration file
cat > ~/.linkfinder/config.json << 'EOF'
{
"default_options": {
"timeout": 30,
"user_agent": "LinkFinder/1.0",
"max_retries": 3,
"output_format": "html"
},
"regex_patterns": {
"endpoints": [
"(?:\"|')(((?:[a-zA-Z]{1,10}://|//)[^\"'/]{1,}\\.[a-zA-Z]{2,}[^\"']{0,})|((?:/|\\.\\./|\\./)[^\"'><,;| *()(%%$^/\\\\\\[\\]][^\"'><,;|()]{1,})|([a-zA-Z0-9_\\-/]{1,}/[a-zA-Z0-9_\\-/]{1,}\\.(?:[a-zA-Z]{1,4}|action)(?:[\\?|#][^\"|']{0,}|))|([a-zA-Z0-9_\\-/]{1,}/[a-zA-Z0-9_\\-/]{3,}(?:[\\?|#][^\"|']{0,}|))|([a-zA-Z0-9_\\-]{1,}\\.(?:php|asp|aspx|jsp|json|action|html|js|txt|xml)(?:[\\?|#][^\"|']{0,}|)))(?:\"|')"
],
"parameters": [
"(?:\"|')([a-zA-Z0-9_\\-]+)(?:\"|')\\s*:\\s*(?:\"|')([^\"']+)(?:\"|')",
"(?:\\?|&)([a-zA-Z0-9_\\-]+)=([^&\\s\"']+)"
]
},
"output": {
"save_raw": true,
"beautify_js": true,
"create_html": true
}
}
EOF
# Set environment variables
export LINKFINDER_CONFIG=~/.linkfinder/config.json
export LINKFINDER_OUTPUT=~/.linkfinder/output
Basic Usage and Commands
Single File Analysis
# Basic JavaScript file analysis
linkfinder -i script.js
# Analyze with HTML output
linkfinder -i script.js -o results.html
# Analyze with custom output directory
linkfinder -i script.js -o /path/to/output/
# Analyze with verbose output
linkfinder -i script.js -v
# Analyze and beautify JavaScript
linkfinder -i script.js -b
# Analyze with custom regex
linkfinder -i script.js -r "custom_regex_pattern"
URL-based Analysis
# Analyze JavaScript from URL
linkfinder -i https://example.com/script.js
# Analyze with custom headers
linkfinder -i https://example.com/script.js -H "Authorization: Bearer token123"
# Analyze with custom user agent
linkfinder -i https://example.com/script.js -H "User-Agent: Custom LinkFinder 1.0"
# Analyze with proxy
linkfinder -i https://example.com/script.js --proxy http://127.0.0.1:8080
# Analyze with timeout
linkfinder -i https://example.com/script.js --timeout 60
# Analyze and save raw content
linkfinder -i https://example.com/script.js -o results.html --save-raw
Bulk Analysis
# Analyze multiple files
linkfinder -i script1.js script2.js script3.js -o bulk_results.html
# Analyze from file list
echo -e "https://example.com/script1.js\nhttps://example.com/script2.js" > js_urls.txt
linkfinder -i js_urls.txt -o bulk_analysis.html
# Analyze all JS files in directory
find /path/to/js/files -name "*.js" -exec linkfinder -i {} -o results_{}.html \;
# Analyze with threading (custom script)
cat > bulk_linkfinder.sh << 'EOF'
#!/bin/bash
input_file="$1"
output_dir="$2"
max_jobs="${3:-10}"
mkdir -p "$output_dir"
while read -r url; do
if [ -n "$url" ]; then
(
safe_name=$(echo "$url" | sed 's/[^a-zA-Z0-9]/_/g')
linkfinder -i "$url" -o "$output_dir/${safe_name}.html"
) &
# Limit concurrent jobs
while [ $(jobs -r | wc -l) -ge $max_jobs ]; do
sleep 1
done
fi
done < "$input_file"
# Wait for all jobs to complete
wait
EOF
chmod +x bulk_linkfinder.sh
./bulk_linkfinder.sh js_urls.txt results/ 5
Advanced Endpoint Discovery
Custom Pattern Extraction
#!/usr/bin/env python3
# Enhanced LinkFinder with custom patterns and analysis
import re
import json
import requests
import argparse
import threading
import time
from urllib.parse import urljoin, urlparse
from concurrent.futures import ThreadPoolExecutor, as_completed
import os
import jsbeautifier
class AdvancedLinkFinder:
def __init__(self, config_file=None):
self.results = []
self.lock = threading.Lock()
self.session = requests.Session()
# Enhanced regex patterns
self.patterns = {
'endpoints': [
# Original LinkFinder pattern
r'(?:"|\')\s*(((?:[a-zA-Z]{1,10}://|//)[^"\'/]{1,}\.[a-zA-Z]{2,}[^"\']{0,})|((?:/|\.\.\/|\.\/)[^"\'><,;| *()(%%$^/\\\\\\[\\]][^"\'><,;|()]{1,})|([a-zA-Z0-9_\\-/]{1,}/[a-zA-Z0-9_\\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\\?|#][^"|\']{0,}|))|([a-zA-Z0-9_\\-/]{1,}/[a-zA-Z0-9_\\-/]{3,}(?:[\\?|#][^"|\']{0,}|))|([a-zA-Z0-9_\\-]{1,}\.(?:php|asp|aspx|jsp|json|action|html|js|txt|xml)(?:[\\?|#][^"|\']{0,}|)))\s*(?:"|\')',
# Additional patterns for modern frameworks
r'(?:fetch|axios|ajax)\s*\(\s*["\']([^"\']+)["\']',
r'(?:GET|POST|PUT|DELETE|PATCH)\s*["\']([^"\']+)["\']',
r'(?:href|src|action)\s*=\s*["\']([^"\']+)["\']',
r'(?:url|endpoint|path)\s*:\s*["\']([^"\']+)["\']',
r'(?:api|route)\s*\(\s*["\']([^"\']+)["\']',
r'router\.\w+\s*\(\s*["\']([^"\']+)["\']',
r'app\.\w+\s*\(\s*["\']([^"\']+)["\']',
# GraphQL and API patterns
r'(?:query|mutation)\s*["\']([^"\']+)["\']',
r'(?:graphql|gql)\s*`([^`]+)`',
r'(?:endpoint|baseURL)\s*:\s*["\']([^"\']+)["\']',
# WebSocket patterns
r'(?:ws|wss)://[^"\'\\s]+',
r'WebSocket\s*\(\s*["\']([^"\']+)["\']',
# File upload patterns
r'(?:upload|file)\s*:\s*["\']([^"\']+)["\']',
r'FormData\s*\(\s*["\']([^"\']+)["\']'
],
'parameters': [
r'(?:"|\')\s*([a-zA-Z0-9_\\-]+)\s*(?:"|\')\s*:\s*(?:"|\')\s*([^"\']+)\s*(?:"|\')',
r'(?:\\?|&)([a-zA-Z0-9_\\-]+)=([^&\\s"\']+)',
r'(?:param|parameter|arg)\s*:\s*["\']([^"\']+)["\']',
r'(?:data|body)\s*\[\s*["\']([^"\']+)["\']',
r'getElementById\s*\(\s*["\']([^"\']+)["\']',
r'querySelector\s*\(\s*["\']([^"\']+)["\']',
r'name\s*=\s*["\']([^"\']+)["\']',
r'id\s*=\s*["\']([^"\']+)["\']'
],
'secrets': [
r'(?i)(api[_\\-]?key|apikey)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{10,})["\']',
r'(?i)(secret|password|pwd)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{8,})["\']',
r'(?i)(token|auth)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{10,})["\']',
r'(?i)(access[_\\-]?token)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{10,})["\']',
r'(?i)(client[_\\-]?secret)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{10,})["\']',
r'(?i)(private[_\\-]?key)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{10,})["\']'
],
'domains': [
r'(?:https?://)?([a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,})',
r'(?:"|\')\s*([a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,})\s*(?:"|\')',
r'(?:subdomain|host|domain)\s*:\s*["\']([a-zA-Z0-9.-]+)["\']'
],
'comments': [
r'/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/',
r'//.*$'
]
}
# Load custom configuration if provided
if config_file and os.path.exists(config_file):
with open(config_file, 'r') as f:
config = json.load(f)
if 'regex_patterns' in config:
self.patterns.update(config['regex_patterns'])
def analyze_javascript_file(self, input_source, beautify=False):
"""Analyze JavaScript file or URL for endpoints and parameters"""
results = {
'source': input_source,
'endpoints': [],
'parameters': [],
'secrets': [],
'domains': [],
'comments': [],
'analysis': {
'total_lines': 0,
'minified': False,
'obfuscated': False,
'framework_detected': [],
'api_calls': 0,
'external_requests': 0
}
}
try:
# Get JavaScript content
if input_source.startswith(('http://', 'https://')):
content = self._fetch_from_url(input_source)
results['source_type'] = 'url'
else:
content = self._read_from_file(input_source)
results['source_type'] = 'file'
if not content:
results['error'] = 'Failed to retrieve content'
return results
# Beautify if requested
if beautify:
try:
content = jsbeautifier.beautify(content)
results['beautified'] = True
except:
results['beautified'] = False
# Analyze content
results['analysis'] = self._analyze_content(content)
# Extract patterns
for pattern_type, patterns in self.patterns.items():
if pattern_type in results:
for pattern in patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
if matches:
for match in matches:
# Handle tuple results from groups
if isinstance(match, tuple):
match = next((m for m in match if m), match[0])
if match and match not in results[pattern_type]:
results[pattern_type].append(match)
# Clean and validate results
results = self._clean_results(results, input_source)
except Exception as e:
results['error'] = str(e)
return results
def _fetch_from_url(self, url):
"""Fetch JavaScript content from URL"""
try:
response = self.session.get(url, timeout=30)
response.raise_for_status()
return response.text
except Exception as e:
print(f"Error fetching {url}: {e}")
return None
def _read_from_file(self, file_path):
"""Read JavaScript content from file"""
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
return f.read()
except Exception as e:
print(f"Error reading {file_path}: {e}")
return None
def _analyze_content(self, content):
"""Analyze JavaScript content characteristics"""
analysis = {
'total_lines': len(content.split('\n')),
'minified': False,
'obfuscated': False,
'framework_detected': [],
'api_calls': 0,
'external_requests': 0,
'eval_usage': 0,
'document_write': 0
}
# Check for minification
if '\n' not in content[:1000] and len(content) > 1000:
analysis['minified'] = True
# Check for obfuscation
if re.search(r'\\x[0-9a-fA-F]{2}', content) or len(re.findall(r'[a-zA-Z_$][a-zA-Z0-9_$]*', content)) < len(content) / 20:
analysis['obfuscated'] = True
# Detect frameworks
frameworks = {
'React': ['React', 'react', 'ReactDOM', 'jsx'],
'Vue': ['Vue', 'vue', 'v-if', 'v-for'],
'Angular': ['angular', 'ng-', '@angular', 'Angular'],
'jQuery': ['jQuery', '$', 'jquery'],
'Express': ['express', 'app.get', 'app.post'],
'Next.js': ['next', 'Next', '_next'],
'Nuxt.js': ['nuxt', 'Nuxt', '_nuxt'],
'Svelte': ['svelte', 'Svelte'],
'Ember': ['ember', 'Ember'],
'Backbone': ['backbone', 'Backbone'],
'D3': ['d3', 'D3'],
'Lodash': ['lodash', '_'],
'Moment': ['moment', 'Moment'],
'Axios': ['axios', 'Axios'],
'Socket.io': ['socket.io', 'io(']
}
for framework, indicators in frameworks.items():
if any(indicator in content for indicator in indicators):
analysis['framework_detected'].append(framework)
# Count specific patterns
analysis['api_calls'] = len(re.findall(r'(?:fetch|axios|ajax|XMLHttpRequest)', content, re.IGNORECASE))
analysis['external_requests'] = len(re.findall(r'https?://(?!(?:localhost|127\.0\.0\.1|0\.0\.0\.0))', content))
analysis['eval_usage'] = len(re.findall(r'\\beval\\s*\\(', content))
analysis['document_write'] = len(re.findall(r'document\\.write', content))
return analysis
def _clean_results(self, results, source_url):
"""Clean and validate extracted results"""
# Clean endpoints
cleaned_endpoints = []
for endpoint in results['endpoints']:
endpoint = endpoint.strip('\'"')
# Skip if too short or contains invalid characters
if len(endpoint) < 2 or any(char in endpoint for char in ['<', '>', '{', '}', '\n', '\t']):
continue
# Convert relative URLs to absolute if source URL is provided
if source_url and source_url.startswith(('http://', 'https://')) and not endpoint.startswith(('http://', 'https://')):
if endpoint.startswith('/'):
base_url = f"{urlparse(source_url).scheme}://{urlparse(source_url).netloc}"
endpoint = urljoin(base_url, endpoint)
else:
endpoint = urljoin(source_url, endpoint)
if endpoint not in cleaned_endpoints:
cleaned_endpoints.append(endpoint)
results['endpoints'] = cleaned_endpoints
# Clean parameters
cleaned_parameters = []
for param in results['parameters']:
if isinstance(param, tuple):
param = param[0] if param[0] else param[1] if len(param) > 1 else ''
param = param.strip('\'"')
if len(param) > 0 and (param.isalnum() or '_' in param or '-' in param):
if param not in cleaned_parameters:
cleaned_parameters.append(param)
results['parameters'] = cleaned_parameters
# Clean secrets
cleaned_secrets = []
for secret in results['secrets']:
if isinstance(secret, tuple) and len(secret) >= 2:
key, value = secret[0], secret[1]
if value.lower() not in ['password', 'secret', 'token', 'key', 'null', 'undefined']:
cleaned_secrets.append({'type': key, 'value': value})
results['secrets'] = cleaned_secrets
# Clean domains
cleaned_domains = []
for domain in results['domains']:
domain = domain.strip('\'"')
if '.' in domain and len(domain) > 3:
if domain not in cleaned_domains:
cleaned_domains.append(domain)
results['domains'] = cleaned_domains
return results
def bulk_analyze(self, input_list, max_workers=10, beautify=False):
"""Analyze multiple JavaScript files/URLs in parallel"""
print(f"Starting bulk analysis of {len(input_list)} targets")
print(f"Max workers: {max_workers}")
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# Submit all tasks
future_to_input = {
executor.submit(self.analyze_javascript_file, input_source, beautify): input_source
for input_source in input_list
}
# Process completed tasks
for future in as_completed(future_to_input):
input_source = future_to_input[future]
try:
result = future.result()
with self.lock:
self.results.append(result)
if 'error' not in result:
endpoint_count = len(result['endpoints'])
param_count = len(result['parameters'])
secret_count = len(result['secrets'])
print(f"✓ {input_source}: {endpoint_count} endpoints, {param_count} parameters, {secret_count} secrets")
else:
print(f"✗ {input_source}: {result['error']}")
except Exception as e:
print(f"✗ Error analyzing {input_source}: {e}")
return self.results
def generate_html_report(self, output_file='linkfinder_report.html'):
"""Generate HTML report similar to original LinkFinder"""
# Calculate statistics
total_endpoints = sum(len(r.get('endpoints', [])) for r in self.results)
total_parameters = sum(len(r.get('parameters', [])) for r in self.results)
total_secrets = sum(len(r.get('secrets', [])) for r in self.results)
# Aggregate unique findings
all_endpoints = []
all_parameters = []
all_domains = []
for result in self.results:
all_endpoints.extend(result.get('endpoints', []))
all_parameters.extend(result.get('parameters', []))
all_domains.extend(result.get('domains', []))
unique_endpoints = sorted(list(set(all_endpoints)))
unique_parameters = sorted(list(set(all_parameters)))
unique_domains = sorted(list(set(all_domains)))
# Generate HTML content
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>LinkFinder Analysis Report</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; }}
.header {{ background-color: #2c3e50; color: white; padding: 20px; border-radius: 5px; margin-bottom: 20px; }}
.stats {{ display: flex; justify-content: space-around; margin: 20px 0; }}
.stat-box {{ background-color: white; padding: 15px; border-radius: 5px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
.section {{ background-color: white; margin: 20px 0; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
.endpoint {{ margin: 5px 0; padding: 8px; background-color: #ecf0f1; border-left: 4px solid #3498db; }}
.parameter {{ margin: 5px 0; padding: 8px; background-color: #e8f5e8; border-left: 4px solid #27ae60; }}
.secret {{ margin: 5px 0; padding: 8px; background-color: #fdf2e9; border-left: 4px solid #e67e22; }}
.domain {{ margin: 5px 0; padding: 8px; background-color: #f4ecf7; border-left: 4px solid #8e44ad; }}
table {{ width: 100%; border-collapse: collapse; margin: 10px 0; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: #34495e; color: white; }}
.toggle-btn {{ background-color: #3498db; color: white; border: none; padding: 5px 10px; cursor: pointer; border-radius: 3px; }}
.hidden {{ display: none; }}
</style>
<script>
function toggleSection(id) {{
var element = document.getElementById(id);
element.classList.toggle('hidden');
}}
</script>
</head>
<body>
<div class="header">
<h1>LinkFinder Analysis Report</h1>
<p>Generated on: {time.strftime('%Y-%m-%d %H:%M:%S')}</p>
<p>Files analyzed: {len(self.results)}</p>
</div>
<div class="stats">
<div class="stat-box">
<h3>{total_endpoints}</h3>
<p>Total Endpoints</p>
</div>
<div class="stat-box">
<h3>{len(unique_endpoints)}</h3>
<p>Unique Endpoints</p>
</div>
<div class="stat-box">
<h3>{total_parameters}</h3>
<p>Total Parameters</p>
</div>
<div class="stat-box">
<h3>{len(unique_parameters)}</h3>
<p>Unique Parameters</p>
</div>
<div class="stat-box">
<h3>{total_secrets}</h3>
<p>Secrets Found</p>
</div>
</div>
<div class="section">
<h2>Unique Endpoints <button class="toggle-btn" onclick="toggleSection('endpoints')">Toggle</button></h2>
<div id="endpoints">
"""
for endpoint in unique_endpoints[:100]: # Show first 100
html_content += f' <div class="endpoint">{endpoint}</div>\n'
if len(unique_endpoints) > 100:
html_content += f' <p><em>... and {len(unique_endpoints) - 100} more endpoints</em></p>\n'
html_content += """
</div>
</div>
<div class="section">
<h2>Unique Parameters <button class="toggle-btn" onclick="toggleSection('parameters')">Toggle</button></h2>
<div id="parameters">
"""
for param in unique_parameters[:50]: # Show first 50
html_content += f' <div class="parameter">{param}</div>\n'
if len(unique_parameters) > 50:
html_content += f' <p><em>... and {len(unique_parameters) - 50} more parameters</em></p>\n'
html_content += """
</div>
</div>
<div class="section">
<h2>Detailed Results <button class="toggle-btn" onclick="toggleSection('detailed')">Toggle</button></h2>
<div id="detailed">
<table>
<tr>
<th>Source</th>
<th>Type</th>
<th>Endpoints</th>
<th>Parameters</th>
<th>Secrets</th>
<th>Framework</th>
</tr>
"""
for result in self.results:
source = result.get('source', 'Unknown')
source_type = result.get('source_type', 'unknown')
endpoint_count = len(result.get('endpoints', []))
param_count = len(result.get('parameters', []))
secret_count = len(result.get('secrets', []))
frameworks = ', '.join(result.get('analysis', {}).get('framework_detected', []))
html_content += f"""
<tr>
<td>{source}</td>
<td>{source_type}</td>
<td>{endpoint_count}</td>
<td>{param_count}</td>
<td>{secret_count}</td>
<td>{frameworks}</td>
</tr>
"""
html_content += """
</table>
</div>
</div>
</body>
</html>
"""
# Save HTML report
with open(output_file, 'w') as f:
f.write(html_content)
print(f"HTML report generated: {output_file}")
return output_file
def generate_json_report(self, output_file='linkfinder_report.json'):
"""Generate JSON report with all findings"""
# Calculate statistics
total_endpoints = sum(len(r.get('endpoints', [])) for r in self.results)
total_parameters = sum(len(r.get('parameters', [])) for r in self.results)
total_secrets = sum(len(r.get('secrets', [])) for r in self.results)
# Aggregate unique findings
all_endpoints = []
all_parameters = []
all_domains = []
for result in self.results:
all_endpoints.extend(result.get('endpoints', []))
all_parameters.extend(result.get('parameters', []))
all_domains.extend(result.get('domains', []))
unique_endpoints = sorted(list(set(all_endpoints)))
unique_parameters = sorted(list(set(all_parameters)))
unique_domains = sorted(list(set(all_domains)))
report = {
'scan_summary': {
'total_files': len(self.results),
'scan_date': time.strftime('%Y-%m-%d %H:%M:%S'),
'total_endpoints': total_endpoints,
'unique_endpoints': len(unique_endpoints),
'total_parameters': total_parameters,
'unique_parameters': len(unique_parameters),
'total_secrets': total_secrets,
'unique_domains': len(unique_domains)
},
'unique_findings': {
'endpoints': unique_endpoints,
'parameters': unique_parameters,
'domains': unique_domains
},
'detailed_results': self.results
}
# Save JSON report
with open(output_file, 'w') as f:
json.dump(report, f, indent=2)
print(f"JSON report generated: {output_file}")
return output_file
# Usage example
if __name__ == "__main__":
# Create LinkFinder instance
linkfinder = AdvancedLinkFinder()
# Analyze single file
result = linkfinder.analyze_javascript_file('example.js', beautify=True)
print(json.dumps(result, indent=2))
# Bulk analysis
targets = [
'https://example.com/script1.js',
'https://example.com/script2.js',
'local_script.js'
]
results = linkfinder.bulk_analyze(targets, max_workers=5, beautify=True)
# Generate reports
linkfinder.generate_html_report('comprehensive_linkfinder_report.html')
linkfinder.generate_json_report('comprehensive_linkfinder_report.json')
Automated Endpoint Discovery
#!/bin/bash
# Comprehensive endpoint discovery using LinkFinder
TARGET_DOMAIN="$1"
OUTPUT_DIR="$2"
if [ -z "$TARGET_DOMAIN" ] || [ -z "$OUTPUT_DIR" ]; then
echo "Usage: $0 <target_domain> <output_dir>"
exit 1
fi
echo "Starting comprehensive endpoint discovery for: $TARGET_DOMAIN"
echo "Output directory: $OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
# Step 1: Discover JavaScript files using multiple methods
echo "Step 1: Discovering JavaScript files..."
# Method 1: Wayback Machine
echo "Collecting JS files from Wayback Machine..."
echo "$TARGET_DOMAIN" | waybackurls | grep -E "\\.js(\$|\\?)" | sort -u > "$OUTPUT_DIR/wayback_js.txt"
# Method 2: Common crawling
echo "Collecting JS files with gau..."
echo "$TARGET_DOMAIN" | gau | grep -E "\\.js(\$|\\?)" | sort -u > "$OUTPUT_DIR/gau_js.txt"
# Method 3: Hakrawler
echo "Collecting JS files with hakrawler..."
echo "https://$TARGET_DOMAIN" | hakrawler -js -depth 3 | sort -u > "$OUTPUT_DIR/hakrawler_js.txt"
# Method 4: Subfinder + httpx + JS discovery
echo "Discovering subdomains and their JS files..."
subfinder -d "$TARGET_DOMAIN" -silent | httpx -silent | while read url; do
curl -s "$url" | grep -oE 'src="[^"]*\\.js[^"]*"' | sed 's/src="//g' | sed 's/"//g' | while read js_path; do
if [[ "$js_path" == /* ]]; then
echo "$url$js_path"
elif [[ "$js_path" == http* ]]; then
echo "$js_path"
else
echo "$url/$js_path"
fi
done
done | sort -u > "$OUTPUT_DIR/subdomain_js.txt"
# Method 5: Directory bruteforcing for common JS files
echo "Bruteforcing common JS file paths..."
cat > "$OUTPUT_DIR/common_js_paths.txt" << 'EOF'
/js/app.js
/js/main.js
/js/bundle.js
/js/vendor.js
/js/chunk.js
/assets/js/app.js
/assets/js/main.js
/static/js/main.js
/static/js/app.js
/dist/js/app.js
/build/js/main.js
/_next/static/chunks/main.js
/_nuxt/app.js
/wp-content/themes/*/js/main.js
/wp-includes/js/jquery/jquery.js
EOF
while read js_path; do
url="https://$TARGET_DOMAIN$js_path"
if curl -s -I "$url" | grep -q "200 OK"; then
echo "$url"
fi
done < "$OUTPUT_DIR/common_js_paths.txt" > "$OUTPUT_DIR/bruteforce_js.txt"
# Combine all JS file sources
cat "$OUTPUT_DIR"/*_js.txt | sort -u > "$OUTPUT_DIR/all_js_files.txt"
JS_COUNT=$(wc -l < "$OUTPUT_DIR/all_js_files.txt")
echo "Found $JS_COUNT JavaScript files total"
# Step 2: Analyze JavaScript files with LinkFinder
echo "Step 2: Analyzing JavaScript files with LinkFinder..."
mkdir -p "$OUTPUT_DIR/linkfinder_results"
# Process each JS file
counter=0
while read -r js_url; do
if [ -n "$js_url" ]; then
counter=$((counter + 1))
echo "[$counter/$JS_COUNT] Analyzing: $js_url"
# Create safe filename
safe_filename=$(echo "$js_url" | sed 's/[^a-zA-Z0-9]/_/g')
# Run LinkFinder
timeout 60 linkfinder -i "$js_url" -o "$OUTPUT_DIR/linkfinder_results/${safe_filename}.html" 2>/dev/null
# Also extract endpoints to text file
timeout 60 linkfinder -i "$js_url" 2>/dev/null | grep -oE 'https?://[^"'\''\\s<>]+' > "$OUTPUT_DIR/linkfinder_results/${safe_filename}_endpoints.txt" 2>/dev/null
timeout 60 linkfinder -i "$js_url" 2>/dev/null | grep -oE '/[^"'\''\\s<>]+' >> "$OUTPUT_DIR/linkfinder_results/${safe_filename}_endpoints.txt" 2>/dev/null
fi
done < "$OUTPUT_DIR/all_js_files.txt"
# Step 3: Aggregate and analyze results
echo "Step 3: Aggregating results..."
# Combine all endpoints
find "$OUTPUT_DIR/linkfinder_results" -name "*_endpoints.txt" -exec cat {} \; | sort -u > "$OUTPUT_DIR/all_endpoints.txt"
# Separate absolute and relative URLs
grep -E '^https?://' "$OUTPUT_DIR/all_endpoints.txt" > "$OUTPUT_DIR/absolute_endpoints.txt"
grep -E '^/' "$OUTPUT_DIR/all_endpoints.txt" | grep -v '^//' > "$OUTPUT_DIR/relative_endpoints.txt"
# Extract API endpoints
grep -iE '(api|endpoint|/v[0-9]|graphql|rest)' "$OUTPUT_DIR/all_endpoints.txt" > "$OUTPUT_DIR/api_endpoints.txt"
# Extract admin/sensitive endpoints
grep -iE '(admin|dashboard|panel|config|debug|test|dev|staging)' "$OUTPUT_DIR/all_endpoints.txt" > "$OUTPUT_DIR/sensitive_endpoints.txt"
# Extract file extensions
grep -oE '\\.[a-zA-Z0-9]+(\$|\\?)' "$OUTPUT_DIR/all_endpoints.txt" | cut -d'?' -f1 | sort | uniq -c | sort -nr > "$OUTPUT_DIR/file_extensions.txt"
# Extract parameters
grep -oE '\\?[^\\s"'\''<>]+' "$OUTPUT_DIR/all_endpoints.txt" | sed 's/?//' | sed 's/&/\\n/g' | cut -d'=' -f1 | sort -u > "$OUTPUT_DIR/parameters.txt"
# Step 4: Validate endpoints
echo "Step 4: Validating endpoints..."
# Test relative endpoints against target domain
mkdir -p "$OUTPUT_DIR/validated"
echo "Testing relative endpoints..."
head -100 "$OUTPUT_DIR/relative_endpoints.txt" | while read endpoint; do
if [ -n "$endpoint" ]; then
test_url="https://$TARGET_DOMAIN$endpoint"
status_code=$(curl -s -o /dev/null -w "%{http_code}" "$test_url" --max-time 10)
if [ "$status_code" = "200" ]; then
echo "$test_url" >> "$OUTPUT_DIR/validated/live_endpoints.txt"
echo "✓ $test_url (200)"
elif [ "$status_code" = "403" ] || [ "$status_code" = "401" ]; then
echo "$test_url" >> "$OUTPUT_DIR/validated/protected_endpoints.txt"
echo "⚠ $test_url ($status_code)"
fi
fi
done
# Test absolute endpoints
echo "Testing absolute endpoints..."
head -50 "$OUTPUT_DIR/absolute_endpoints.txt" | while read endpoint; do
if [ -n "$endpoint" ]; then
status_code=$(curl -s -o /dev/null -w "%{http_code}" "$endpoint" --max-time 10)
if [ "$status_code" = "200" ]; then
echo "$endpoint" >> "$OUTPUT_DIR/validated/live_absolute_endpoints.txt"
echo "✓ $endpoint (200)"
fi
fi
done
# Step 5: Generate comprehensive report
echo "Step 5: Generating comprehensive report..."
TOTAL_ENDPOINTS=$(wc -l < "$OUTPUT_DIR/all_endpoints.txt")
ABSOLUTE_COUNT=$(wc -l < "$OUTPUT_DIR/absolute_endpoints.txt")
RELATIVE_COUNT=$(wc -l < "$OUTPUT_DIR/relative_endpoints.txt")
API_COUNT=$(wc -l < "$OUTPUT_DIR/api_endpoints.txt")
SENSITIVE_COUNT=$(wc -l < "$OUTPUT_DIR/sensitive_endpoints.txt")
PARAM_COUNT=$(wc -l < "$OUTPUT_DIR/parameters.txt")
LIVE_COUNT=$(wc -l < "$OUTPUT_DIR/validated/live_endpoints.txt" 2>/dev/null || echo "0")
PROTECTED_COUNT=$(wc -l < "$OUTPUT_DIR/validated/protected_endpoints.txt" 2>/dev/null || echo "0")
cat > "$OUTPUT_DIR/discovery_report.txt" << EOF
LinkFinder Endpoint Discovery Report
===================================
Target Domain: $TARGET_DOMAIN
Discovery Date: $(date)
Analysis Duration: $(date -d@$SECONDS -u +%H:%M:%S)
JavaScript Files Analyzed: $JS_COUNT files
Endpoint Discovery Results:
- Total Endpoints Found: $TOTAL_ENDPOINTS
- Absolute URLs: $ABSOLUTE_COUNT
- Relative Paths: $RELATIVE_COUNT
- API Endpoints: $API_COUNT
- Sensitive Endpoints: $SENSITIVE_COUNT
- Unique Parameters: $PARAM_COUNT
Validation Results:
- Live Endpoints (200): $LIVE_COUNT
- Protected Endpoints (401/403): $PROTECTED_COUNT
Top 10 File Extensions:
$(head -10 "$OUTPUT_DIR/file_extensions.txt")
Top 20 Endpoints:
$(head -20 "$OUTPUT_DIR/all_endpoints.txt")
Top 20 Parameters:
$(head -20 "$OUTPUT_DIR/parameters.txt")
Sensitive Endpoints Found:
$(head -10 "$OUTPUT_DIR/sensitive_endpoints.txt")
API Endpoints Found:
$(head -10 "$OUTPUT_DIR/api_endpoints.txt")
EOF
# Create summary files for easy access
echo "Creating summary files..."
# High-value targets
cat > "$OUTPUT_DIR/high_value_targets.txt" << EOF
# High-Value Targets for Manual Testing
## Live Endpoints (Confirmed 200 responses)
$(cat "$OUTPUT_DIR/validated/live_endpoints.txt" 2>/dev/null || echo "None validated")
## Protected Endpoints (401/403 - potential bypass targets)
$(cat "$OUTPUT_DIR/validated/protected_endpoints.txt" 2>/dev/null || echo "None found")
## API Endpoints (potential for injection/logic flaws)
$(head -20 "$OUTPUT_DIR/api_endpoints.txt")
## Sensitive Endpoints (admin/debug/config paths)
$(head -20 "$OUTPUT_DIR/sensitive_endpoints.txt")
## Interesting Parameters (potential for parameter pollution/injection)
$(head -30 "$OUTPUT_DIR/parameters.txt")
EOF
echo "Discovery completed successfully!"
echo ""
echo "📊 Summary:"
echo " JavaScript files analyzed: $JS_COUNT"
echo " Total endpoints discovered: $TOTAL_ENDPOINTS"
echo " API endpoints: $API_COUNT"
echo " Sensitive endpoints: $SENSITIVE_COUNT"
echo " Live endpoints validated: $LIVE_COUNT"
echo ""
echo "📁 Output files:"
echo " Main report: $OUTPUT_DIR/discovery_report.txt"
echo " All endpoints: $OUTPUT_DIR/all_endpoints.txt"
echo " High-value targets: $OUTPUT_DIR/high_value_targets.txt"
echo " LinkFinder HTML reports: $OUTPUT_DIR/linkfinder_results/"
echo ""
echo "🎯 Next steps:"
echo " 1. Review high-value targets for manual testing"
echo " 2. Test API endpoints for injection vulnerabilities"
echo " 3. Attempt to access protected endpoints"
echo " 4. Fuzz parameters for additional attack vectors"
Integration with Security Tools
Burp Suite Extension
#!/usr/bin/env python3
# Burp Suite extension for LinkFinder integration
from burp import IBurpExtender, IHttpListener, ITab, IContextMenuFactory, IContextMenuInvocation
from javax.swing import JPanel, JButton, JTextArea, JScrollPane, JLabel, JTextField
from java.awt import BorderLayout, GridLayout, FlowLayout
from java.awt.event import ActionListener
import json
import re
import threading
class BurpExtender(IBurpExtender, IHttpListener, ITab, IContextMenuFactory):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("LinkFinder Integration")
callbacks.registerHttpListener(self)
callbacks.registerContextMenuFactory(self)
# Create UI
self._create_ui()
callbacks.addSuiteTab(self)
# Storage for results
self._results = []
print("LinkFinder Integration loaded successfully")
def _create_ui(self):
self._main_panel = JPanel(BorderLayout())
# Control panel
control_panel = JPanel(GridLayout(3, 2))
# Auto-analysis toggle
self._auto_analyze = JButton("Enable Auto-Analysis", actionPerformed=self._toggle_auto_analysis)
self._auto_enabled = False
# Manual analysis button
self._analyze_button = JButton("Analyze Selected", actionPerformed=self._analyze_selected)
# Clear results button
self._clear_button = JButton("Clear Results", actionPerformed=self._clear_results)
# Export results button
self._export_button = JButton("Export Results", actionPerformed=self._export_results)
# URL filter
self._url_filter = JTextField("Filter URLs (regex)")
# Status label
self._status_label = JLabel("Ready")
control_panel.add(self._auto_analyze)
control_panel.add(self._analyze_button)
control_panel.add(self._clear_button)
control_panel.add(self._export_button)
control_panel.add(JLabel("URL Filter:"))
control_panel.add(self._url_filter)
# Results area
self._results_area = JTextArea(25, 100)
self._results_area.setEditable(False)
results_scroll = JScrollPane(self._results_area)
# Status panel
status_panel = JPanel(FlowLayout())
status_panel.add(self._status_label)
# Add components
self._main_panel.add(control_panel, BorderLayout.NORTH)
self._main_panel.add(results_scroll, BorderLayout.CENTER)
self._main_panel.add(status_panel, BorderLayout.SOUTH)
def getTabCaption(self):
return "LinkFinder"
def getUiComponent(self):
return self._main_panel
def createMenuItems(self, invocation):
# Context menu for right-click analysis
menu_items = []
if invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST or \
invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE:
menu_item = JMenuItem("Analyze with LinkFinder", actionPerformed=lambda x: self._analyze_context_menu(invocation))
menu_items.append(menu_item)
return menu_items
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
# Auto-analyze JavaScript responses if enabled
if self._auto_enabled and not messageIsRequest:
response = messageInfo.getResponse()
if response:
response_info = self._helpers.analyzeResponse(response)
headers = response_info.getHeaders()
# Check if response is JavaScript
content_type = ""
for header in headers:
if header.lower().startswith("content-type:"):
content_type = header.lower()
break
if "javascript" in content_type or "application/js" in content_type:
# Check URL filter
url = str(messageInfo.getUrl())
url_filter = self._url_filter.getText()
if not url_filter or re.search(url_filter, url):
# Analyze in background thread
thread = threading.Thread(target=self._analyze_response, args=(messageInfo,))
thread.daemon = True
thread.start()
def _toggle_auto_analysis(self, event):
self._auto_enabled = not self._auto_enabled
if self._auto_enabled:
self._auto_analyze.setText("Disable Auto-Analysis")
self._status_label.setText("Auto-analysis enabled")
else:
self._auto_analyze.setText("Enable Auto-Analysis")
self._status_label.setText("Auto-analysis disabled")
def _analyze_selected(self, event):
# Analyze selected request/response
selected_messages = self._callbacks.getSelectedMessages()
if not selected_messages:
self._status_label.setText("No request/response selected")
return
self._status_label.setText("Analyzing selected messages...")
# Analyze in background thread
thread = threading.Thread(target=self._analyze_messages, args=(selected_messages,))
thread.daemon = True
thread.start()
def _analyze_context_menu(self, invocation):
# Analyze from context menu
messages = invocation.getSelectedMessages()
if messages:
thread = threading.Thread(target=self._analyze_messages, args=(messages,))
thread.daemon = True
thread.start()
def _analyze_messages(self, messages):
for message in messages:
self._analyze_response(message)
self._status_label.setText(f"Analysis complete. {len(self._results)} total results.")
def _analyze_response(self, messageInfo):
"""Analyze HTTP response for JavaScript endpoints"""
try:
response = messageInfo.getResponse()
if not response:
return
response_info = self._helpers.analyzeResponse(response)
body_offset = response_info.getBodyOffset()
body = response[body_offset:].tostring()
url = str(messageInfo.getUrl())
# Parse JavaScript content
results = self._parse_javascript_content(body)
if results['endpoints'] or results['parameters'] or results['secrets']:
result_entry = {
'url': url,
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
'endpoints': results['endpoints'],
'parameters': results['parameters'],
'secrets': results['secrets'],
'domains': results['domains']
}
self._results.append(result_entry)
self._add_results_to_ui(url, results)
except Exception as e:
print(f"Error analyzing response: {e}")
def _parse_javascript_content(self, content):
"""Parse JavaScript content using LinkFinder patterns"""
results = {
'endpoints': [],
'parameters': [],
'secrets': [],
'domains': []
}
# LinkFinder endpoint pattern
endpoint_pattern = r'(?:"|\')\s*(((?:[a-zA-Z]{1,10}://|//)[^"\'/]{1,}\.[a-zA-Z]{2,}[^"\']{0,})|((?:/|\.\.\/|\.\/)[^"\'><,;| *()(%%$^/\\\\\\[\\]][^"\'><,;|()]{1,})|([a-zA-Z0-9_\\-/]{1,}/[a-zA-Z0-9_\\-/]{1,}\.(?:[a-zA-Z]{1,4}|action)(?:[\\?|#][^"|\']{0,}|))|([a-zA-Z0-9_\\-/]{1,}/[a-zA-Z0-9_\\-/]{3,}(?:[\\?|#][^"|\']{0,}|))|([a-zA-Z0-9_\\-]{1,}\.(?:php|asp|aspx|jsp|json|action|html|js|txt|xml)(?:[\\?|#][^"|\']{0,}|)))\s*(?:"|\')'
# Additional patterns
additional_patterns = [
r'(?:fetch|axios|ajax)\s*\(\s*["\']([^"\']+)["\']',
r'(?:GET|POST|PUT|DELETE|PATCH)\s*["\']([^"\']+)["\']',
r'(?:href|src|action)\s*=\s*["\']([^"\']+)["\']'
]
# Parameter patterns
parameter_patterns = [
r'(?:\\?|&)([a-zA-Z0-9_\\-]+)=([^&\\s"\']+)',
r'(?:param|parameter|arg)\s*:\s*["\']([^"\']+)["\']'
]
# Secret patterns
secret_patterns = [
r'(?i)(api[_\\-]?key|apikey)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{10,})["\']',
r'(?i)(secret|password|pwd)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{8,})["\']',
r'(?i)(token|auth)\s*[:=]\s*["\']([a-zA-Z0-9_\\-]{10,})["\']'
]
# Domain patterns
domain_patterns = [
r'(?:https?://)?([a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,})',
r'(?:"|\')\s*([a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,})\s*(?:"|\')'
]
# Extract endpoints
all_endpoint_patterns = [endpoint_pattern] + additional_patterns
for pattern in all_endpoint_patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
for match in matches:
if isinstance(match, tuple):
match = next((m for m in match if m), match[0])
if match and match not in results['endpoints']:
# Clean the match
match = match.strip('\'"')
if len(match) > 1 and not any(char in match for char in ['<', '>', '{', '}', '\n']):
results['endpoints'].append(match)
# Extract parameters
for pattern in parameter_patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
for match in matches:
if isinstance(match, tuple):
param = match[0] if match[0] else match[1] if len(match) > 1 else ''
else:
param = match
if param and param not in results['parameters']:
results['parameters'].append(param)
# Extract secrets
for pattern in secret_patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
for match in matches:
if isinstance(match, tuple) and len(match) >= 2:
secret_info = f"{match[0]}: {match[1]}"
if secret_info not in results['secrets']:
results['secrets'].append(secret_info)
# Extract domains
for pattern in domain_patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.IGNORECASE)
for match in matches:
if match and match not in results['domains']:
results['domains'].append(match)
return results
def _add_results_to_ui(self, source_url, results):
"""Add parsing results to the UI"""
self._results_area.append(f"\\n=== Analysis: {source_url} ===\\n")
self._results_area.append(f"Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S')}\\n")
if results['endpoints']:
self._results_area.append(f"\\nEndpoints ({len(results['endpoints'])}):
")
for endpoint in results['endpoints'][:15]: # Show first 15
self._results_area.append(f" • {endpoint}\\n")
if len(results['endpoints']) > 15:
self._results_area.append(f" ... and {len(results['endpoints']) - 15} more\\n")
if results['parameters']:
self._results_area.append(f"\\nParameters ({len(results['parameters'])}):
")
for param in results['parameters'][:10]: # Show first 10
self._results_area.append(f" • {param}\\n")
if len(results['parameters']) > 10:
self._results_area.append(f" ... and {len(results['parameters']) - 10} more\\n")
if results['secrets']:
self._results_area.append(f"\\nSecrets ({len(results['secrets'])}):
")
for secret in results['secrets']:
self._results_area.append(f" ⚠️ {secret}\\n")
if results['domains']:
self._results_area.append(f"\\nDomains ({len(results['domains'])}):
")
for domain in results['domains'][:10]: # Show first 10
self._results_area.append(f" • {domain}\\n")
self._results_area.append("\\n" + "="*80 + "\\n")
# Auto-scroll to bottom
self._results_area.setCaretPosition(self._results_area.getDocument().getLength())
def _clear_results(self, event):
self._results_area.setText("")
self._results = []
self._status_label.setText("Results cleared")
def _export_results(self, event):
"""Export results to JSON file"""
if not self._results:
self._status_label.setText("No results to export")
return
try:
# Create export data
export_data = {
'export_timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
'total_analyses': len(self._results),
'results': self._results
}
# Save to file (in Burp's working directory)
filename = f"linkfinder_export_{int(time.time())}.json"
with open(filename, 'w') as f:
json.dump(export_data, f, indent=2)
self._status_label.setText(f"Results exported to {filename}")
except Exception as e:
self._status_label.setText(f"Export failed: {e}")
Performance Optimization and Troubleshooting
Performance Tuning
# Optimize LinkFinder for different scenarios
# Fast analysis with minimal output
linkfinder -i script.js --no-beautify
# Memory-efficient analysis for large files
linkfinder -i large_script.js --timeout 30
# Parallel processing for multiple files
find /path/to/js/files -name "*.js" | xargs -P 10 -I {} linkfinder -i {}
# Network-optimized analysis
linkfinder -i https://example.com/script.js --timeout 60 --retries 3
# Performance monitoring script
#!/bin/bash
monitor_linkfinder_performance() {
local target="$1"
local output_file="linkfinder-performance-$(date +%s).log"
echo "Monitoring LinkFinder performance for: $target"
# Start monitoring
{
echo "Timestamp,CPU%,Memory(MB),Status"
while true; do
if pgrep -f "linkfinder" > /dev/null; then
local cpu=$(ps -p $(pgrep -f "linkfinder") -o %cpu --no-headers)
local mem=$(ps -p $(pgrep -f "linkfinder") -o rss --no-headers | awk '{print $1/1024}')
echo "$(date +%s),$cpu,$mem,running"
fi
sleep 1
done
} > "$output_file" &
local monitor_pid=$!
# Run LinkFinder
time linkfinder -i "$target" -o performance_test.html
# Stop monitoring
kill $monitor_pid 2>/dev/null
echo "Performance monitoring completed: $output_file"
}
# Usage
monitor_linkfinder_performance "https://example.com/script.js"
Troubleshooting Common Issues
# Troubleshooting script for LinkFinder
troubleshoot_linkfinder() {
echo "LinkFinder Troubleshooting Guide"
echo "==============================="
# Check if Python is installed
if ! command -v python3 &> /dev/null; then
echo "❌ Python 3 not found"
echo "Solution: Install Python 3"
return 1
fi
echo "✅ Python 3 found: $(python3 --version)"
# Check if LinkFinder is available
if ! command -v linkfinder &> /dev/null && [ ! -f "linkfinder.py" ]; then
echo "❌ LinkFinder not found"
echo "Solution: Clone LinkFinder repository"
return 1
fi
echo "✅ LinkFinder found"
# Check Python dependencies
local required_modules=("requests" "argparse" "jsbeautifier")
for module in "${required_modules[@]}"; do
if python3 -c "import $module" 2>/dev/null; then
echo "✅ Python module '$module': OK"
else
echo "❌ Python module '$module': Missing"
echo "Solution: pip install $module"
fi
done
# Check network connectivity
if ! curl -s --connect-timeout 5 https://httpbin.org/get > /dev/null; then
echo "❌ Network connectivity issues"
echo "Solution: Check internet connection"
return 1
fi
echo "✅ Network connectivity OK"
# Test basic functionality
echo "Testing basic LinkFinder functionality..."
# Create test JavaScript file
cat > test_script.js << 'EOF'
var apiEndpoint = "/api/users";
var secretKey = "abc123def456";
fetch("/api/data");
window.location.href = "/dashboard";
EOF
if linkfinder -i test_script.js > /dev/null 2>&1; then
echo "✅ Basic functionality test passed"
else
echo "❌ Basic functionality test failed"
echo "Solution: Check LinkFinder installation and dependencies"
fi
# Clean up
rm -f test_script.js
echo "Troubleshooting completed"
}
# Common error solutions
fix_common_linkfinder_errors() {
echo "Common LinkFinder Error Solutions"
echo "================================"
echo "1. 'ModuleNotFoundError: No module named ...'"
echo " Solution: pip install -r requirements.txt"
echo " Alternative: pip install requests jsbeautifier"
echo ""
echo "2. 'Permission denied' when running linkfinder"
echo " Solution: chmod +x linkfinder.py"
echo " Alternative: python3 linkfinder.py [options]"
echo ""
echo "3. 'Connection timeout' or network errors"
echo " Solution: Increase timeout with --timeout option"
echo " Example: linkfinder -i <url> --timeout 60"
echo ""
echo "4. 'No endpoints found' (false negatives)"
echo " Solution: Try beautifying JavaScript first"
echo " Example: linkfinder -i <file> -b"
echo ""
echo "5. 'Memory error' for large files"
echo " Solution: Process files in smaller chunks"
echo " Alternative: Increase system memory"
echo ""
echo "6. 'SSL certificate error'"
echo " Solution: Use --no-ssl-verify option (not recommended for production)"
echo ""
echo "7. 'Too many false positives'"
echo " Solution: Use custom regex patterns or post-processing filters"
echo " Example: Filter results based on file extensions or paths"
echo ""
echo "8. 'HTML output not generated'"
echo " Solution: Check output directory permissions"
echo " Alternative: Specify full output path"
}
# Run troubleshooting
troubleshoot_linkfinder
fix_common_linkfinder_errors
Resources and Documentation
Official Resources
- LinkFinder GitHub Repository - Main repository and source code
- Original Blog Post - Creator's explanation and methodology
- Bug Bounty Methodology - Integration with reconnaissance workflows
Community Resources
- Bug Bounty Community - Discord community for discussions
- OWASP Testing Guide - Web application testing methodologies
- Hacker101 - Educational resources for web security
Integration Examples
- Automation Scripts - Bug bounty hunting toolkit
- Reconnaissance Workflows - Comprehensive recon automation
- JavaScript Analysis Tools - Related tools and techniques
- Burp Suite Extensions - Security testing tool integrations