@@ -897,6 +897,187 @@ defending against and the gaps we knowingly carry.
897897
898898---
899899
900+ ## Attack Surface Visibility — Port & Service Exposure
901+
902+ Companion to the vulnerability scanning argus already does
903+ (Trivy / Grype find CVEs in installed packages, Bandit / Opengrep
904+ find SAST issues in source). This section tracks features that
905+ report * attack surface* — what network endpoints an image
906+ declares — separate from whether those endpoints have known CVEs.
907+ "Image exposes 6379/tcp" is a different question from "image has
908+ a vulnerable Redis package" and most security reviewers want both.
909+
910+ ### Container image exposed ports — actionable
911+
912+ - [ ] ** Surface declared EXPOSE ports as findings.** Extend the
913+ existing ` argus/scanners/container.py ` sub-scanner flow
914+ (alongside trivy / grype / syft). No new scanner module — the
915+ data is already free: ` docker inspect <image> ` returns
916+ ` Config.ExposedPorts ` and the container scanner already inspects
917+ every image it scans.
918+
919+ ** Output shape:** one ` Finding ` per declared port:
920+ ```
921+ INFO EXPOSE-8080 Port 8080/tcp declared exposed
922+ WARN EXPOSE-22 Port 22/tcp (SSH) declared exposed — review necessity
923+ WARN EXPOSE-3306 Port 3306/tcp (MySQL) declared exposed — review necessity
924+ ```
925+ Severity defaults to ` INFO ` for ordinary application ports and
926+ ` WARN ` for ports on a built-in risky-defaults list (services that
927+ shouldn't normally be exposed from a container image without a
928+ deliberate reason). The list is config-overridable so teams can
929+ tune it for their context.
930+
931+ ** Built-in risky-port defaults (warn):**
932+ - ` 21/tcp ` (FTP), ` 22/tcp ` (SSH), ` 23/tcp ` (Telnet)
933+ - ` 25/tcp ` (SMTP), ` 110/tcp ` (POP3), ` 143/tcp ` (IMAP)
934+ - ` 161/udp ` (SNMP), ` 389/tcp ` (LDAP), ` 445/tcp ` (SMB)
935+ - ` 3306/tcp ` (MySQL), ` 3389/tcp ` (RDP)
936+ - ` 5432/tcp ` (PostgreSQL), ` 6379/tcp ` (Redis)
937+ - ` 9200/tcp ` (Elasticsearch), ` 11211/tcp ` (Memcached)
938+ - ` 27017/tcp ` (MongoDB)
939+ Source: services that historically ship with weak defaults or
940+ that fronting via a container without auth/TLS in front of them
941+ is a recurring incident pattern. Each entry cites a "why" in the
942+ scanner module's docstring so future contributors don't tune the
943+ list blindly.
944+
945+ ** Config knob** (in ` argus.yml ` ):
946+ ``` yaml
947+ scanners :
948+ container :
949+ expose_warn_ports : # override the built-in WARN list
950+ - 22/tcp
951+ - 6379/tcp
952+ expose_ignore_ports : # don't emit a finding at all
953+ - 8080/tcp
954+ - 443/tcp
955+ ` ` `
956+
957+ **Why findings (not metadata):** flows through the existing
958+ reporter pipeline (terminal table, markdown, SARIF, JSON, GitHub
959+ annotations, GitLab Code Quality, JUnit), ` --severity-threshold`
960+ works on them, audit-trail captures them, and the
961+ ` argus view terminal` / `argus view browser` UIs render them
962+ alongside CVE findings without per-reporter custom code.
963+
964+ **Implementation tasks** (single PR, no new dependencies):
965+ - [ ] `argus/scanners/container.py` : new `_scan_exposed_ports`
966+ sub-method that reads `Config.ExposedPorts` from the existing
967+ ` docker inspect` output. Emits one `Finding` per port with
968+ ` id=f"EXPOSE-{port}"` , `scanner="container"`,
969+ `metadata={"port" : ..., "protocol": ..., "common_service": ...}`.
970+ - [ ] Built-in `RISKY_PORTS : dict[tuple[int, str], str]` mapping
971+ ` (port, protocol) -> service_name` with the WARN-list above;
972+ cited entries in the docstring.
973+ - [ ] Config schema additions : ` expose_warn_ports` and
974+ ` expose_ignore_ports` (both `list[str]`, parsed as `"port/proto"`).
975+ Validator errors on malformed entries.
976+ - [ ] Tests in `argus/tests/scanners/test_container.py` : fixture
977+ with a multi-port `Config.ExposedPorts` blob; assert one finding
978+ per port with correct severity, ignore-list suppresses, override
979+ warn-list changes the severity.
980+ - [ ] Docs : ` docs/scanners.md` container section gets an
981+ " Exposed ports" subsection with config examples;
982+ ` docs/config-reference.md` adds the two new keys.
983+ - [ ] `.ai/architecture.yaml` : container scanner description
984+ updated to mention the new sub-scanner capability.
985+
986+ **Out of scope for this PR:** *runtime* port enumeration (actually
987+ start the container, probe with `nmap` / `ss`). Static `EXPOSE`
988+ data is the bulk of the value at a fraction of the operational
989+ cost. A runtime variant can become a separate roadmap item if
990+ consumer demand surfaces.
991+
992+ # ## OS image port enumeration — research item
993+
994+ Same question as above but for OS-level images : AWS AMIs, Azure VHDs,
995+ GCP disk images, on-prem VMware OVA/VMDK, ISO files, raw disk dumps,
996+ rootfs tarballs. What network endpoints would this image bind on boot?
997+
998+ - [ ] **Research : in-scope, out-of-scope, or wrap-existing?**
999+ Before scoping any implementation, answer three questions :
1000+
1001+ **Is offline OS-image inspection in argus's scope at all?**
1002+ Argus today operates on (a) source-code directories and (b)
1003+ container image references. An OS image is a fundamentally
1004+ different artifact — it's a bootable disk, not a layered
1005+ filesystem manifest. Inspecting it offline typically requires
1006+ one of :
1007+ - **libguestfs / `virt-customize` / `virt-inspect`** — mount the
1008+ disk image, walk the filesystem, read systemd unit files
1009+ (`/etc/systemd/system/*.service`, `/lib/systemd/system/*.service`),
1010+ SysV init scripts, common service configs (sshd_config,
1011+ postgresql.conf, nginx.conf, etc.). Linux-only on the host;
1012+ needs root or libvirt group; libguestfs is a heavy install
1013+ (~hundreds of MB once a guest kernel is included).
1014+ - **Boot-and-inspect** — actually boot the image in a sandbox VM,
1015+ let services start, capture listening sockets via SSH or guest
1016+ agent, tear down. Heaviest path; needs hypervisor (KVM, QEMU,
1017+ or cloud-provider API). Argus's container-or-source-code model
1018+ doesn't extend here cleanly.
1019+ - **Cloud-provider native** — AWS Inspector (AMIs), Azure
1020+ Defender for Cloud, GCP Security Command Center. These run
1021+ server-side, no local install.
1022+
1023+ **Are there existing tools that solve this well enough that argus
1024+ should not reimplement?** Candidates to evaluate :
1025+ - **OpenSCAP + `oscap-vm` / `oscap-docker`** — SCAP content with
1026+ OVAL definitions can audit a running or mounted system; covers
1027+ listening-port checks via STIG/CIS profiles. Output is XCCDF
1028+ XML — would need an argus reporter shim.
1029+ - **Lynis** — system audit tool. Runs against a live root
1030+ filesystem; can chroot into a mounted image. Output is text;
1031+ parser would be needed.
1032+ - **CIS-CAT** — CIS benchmark scanner. Commercial license tier
1033+ needed for production use; OSS version exists but limited.
1034+ - **AWS Inspector v2** — first-class AMI scanning, no install
1035+ required if you're already on AWS. Doesn't help users on
1036+ other clouds or with on-prem images.
1037+ - **Anchore Enterprise** / **Aqua** / **Sysdig Secure** — all
1038+ have on-prem image scanning but are commercial/freemium.
1039+ - **`debootstrap` + chroot + `ss`** — DIY for Linux rootfs
1040+ tarballs only. Possible but argus-specific implementation.
1041+
1042+ **Or is this a "different product" feeling?** The audience for
1043+ OS-image hardening (DevSecOps building golden AMIs, on-prem VM
1044+ templates, FedRAMP-bound infrastructure teams) overlaps with
1045+ argus's audience but the operational model is different :
1046+ - Argus runs in PR CI / dev loops; OS image scans are
1047+ typically pre-release gates on infrastructure-as-code
1048+ pipelines (Packer builds, Terraform deploys).
1049+ - Argus expects sub-minute scan times; OS-image inspection via
1050+ libguestfs is minutes-to-tens-of-minutes per image.
1051+ - Argus's container-scanner model assumes Docker is available;
1052+ OS-image inspection assumes libguestfs / KVM / cloud API
1053+ access, which is a different host requirement.
1054+
1055+ **Specific research deliverables** (one investigation, output is
1056+ a short ADR — not code) :
1057+ - [ ] Inventory of 3-5 actual user requests / use cases for OS
1058+ image inspection. Without concrete demand, this stays
1059+ deferred.
1060+ - [ ] Comparison matrix : argus-native (libguestfs) vs. wrap
1061+ OpenSCAP vs. defer to AWS Inspector vs. out-of-scope.
1062+ Dimensions : install footprint, host OS requirements, supported
1063+ image formats, scan latency, license cost, reporting fidelity.
1064+ - [ ] Scope decision in `.ai/decisions.yaml` (likely ADR-025 or
1065+ later) : one of "argus native", "argus wraps OpenSCAP", "argus
1066+ out of scope; recommend X", "deferred until more demand".
1067+ - [ ] If "out of scope" : note in `docs/scanners.md` pointing
1068+ users at the right tool for OS-image port enumeration so they
1069+ don't open issues asking for it later.
1070+
1071+ **My strong prior** (to be tested against the research):
1072+ argus stays focused on source-code + container images;
1073+ OS-image inspection is best served by purpose-built tools
1074+ (OpenSCAP for offline, cloud-native scanners for AMIs). The
1075+ argus answer for users would be a documentation pointer plus,
1076+ if compelling, a `docs/security.md` paragraph naming the tools
1077+ we recommend for each cloud + on-prem path.
1078+
1079+ ---
1080+
9001081# # Dependency Maintenance — Full Coverage
9011082
9021083| Dependency Type | Tool | Config Location | Status |
0 commit comments