Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

## What is DockSec?

DockSec is an **OWASP Lab Project** that bridges the gap between complex security scan results and actionable developer fixes. It integrates industry-standard scanners (Trivy, Hadolint, Docker Scout) with AI to provide **context-aware security analysis**.
DockSec is an **OWASP Lab Project** that bridges the gap between complex security scan results and actionable developer fixes. It integrates industry-standard scanners (Trivy, Grype, Hadolint, Docker Scout) with AI to provide **context-aware security analysis**.

Instead of overwhelming you with a list of 200+ CVEs, DockSec:

Expand All @@ -50,7 +50,7 @@ Everything scans locally; the only thing that ever leaves your machine is the (s

DockSec follows a four-stage pipeline:

1. **Scan**: Runs Trivy, Hadolint, and Docker Scout locally on your environment.
1. **Scan**: Runs Trivy, Grype, Hadolint, and Docker Scout locally on your environment.
2. **Analyze**: AI correlates findings across all scanners to remove noise and assess real-world impact.
3. **Recommend**: Generates human-readable explanations and specific remediation steps.
4. **Report**: Exports actionable results as HTML, PDF, JSON, CSV, SARIF, and CycloneDX SBOM.
Expand All @@ -67,10 +67,11 @@ DockSec orchestrates local scanners, so it needs:
|---|---|---|
| Python 3.12+ | DockSec itself | [python.org](https://www.python.org/downloads/) |
| Trivy | All scans (required) | `brew install trivy` or [Trivy docs](https://trivy.dev/latest/getting-started/installation/) |
| Grype | Vulnerability scanning (optional) | `brew install anchore/grype/grype` or [Grype docs](https://github.com/anchore/grype#installation) |
| Hadolint | Dockerfile linting | `brew install hadolint` or [Hadolint docs](https://github.com/hadolint/hadolint#install) |
| Docker | Image scans (`-i`) | [Docker docs](https://docs.docker.com/get-docker/) |

Or let DockSec install Trivy and Hadolint for you:
Or let DockSec install Trivy, Grype, and Hadolint for you:

```bash
python -m docksec.setup_external_tools
Expand Down Expand Up @@ -179,6 +180,12 @@ docksec Dockerfile --scan-only --sarif
# Write a CycloneDX SBOM of an image for supply-chain tooling
docksec --image-only -i myapp:latest --sbom

# Use Grype instead of Trivy for vulnerability scanning
docksec -i myapp:latest --image-only --scanner grype

# Run both Trivy and Grype, deduplicate findings
docksec -i myapp:latest --image-only --scanner all

# Fully offline scan: local Trivy DB, no network, no AI
docksec --image-only -i myapp:latest --offline

Expand Down Expand Up @@ -339,7 +346,7 @@ it is independent of `--format`.

DockSec is designed so you always know what leaves your machine:

- **Scanning is fully local.** Trivy, Hadolint, and the security score run on your
- **Scanning is fully local.** Trivy, Grype, Hadolint, and the security score run on your
machine. Image contents are never uploaded anywhere by DockSec.
- **AI analysis sends only the scanned file.** When the AI pass runs, the Dockerfile
or compose file content (plus a short summary of vulnerability counts for scoring)
Expand Down Expand Up @@ -403,7 +410,8 @@ command updates the DockSec section in place instead of duplicating it.
- **Multi-LLM Support**: OpenAI, Anthropic Claude, Google Gemini, or local models via Ollama.
- **Privacy First**: Secret values are redacted before any content reaches an AI provider, scanning is fully local, and there is no telemetry.
- **Docker Compose Scanning**: Detect orchestration-level misconfigurations and scan all services in a compose file.
- **Deep Integration**: Combines Trivy (vulnerabilities), Hadolint (linting), and Docker Scout.
- **Multi-Scanner**: Run Trivy, Grype, or both (`--scanner all`) with automatic deduplication by CVE ID.
- **Deep Integration**: Combines Trivy (vulnerabilities), Grype (vulnerabilities), Hadolint (linting), and Docker Scout.
- **Security Scoring**: A 0-100 score with a rating to track your security posture over time.
- **Rich Formats**: HTML (interactive), PDF, JSON, CSV, SARIF, and CycloneDX SBOM.
- **CI/CD Ready**: `--fail-on` exit codes, baseline/ratchet mode, auditable waivers, JSON-to-stdout, and a GitHub Action on the Marketplace.
Expand Down
16 changes: 14 additions & 2 deletions docksec/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ def main() -> None:
parser.add_argument('-v', '--verbose', action='store_true', help='Show INFO-level log lines on stderr')
parser.add_argument('--log-file', dest='log_file', metavar='FILE', help='Also append log lines to FILE, creating missing parent directories; combine with --verbose to capture INFO-level logs')
parser.add_argument('--no-color', action='store_true', help='Disable colored output (also honors the NO_COLOR env var)')
parser.add_argument('--scanner', choices=['trivy', 'grype', 'all'], default=None,
help='Vulnerability scanner to use: trivy (default), grype, or all (both, deduplicated). '
'Can also be set via DOCKSEC_SCANNER environment variable.')
parser.add_argument('--version', action='version', version=f'DockSec {get_version()}')

args = parser.parse_args()
Expand All @@ -93,6 +96,11 @@ def main() -> None:
os.environ["NO_COLOR"] = "1"
output.configure(quiet=args.quiet, no_color=no_color, json_mode=args.json_stdout)

# Resolve --scanner: CLI flag > DOCKSEC_SCANNER env var > default "trivy"
if args.scanner is None:
env_scanner = os.environ.get("DOCKSEC_SCANNER", "trivy").lower()
args.scanner = env_scanner if env_scanner in ("trivy", "grype", "all") else "trivy"

# Set provider and model from CLI args if provided (overrides env vars)
if args.provider:
os.environ["LLM_PROVIDER"] = args.provider
Expand Down Expand Up @@ -280,6 +288,8 @@ def main() -> None:
output.kv("Reports", output_dir)
if run_scan:
output.kv("Severity", severity)
scanner_label = {"trivy": "Trivy", "grype": "Grype", "all": "Trivy + Grype"}.get(args.scanner, args.scanner)
output.kv("Scanner", scanner_label)
if run_ai:
config = get_config()
output.kv("AI Provider", str(config.llm_provider))
Expand Down Expand Up @@ -378,7 +388,8 @@ def main() -> None:
orchestrator = ComposeOrchestrator(
args.compose,
scan_only=not run_ai,
skip_ai_scoring=args.skip_ai_scoring
skip_ai_scoring=args.skip_ai_scoring,
scanner=args.scanner,
)
output.info(f"Scanning Compose file: {args.compose}")
results = orchestrator.run_full_scan(severity)
Expand All @@ -396,7 +407,8 @@ def main() -> None:
results_dir=output_dir,
scan_only=not run_ai,
skip_ai_scoring=args.skip_ai_scoring,
offline=args.offline
offline=args.offline,
scanner=args.scanner,
)

# Run appropriate scan based on mode
Expand Down
6 changes: 4 additions & 2 deletions docksec/compose_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,11 @@ def get_services(self) -> Dict[str, Dict]:
return self.data.get('services', {})

class ComposeOrchestrator:
def __init__(self, compose_path: str, scan_only: bool = False, skip_ai_scoring: bool = False):
def __init__(self, compose_path: str, scan_only: bool = False, skip_ai_scoring: bool = False, scanner: str = "trivy"):
self.compose_path = compose_path
self.scan_only = scan_only
self.skip_ai_scoring = skip_ai_scoring
self.scanner_mode = scanner
self.scanner = ComposeScanner(compose_path)

def run_full_scan(self, severity: str = "CRITICAL,HIGH") -> Dict:
Expand Down Expand Up @@ -430,7 +431,8 @@ def run_full_scan(self, severity: str = "CRITICAL,HIGH") -> Dict:
dockerfile_path=dockerfile_path,
image_name=image_name,
scan_only=self.scan_only,
skip_ai_scoring=self.skip_ai_scoring
skip_ai_scoring=self.skip_ai_scoring,
scanner=self.scanner_mode,
)

# Disable cache for service scans to ensure fresh results?
Expand Down
Loading