"""Secret Scanner CLI - Direct execution module"""
import argparse
import json
import sys
from pathlib import Path
from .scanner import SecretScanner
from .patterns import get_pattern_count
from .utils import mask_secret
def scan_file_cli(file_path: str, profile: str = "balanced"):
"""Scan a single file and print results"""
scanner = SecretScanner()
findings = scanner.scan_file(file_path, profile=profile)
results = []
for finding in findings:
if not finding.is_false_positive:
results.append({
"type": finding.type,
"value": mask_secret(finding.value),
"severity": finding.severity,
"category": finding.category,
"file": finding.file_path,
"line": finding.line_number
})
print(json.dumps({
"success": True,
"file": file_path,
"count": len(results),
"findings": results
}, indent=2))
def scan_directory_cli(directory_path: str, profile: str = "balanced", exclude_patterns=None):
"""Scan a directory and print results"""
scanner = SecretScanner()
result = scanner.scan_directory(directory_path, profile=profile, exclude_patterns=exclude_patterns)
findings_dict = []
for finding in result.get("findings", []):
if not finding.is_false_positive:
findings_dict.append({
"type": finding.type,
"value": mask_secret(finding.value),
"severity": finding.severity,
"category": finding.category,
"file": finding.file_path,
"line": finding.line_number
})
print(json.dumps({
"success": True,
"directory": directory_path,
"count": len(findings_dict),
"findings": findings_dict,
"stats": result.get("stats", {})
}, indent=2))
def scan_content_cli(content: str, file_name: str = "content.txt"):
"""Scan text content and print results"""
scanner = SecretScanner()
findings = scanner.scan_content(content, file_name)
results = []
for finding in findings:
if not finding.is_false_positive:
results.append({
"type": finding.type,
"value": mask_secret(finding.value),
"severity": finding.severity,
"category": finding.category,
"file": file_name,
"line": finding.line_number
})
print(json.dumps({
"success": True,
"file": file_name,
"count": len(results),
"findings": results
}, indent=2))
def main():
"""Main CLI entry point"""
parser = argparse.ArgumentParser(
description="Secret Scanner - Detect exposed API keys and credentials",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=f"""
Examples:
python -m src scan file config.py
python -m src scan directory . --exclude-patterns '.*\\.txt$'
python -m src scan content "AKIAIOSFODNN7EXAMPLE"
Available patterns: {get_pattern_count()}
"""
)
subparsers = parser.add_subparsers(dest='command', help='Available commands')
# Scan file command
file_parser = subparsers.add_parser('scan', help='Scan for secrets')
file_subparsers = file_parser.add_subparsers(dest='scan_type')
# File scanning
file_cmd = file_subparsers.add_parser('file', help='Scan a file')
file_cmd.add_argument('file_path', help='Path to file to scan')
file_cmd.add_argument('--profile', choices=['fast', 'balanced', 'deep'],
default='balanced', help='Scanning profile')
# Directory scanning
dir_cmd = file_subparsers.add_parser('directory', help='Scan a directory')
dir_cmd.add_argument('directory_path', help='Path to directory to scan')
dir_cmd.add_argument('--profile', choices=['fast', 'balanced', 'deep'],
default='balanced', help='Scanning profile')
dir_cmd.add_argument('--exclude-patterns', nargs='*',
help='Regex patterns to exclude files')
# Content scanning
content_cmd = file_subparsers.add_parser('content', help='Scan text content')
content_cmd.add_argument('content', help='Text content to scan')
content_cmd.add_argument('--file-name', default='content.txt',
help='Virtual file name for context')
# Info command
info_cmd = subparsers.add_parser('info', help='Show scanner information')
args = parser.parse_args()
if not args.command:
parser.print_help()
return
try:
if args.command == 'info':
print(f"Secret Scanner")
print(f"Patterns available: {get_pattern_count()}")
print(f"Categories: cloud, AI, payment, auth, database")
elif args.command == 'scan':
if args.scan_type == 'file':
if not Path(args.file_path).exists():
print(f"Error: File '{args.file_path}' not found")
sys.exit(1)
scan_file_cli(args.file_path, args.profile)
elif args.scan_type == 'directory':
if not Path(args.directory_path).exists():
print(f"Error: Directory '{args.directory_path}' not found")
sys.exit(1)
scan_directory_cli(args.directory_path, args.profile, args.exclude_patterns)
elif args.scan_type == 'content':
scan_content_cli(args.content, args.file_name)
else:
parser.print_help()
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()