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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,17 @@ docksec -i myapp:latest --image-only --baseline .docksec-baseline.json --fail-on
# Reduce output to warnings, errors, and the result summary
docksec Dockerfile --scan-only --quiet

# Enable INFO-level logs for this run
docksec Dockerfile --scan-only --verbose

# Disable colored output (also honors the NO_COLOR env var)
docksec Dockerfile --no-color
```

Every scan ends with a result summary: a severity table, the security score with a
rating, a "Quick take" action block, the generated reports, and a suggested next
command. Use `--quiet` for a compact result and `--no-color` for plain output.
Use `--verbose` or `-v` to force INFO-level logs when needed.

### Machine-readable output

Expand Down
5 changes: 4 additions & 1 deletion docksec/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def main() -> None:
parser.add_argument('--offline', dest='offline', action='store_true', help='Run without network access: use the local Trivy DB (no DB update) and skip AI analysis')
parser.add_argument('--baseline', dest='baseline', metavar='FILE', help='Path to a baseline file; with --fail-on, only findings not present in the baseline trigger the gate')
parser.add_argument('--update-baseline', dest='update_baseline', action='store_true', help='Write the current scan findings to --baseline instead of gating against it')
parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose INFO-level logging for this run')
parser.add_argument('--quiet', action='store_true', help='Reduce output to warnings, errors, and the result summary')
parser.add_argument('--no-color', action='store_true', help='Disable colored output (also honors the NO_COLOR env var)')
parser.add_argument('--version', action='version', version=f'DockSec {get_version()}')
Expand All @@ -93,6 +94,8 @@ def main() -> None:
os.environ["LLM_PROVIDER"] = args.provider
if args.model:
os.environ["LLM_MODEL"] = args.model
if args.verbose and not os.getenv("DOCKSEC_LOG_LEVEL"):
os.environ["DOCKSEC_LOG_LEVEL"] = "INFO"

# Set compact output mode if requested
if args.compact_output:
Expand Down Expand Up @@ -679,4 +682,4 @@ def _suggest_next_command(args, results, run_ai, run_compose_analysis):
return ""

if __name__ == "__main__":
main()
main()
51 changes: 51 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,57 @@ def test_quiet_and_no_color_flags_are_accepted(self):
main()
self.assertNotEqual(ctx.exception.code, 2)

@patch('sys.argv', ['docksec', 'Dockerfile', '--scan-only', '-v'])
@patch('docksec.cli.DockerSecurityScanner', create=True)
def test_verbose_flag_sets_log_level(self, mock_scanner_class):
"""`-v/--verbose` enables INFO logs when no override env var exists."""
from docksec.cli import main

scanner = Mock()
scanner.run_full_scan.return_value = {
"json_data": [],
"scan_mode": "full",
"dockerfile_scan": {"skipped": False, "success": True},
"image_scan": {"skipped": True, "success": True},
"timestamp": "2026-01-01 00:00:00",
"dockerfile": "Dockerfile",
}
scanner.get_security_score.return_value = 88
scanner.generate_all_reports.return_value = {}
scanner.RESULTS_DIR = '/tmp'
mock_scanner_class.return_value = scanner

with patch('builtins.print'):
with patch.dict(os.environ, {}, clear=True):
main()
self.assertEqual(os.environ.get("DOCKSEC_LOG_LEVEL"), "INFO")


@patch('sys.argv', ['docksec', 'Dockerfile', '--scan-only', '-v'])
@patch('docksec.cli.DockerSecurityScanner', create=True)
def test_verbose_respects_existing_log_level(self, mock_scanner_class):
"""`-v` should not override an explicit DOCKSEC_LOG_LEVEL from env."""
from docksec.cli import main

scanner = Mock()
scanner.run_full_scan.return_value = {
"json_data": [],
"scan_mode": "full",
"dockerfile_scan": {"skipped": False, "success": True},
"image_scan": {"skipped": True, "success": True},
"timestamp": "2026-01-01 00:00:00",
"dockerfile": "Dockerfile",
}
scanner.get_security_score.return_value = 88
scanner.generate_all_reports.return_value = {}
scanner.RESULTS_DIR = '/tmp'
mock_scanner_class.return_value = scanner

with patch('builtins.print'):
with patch.dict(os.environ, {"DOCKSEC_LOG_LEVEL": "WARNING"}, clear=True):
main()
self.assertEqual(os.environ.get("DOCKSEC_LOG_LEVEL"), "WARNING")


class TestCLIHelpers(unittest.TestCase):
"""Test cases for the CLI summary helper functions."""
Expand Down