Skip to content
Merged
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
42 changes: 42 additions & 0 deletions docksec/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,48 @@ def main() -> None:
output.error(f"Scanner failed: {e}")
scan_ok = False

# AI-only report: when AI analysis ran but no scan did (e.g. a Dockerfile
# with no -i image, or --ai-only), the scan block above never generated a
# report. Write the AI findings out here so they're available in full,
# instead of leaving the user with only the truncated on-screen summary.
if run_ai and ai_ok and not run_scan and ai_findings and not args.json_stdout:
try:
from docksec.docker_scanner import DockerSecurityScanner
from datetime import datetime

ai_results = {
"dockerfile_scan": {
"success": True,
"output": "Skipped - AI analysis only",
"skipped": True,
},
"image_scan": {
"success": True,
"output": "Skipped - AI analysis only",
"skipped": True,
},
"json_data": [],
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"image_name": "N/A - AI analysis only",
"dockerfile_path": args.compose if run_compose_analysis else args.dockerfile,
"scan_mode": "compose" if run_compose_analysis else "ai_only",
"ai_findings": ai_findings,
}

ai_scanner = DockerSecurityScanner(
None, None, results_dir=output_dir, scan_only=True,
skip_ai_scoring=args.skip_ai_scoring,
)
report_paths = ai_scanner.generate_all_reports(ai_results, formats=report_formats)
if args.sarif:
report_paths["sarif"] = _generate_sarif_report(ai_scanner, ai_results)

if report_paths:
output.report_results(report_paths, output_dir)
except Exception as e:
output.error(f"Failed to write AI analysis report: {e}")
ai_ok = False

# Exit codes (CI-friendly): 0 clean, 1 findings at/above --fail-on,
# 2 usage error, 3 tool/runtime error.
if not run_ai and not run_scan:
Expand Down
10 changes: 4 additions & 6 deletions docksec/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,10 @@ def analyze_security(response: AnalyzesResponse, compact: bool = True, report_pa
print_section("Exposed Credentials", exposed_credentials, "magenta", max_items)
print_section("Remediation Steps", remediation, "green", max_items)

# Build message with report location if provided
if report_path:
console.print(f"\n[dim]For detailed AI analysis, check the generated reports at: {report_path}[/]")
else:
console.print("\n[dim]For detailed AI analysis, check the generated reports[/]")

# Note: the "reports written" message is emitted by the CLI after reports
# are actually generated (see cli.py), so it isn't printed here where we
# don't yet know whether a report file will be written for this run.

# Return findings for report generation
return {
"vulnerabilities": vulnerabilities,
Expand Down
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2026.7.3] - 2026-07-02

### Fixed

- **AI analysis without an image wrote no report, but claimed it did**: running an AI analysis with a Dockerfile but no image (e.g. `docksec Dockerfile --provider anthropic`, or `--ai-only`) set the tool into a mode where the AI pass ran but the scan pass — the only place reports were generated — did not. No report file was written, yet the tool still printed "For detailed AI analysis, check the generated reports at: ...", pointing at a directory that contained only stale files from previous runs. Since the on-screen findings are truncated to the top few per section, the full AI findings were effectively unreachable. AI-only runs now write the complete findings to a report (JSON/CSV/PDF/HTML, plus SARIF with `--sarif`), honoring `--format` and `--output-dir`, and the "reports written" message is only shown when a report was actually generated.

## [2026.7.2] - 2026-07-02

### Fixed
Expand Down
Loading