Skip to content
Open
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
45 changes: 30 additions & 15 deletions bbot/modules/output/stdout.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys
import json

from bbot.logger import log_to_stderr
from bbot.modules.output.base import BaseOutputModule


Expand All @@ -15,12 +15,23 @@ class Stdout(BaseOutputModule):
"in_scope_only": "Whether to only show in-scope events",
"accept_dupes": "Whether to show duplicate events, default True",
}
vuln_severity_map = {
"INFO": "HUGEINFO",
"LOW": "HUGEWARNING",
"MEDIUM": "HUGEWARNING",
"HIGH": "CRITICAL",
"CRITICAL": "CRITICAL",
# base RGB hue per severity (matches the 🟦🟨🟧🟥🟪 convention from FINDING.severity_colors)
# all hues normalized so the brightest channel is 255 — gives a uniform brightness range per row
finding_severity_rgb = {
"INFO": (113, 161, 255), # blue
"LOW": (255, 215, 0), # yellow
"MEDIUM": (255, 135, 0), # orange
"HIGH": (255, 0, 0), # red
"CRITICAL": (207, 0, 255), # purple
}
# brightness multiplier per confidence — CONFIRMED also adds bold
# tuned so even UNKNOWN stays legible on dark terminals (brightest channel ~115)
finding_confidence_brightness = {
"CONFIRMED": 1.00,
"HIGH": 0.82,
"MEDIUM": 0.65,
"LOW": 0.55,
"UNKNOWN": 0.45,
}
format_choices = ["text", "json"]

Expand Down Expand Up @@ -61,16 +72,20 @@ async def handle_text(self, event, event_json):
else:
event_str = self.human_event_str(event)

# log findings in vivid colors based on severity
if event.type == "FINDING":
severity = event.data.get("severity", "INFO")
if severity in self.vuln_severity_map:
loglevel = self.vuln_severity_map[severity]
log_to_stderr(event_str, level=loglevel, logname=False)
else:
log_to_stderr(event_str, level="HUGEINFO", logname=False)
# color findings: severity picks the hue, confidence dims the brightness
if event.type == "FINDING" and isinstance(event.data, dict) and sys.stdout.isatty():
event_str = self._colorize_finding(event_str, event.data)

print(event_str)

def _colorize_finding(self, event_str, data):
severity = str(data.get("severity", "INFO")).upper()
confidence = str(data.get("confidence", "UNKNOWN")).upper()
r, g, b = self.finding_severity_rgb.get(severity, self.finding_severity_rgb["INFO"])
mult = self.finding_confidence_brightness.get(confidence, 0.65)
r, g, b = int(r * mult), int(g * mult), int(b * mult)
bold = "1;" if confidence == "CONFIRMED" else ""
return f"\033[{bold}38;2;{r};{g};{b}m{event_str}\033[0m"

async def handle_json(self, event, event_json):
print(json.dumps(event_json))
Loading