Skip to content

Commit f12ae5e

Browse files
committed
External sites carry owners, environments, repositories and the full telemetry
A site now has executive and technical owners, which must be email addresses, comments, Active or Obsolete, an environment of Prod, Dev, Stage or Clone, whether external auth or a whitelist is needed, and how often it changes in days. Add and Edit both have them, and so does the API. Repositories are picked from a searchable list of every organization, repository and branch the scans have seen. Above it the picker recommends repositories for the hosts being added, from the Web Applications tab: an application that lists the host recommends every repository in it, another environment of a listed host counts too, and the application's owners can be filled in with one press. A scan reads the picked branches for routes. Telemetry adds nameservers, the CNAME chain, IPv6, mail servers, reverse DNS, ASN, netblock and the domain's registrar and dates, all from DNS and whois. Every telemetry field can be filled in or corrected by hand, and a value filled in by hand wins until it is cleared. The site page now opens with a large screenshot and follows the site report's layout, with the security headers and every response header. The list has a Scan button on each site, and scans or deletes the ticked sites. Pages waiting on a scan no longer reload while a dialog is open. A scan that found nothing said the model gave no usable answer when it was triaged, though nothing was sent. It now says there was nothing to read.
1 parent d6029c9 commit f12ae5e

13 files changed

Lines changed: 1560 additions & 124 deletions

‎app/core/api_reference.py‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@ def _f(name, kind, required, description):
408408
_ASSET = {"id": 12, "host": "shop.example.com", "kind": "subdomain",
409409
"parent_domain": "example.com", "application": "Storefront",
410410
"tags": ["ecommerce"], "no_scan": False, "scan_blocked": False,
411+
"executive_owner": "owner@example.com", "technical_owner": "team@example.com",
412+
"comments": "", "status": "active", "environment": "prod",
413+
"external_auth_needed": False, "whitelist_needed": True,
414+
"change_frequency_days": 14,
415+
"fields": {"nameservers": {"value": "ns1.example.net, ns2.example.net",
416+
"scan": "ns1.example.net, ns2.example.net",
417+
"manual": False}},
411418
"telemetry": {"at": "2026-11-03T10:00:00+00:00", "grade": "B",
412419
"category": "live", "status_code": 200, "ip": "203.0.113.10",
413420
"open_ports": [80, 443], "tls_days_left": 61,
@@ -443,6 +450,23 @@ def _f(name, kind, required, description):
443450
]
444451

445452

453+
_DETAILS = [
454+
_f("executive_owner", "string", False, "Email addresses, comma separated."),
455+
_f("technical_owner", "string", False, "Email addresses, comma separated."),
456+
_f("comments", "string", False, "Anything else about the site."),
457+
_f("status", "string", False, "active or obsolete."),
458+
_f("environment", "string", False, "prod, dev, stage or clone."),
459+
_f("external_auth_needed", "boolean", False, "Signing in goes through something outside the site."),
460+
_f("whitelist_needed", "boolean", False, "The scanner has to be let through a firewall."),
461+
_f("change_frequency_days", "integer", False, "How often the site changes, in days."),
462+
_f("repositories", "array", False, "Objects with repository (org/name) and an optional "
463+
"branch. Replaces what was picked before."),
464+
_f("telemetry_fields", "object", False, "Telemetry filled in by hand, by field name: "
465+
"nameservers, isp, asn, registrar, tls_issuer and "
466+
"the rest. null clears one."),
467+
]
468+
469+
446470
def _external_endpoints() -> list:
447471
asset_id = _p("asset_id", "The site's id.", where="path", kind="integer", example=12)
448472
scan_id = _p("scan_id", "The scan's id.", where="path", kind="integer", example=40)
@@ -465,8 +489,8 @@ def _external_endpoints() -> list:
465489
_f("no_scan", "boolean", False, "True when it must never be scanned, "
466490
"a SaaS product for example."),
467491
_f("no_scan_reason", "string", False, "Why not."),
468-
_f("notes", "string", False, "Anything else."),
469-
_f("telemetry", "boolean", False, "False to skip the first read.")],
492+
_f("telemetry", "boolean", False, "False to skip the first read.")]
493+
+ _DETAILS,
470494
body_example={"host": "shop.example.com", "application": "Storefront",
471495
"tags": ["ecommerce"]}, example=_ASSET),
472496
_ext("ext-get", "GET", "/external/assets/{asset_id}", "One site",
@@ -476,9 +500,8 @@ def _external_endpoints() -> list:
476500
"stay as they are.", [asset_id],
477501
body=[_f("application", "string", False, "The application."),
478502
_f("tags", "array", False, "Replaces the tags."),
479-
_f("notes", "string", False, "Notes."),
480503
_f("no_scan", "boolean", False, "Never scan it."),
481-
_f("no_scan_reason", "string", False, "Why not.")],
504+
_f("no_scan_reason", "string", False, "Why not.")] + _DETAILS,
482505
body_example={"no_scan": True, "no_scan_reason": "Vendor hosted"},
483506
example=_ASSET),
484507
_ext("ext-delete", "DELETE", "/external/assets/{asset_id}", "Remove a site",

0 commit comments

Comments
 (0)