Skip to content
Connect an agent

Hosted diagrams and plans

Secret scanning

Plotwork scans every diagram's source for what looks like a credential, and warns before the diagram is published or committed. An organisation can go further and refuse a publish to its library that has one.

TL;DR

QuestionAnswer
What is found?Cloud keys (AWS, Google Cloud, Azure), GitHub, Slack and Stripe tokens, JWTs, private keys, connection strings with a password, a random value assigned to a name such as password or api_key, and Plotwork's own API keys.
Who gets the warning?Everyone, on every draw and check: draw_diagram, validate_diagram, the API, the command line, the GitHub Action and the portal's editor. A personal account is warned and never blocked.
Does it stop a drawing?No. The warnings are W700 to W710; the picture is drawn as always.
Does it fail CI?Yes, with validate --strict or the Action's fail-on: warning. A secret warning is not a style suggestion.
Is the secret repeated back?Never. A warning names the kind and shows the first four characters and a mask, such as ghp_********, and the source line under a CLI message is starred out.
What can an organisation add?Secret scanning before publish on its Settings tab: Warn (the default) or Block. Block refuses a publish to the library, an agent's auto-publish, an editor save and a public link.
Is it a guarantee?No. Detection is pattern-based: it can miss a secret, and it can name a value that is not one.

1. What is found

The scan reads the raw source line by line, so labels, notes, attributes, comments and pasted JSON are all covered. Each finding has a code, a line, a column, a kind and a hint.

CodeKindWhat it looks for
W700aws-access-key, aws-secret-keyAn access key id (AKIA, ASIA and the other prefixes) or a 40-character secret assigned to aws_secret_access_key
W700gcp-api-key, gcp-service-accountA Google API key (AIza and 35 characters), or the private_key_id of a pasted service account file
W700azure-storage-key, azure-sas-tokenAccountKey= or SharedAccessKey= in a connection string, or the sig= of a shared access signature beside its sv=
W701github-tokenghp_, gho_, ghu_, ghs_, ghr_ and github_pat_ tokens of their exact length
W702slack-token, slack-webhookxoxb-, xoxp- and the other Slack tokens, and hooks.slack.com addresses
W703stripe-keysk_live_, rk_live_ and whsec_
W704jwtThree base64url parts whose first decodes to a header with alg
W705private-keyA PEM private key header, such as -----BEGIN RSA PRIVATE KEY----- or the OpenSSH, EC, DSA, PGP and encrypted forms
W706assigned-secretA random value of ten characters or more, with letters and digits, assigned to a name such as password, secret, token, api_key or client_secret
W707connection-stringAn address with a password in it, such as postgres://app:<password>@db
W708plotwork-keyA Plotwork API key, pw_ and 64 hexadecimal characters
W709private-ipA private IPv4 address. Only when an organisation asks; see section 4.
W710internal-hostA hostname matching the organisation's patterns. Only when an organisation asks.

What is left alone

A diagram that documents where a key goes should not be flagged for it. These are never findings:

Written asExample
A placeholder<token>, YOUR_API_KEY, changeme, redacted
A reference to a value kept elsewhere${DB_PASSWORD}, {{ secrets.DEPLOY_TOKEN }}, %API_KEY%
A repeated or counting valuexxxxxxxx, 00000000, 1234567890abcdef
A vendor's published exampleAWS's documentation key, the jwt.io sample token
A stand-in password in an addresspostgres://user:password@localhost
A short or plain word assignedpassword=hunter2, secret=configured_in_vault

Private addresses and internal hostnames are left alone too, unless an organisation turns them on, because an architecture diagram usually means them.

2. The warning everyone gets

A finding is a warning, never an error, so a draw, a check and a render all go ahead:

text
warning W701: This looks like a GitHub token (ghp_********); remove it before you publish or commit.   3 | node ci service "CI ****************************************"     |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^hint: Put a placeholder such as <github-token> or a reference such as ${NAME} in its place, and rotate the value if it was ever shared. The scan matches patterns, so it can miss a secret or name a value that is not one.

The command line stars out every secret on the line it shows, under any message, so a CI log never carries one. In CI, plotwork validate --strict and the Action's fail-on: warning fail on a secret warning; the style suggestions W204, W205 and W5xx still pass.

3. Block at the library (organisations)

On the organisation's Settings tab, Secret scanning before publish has two choices. The owner and the admins change it; every member can read it.

ChoiceA publish to the library with a finding
Warn, the defaultGoes ahead. The person drawing still sees each warning.
Block, recommendedIs refused with 422 orgs.publish_blocked, and nothing is stored.

Block covers every way into the library: a publish from an agent, the API or the command line, "Agents publish every diagram they draw", a save in the portal's editor, and sharing a diagram at a public link, which checks the stored picture. The answer is data, not an exception: its details.findings lists every finding with its code, line, column, kind and masked value. An agent is told to remove each one and publish again, and the editor lists them under its bar. Block is the Security item Block secrets in published diagrams in Recommended settings.

A personal account is never blocked, whatever an organisation it belongs to chose.

4. Private addresses and internal hostnames

Under Block, an organisation can also find:

OptionFindsDefault
Private IP addressesAddresses in 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16, as W709Off
Internal hostnamesNames matching up to 20 patterns, as W710. * stands for one or more labels at the start, so *.corp.example finds db.eu.corp.example.None

Both count only under Block, and only for a publish to that organisation's library.

5. What the audit log records

EventWhenMetadata
orgs.diagram.publish_blockedA publish was refusedvia (publish, auto, edit or share), findings (how many), kinds (each kind with its count, such as github-token=2)
orgs.settings.secret_scanningThe setting changedmode, privateIps, and hostPatterns (how many)

Neither ever holds a value, a masked value or a hostname pattern. On Enterprise both are sent to your SIEM like every other event; see Send audit events and drift to your tools.

6. Not a guarantee

Detection is pattern-based. It finds credentials with a known shape and random values assigned to secret-like names, and it can miss a password that is a plain word, a key of a format it does not know, or a secret split across lines. It can also name a value that is not a secret. Treat it as a second pair of eyes, keep secrets out of diagram source in the first place, and rotate any value that was published.

See also

DocWhy
Diagnostic codesEvery code, W700 to W710 included
OrganisationsThe library, roles and the other Settings
Audit logReading and exporting the events
Diagrams in CIfail-on: warning and the checks a pull request runs
Why SVGWhat a published picture carries, and what it never does