ShakerScan 2.10.0
The client for this release is shakerscan 0.10.0 (shakerscan secrets).
Exposure is the service view
The Exposure page now shows only its service view: per target, each listener, the product and
version observed on it, and the weaknesses known for that version. The Triage, Map and
Attack paths views are removed: they restated what the Targets and Findings pages show, and
their rankings were not decisions an operator could act on. An old /exposure?lens=... link opens
the service view.
- Editing a target's owner, environment and reporting cohort moved to the target page
(
/targets/{id}, Edit details). It was only possible from the removed triage drawer. The target detail API (GET /targets/{id}/asset) now returnsownerandcohort. - The dashboard's "What changed" links for new and stale assets open the Targets page (newest first, or scanned targets) instead of the removed triage view; a domain scope becomes the Targets search.
- API:
GET /exposure/nodesandGET /exposure/attack-pathsare removed (only the removed views called them).GET /exposure/graph(an Arsenal read for agents),GET /exposure/assets(the dashboard's posture),GET /exposure/changesandGET /exposure/servicesare unchanged.
Known vulnerabilities per service version
- A real vulnerability snapshot. The pinned snapshot the service view matches against now covers the network and web services the scanner fingerprints (OpenSSH, Apache httpd, nginx, IIS, Tomcat, PHP, MySQL/MariaDB, PostgreSQL, Redis, Elasticsearch, Jenkins, mail, DNS and file servers, common edge appliances, and the embedded products it already covered), generated from NVD at release time. Before, it held 24 curated records, so almost every listener showed no candidates.
- Exploitation context on each candidate: the CVSS base score, whether CISA lists it as known exploited (with the date added and ransomware use), the EPSS likelihood of exploitation in the next 30 days, and the nuclei checks this release ships for that CVE. Candidates are ordered known exploited first, then likelihood, then severity, and each listener shows how many are known exploited. All of it is metadata in the pinned snapshot: nothing is fetched at run time and nothing is executed from it.
- A product named without a CPE is matched when its version is known (for example
nginx 1.18.0from HTTP fingerprinting); a product with no usable version is not. - Less noise: listeners whose probe got no answer and that have nothing linked are folded behind "show N inconclusive" on each target.
- A version match is still a candidate, never a finding: confirm the exact build and its backports, or run the check as part of an authorized Scan.
Model Intake is an optional module
Model Intake (scanning AI models before deployment) ships as two images of about 4.8 GB per release, more than half of what an install downloads. A new install now leaves it off; an existing install keeps it exactly as it was (an install that predates the setting keeps Model Intake on, and nothing is removed by the upgrade).
shakerscan model-intake enablepulls its two images (from the same verified release lock) and starts its services;shakerscan model-intake disablestops and removes them (--remove-imagesalso deletes its images);shakerscan model-intake statusshows the setting. The setting isSHAKERSCAN_MODEL_INTAKE_ENABLEDin~/.shakerscan/.env.- With the module off, the Model Intake page explains what it does and shows the command to install it, and makes no Model Intake request. Its sidebar item is under Show all sidebar sections.
- The API refuses changes to Model Intake (
409,reason_code: module_not_installed, naming the enable command); existing Model Intake records stay readable./healthreportsmodel_intake_worker: {"enabled": false, "status": "disabled"}instead of a missing worker. - Compose: the four Model Intake services are in the
model-intakeprofile; nothing else depends on them.
Hunt can use secrets found earlier on a target
A value a Hunt withholds from its output (a password in a configuration file, an API key in a response) used to be usable only in that Hunt, for at most 24 hours. It is now also kept for the target, so a later Hunt can use it too, still without ever seeing it.
- Kept per target, encrypted under the same key as credential profiles, once per value (repeat
sightings update when it was last seen), with a 30-day expiry that a new sighting extends. Scans
keep the secrets they prove as well: a verified exposure (an
.envor other configuration file, a credential store, a SQL dump) keeps the values it found. - Used by reference.
secrets.inspectlists a target's usable secrets for the Hunt (reference, kind, field name, length, when seen; never the value), and anhttp.requestbinding takes{"secret_ref": "secret://<id>"}besidewithheld_ref. The worker sends the value; every later response of that Hunt that echoes it has it withheld. A reference resolves only for a live Hunt of the secret's own target while it is active and unexpired; another target, a revoked or expired secret, or a malformed reference is refused with a reason code and no value. Every use is recorded with the action and the destination host. - Operator routes:
GET /targets/{id}/discovered-secrets(metadata only),POST /targets/{id}/discovered-secrets/{secret}/state(revoke or reactivate),POST /targets/{id}/discovered-secrets/{secret}/expiry(1–365 days) andDELETE /targets/{id}/discovered-secrets(purge). No route returns a value, and a Hunt planner cannot use them. An operator's revocation or expiry stands: later sightings do not undo it. - Granting to another target: an operator can let Hunts of another target use a secret
(
POST .../{secret}/grants {"target_id": ...},DELETE .../grants/{target}). Only the target it was found on grants it, at most 50 targets per secret; a grant never widens a Hunt's scope (the value is sent only where that Hunt may already go). - Promoting and revealing:
POST .../{secret}/promotemoves the value into a new credential profile of the target (validated like any typed profile; the discovered secret becomessupersededand points at it).POST .../{secret}/revealshows the value to a person (cache-control: no-store). Both are recorded with who did it, apart from Hunt uses. Behind the Enterprise gateway, revealing, granting and promoting each need the person's step-up for that one decision. - In the Hunt briefing: the live sections say how many usable secrets the target holds, how many were never tried, by kind, and how to list and use them.
- Terminal:
shakerscan secrets list <target> [--usable],revoke,reactivate,expiry --days,ungrant --to, andgrant --to,promote --name --auth-kind,reveal, the last three decided in your own terminal (a keypress on an open-source engine, your step-up on Enterprise). - Target page: a Discovered secrets card lists them (never a value) with the same actions; a revealed value is shown in its dialog only. Behind the Enterprise gateway the card names the terminal command for a step-up decision.
- Lifecycle: an hourly sweep marks expired secrets and deletes their ciphertext 30 days after they stopped being usable; a target keeps at most 2,000. Deleting a Hunt record deletes the secrets it found (use records stay); deleting a target deletes its secrets; merging duplicate targets moves them to the surviving target.
Secrets no longer stay in finding evidence
The JavaScript-secrets and API data-exposure checks of the standalone scanner (scanner.py, its
CLI and --server mode) put the matched text into a finding's evidence: 100 characters around a
JavaScript secret (the whole value), or the raw response snippet. The JavaScript context now shows
the value only as its preview (AIza...4567) with any other token-like run near it masked, and the
data-exposure snippet keeps no response text (the finding family, the match's length and offset).
As a second line, before the worker writes a Scan's result anywhere (the result artifact, a scan's
stored result, the findings table, a fleet node's posted result), every self-evident provider secret
still in a finding's evidence (AWS, Stripe, Slack, GitHub, npm and SendGrid keys, credentialed
database URLs, password hashes) is kept for the target as above and replaced by its secret://
reference, or by [REDACTED:<kind>] when it cannot be kept.
Upgrades look after themselves
An upgrade on a host short of disk space could leave Redis unable to start ("dependency failed to start: redis is unhealthy") and its operator choosing Docker volumes to delete. The launcher now handles it, and never asks you to manage Docker:
- Disk space first. Before it downloads or stops anything,
shakerscan startchecks that the images this release still needs (plus room for the databases) fit in Docker's storage. If not, it removes ShakerScan release images that no container uses (running or stopped), never a volume, never another product's image, never this install's current or previous release (kept for rollback), and, if that is still not enough, stops with the numbers and changes nothing. A start with nothing to download never needs this room. Docker Desktop, whose disk the host cannot see, skips the check. - The database is saved before an upgrade. A start that changes the release dumps PostgreSQL to
~/.shakerscan/backups/pre-upgrade-<time>-<from>-to-<to>/first (the newest two are kept;SHAKERSCAN_PRE_UPGRADE_BACKUP=0turns this off). - Redis is repaired, not deleted. Redis must be healthy before anything that depends on it
starts. A damaged append-only file (an interrupted write, typically a full disk) is copied to
backups/redis-<time>/and repaired; a Redis that cannot write gets room from old images. Only if repair fails does it start empty, which loses queues and job status only: scans, findings and evidence are in PostgreSQL. The API and workers are then restarted onto it. A Redis that is still loading is waited for; one that fails for a reason the launcher does not recognise is left as it is, with its last messages. - Old images go after an upgrade. After a successful start, this install's own ShakerScan
images of releases older than the previous one are removed. Images it never recorded stay, and so
do third-party images (PostgreSQL, Redis) and an optional module's images while it is off
(
SHAKERSCAN_PRUNE_OLD_IMAGES=0keeps them all). A second, stopped install on the same host that still uses one of those old releases pulls its images again when it starts. - Warnings ahead of time.
shakerscan statusanddoctorwarn when Docker has less than 20 GB free, or when the database volume is missing on an install that has run before. Nothing in ShakerScan suggestsdocker system prune --volumes, which deletes the database. - Scans lost from the queue run. A Scan that was waiting when Redis lost its queue used to stay "pending" forever. One that provably never started (no start, no worker, no step begun) is now queued again from its saved job, with the routing it was first queued with, within about ten minutes, at most three times. A Scan that may have run keeps the "outcome unknown" flag, so nothing runs twice.
Rolling back to 2.9.x
Nothing this release writes is read differently by 2.9.x: the owner and cohort were already
stored in the target's metadata, and 2.9.x ignores the discovered_secrets,
discovered_secret_uses and discovered_secret_grants tables (they stay, unused, until 2.10 runs again). A finding's evidence
that holds a [secret://...] reference shows it as text. A 2.9.x launcher ignores SHAKERSCAN_MODEL_INTAKE_ENABLED and
starts Model Intake again (pulling its images if they were removed).