Issue description
Sensitive values are rendered in plaintext across the dashboard's read paths. The masking added in d32c945 covers only the secret-manager forms (Vault token, AWS keys, GCP private key) and the Settings admin-key field; the most common credential paths are unprotected:
- Consumer credential secrets — a key-auth
key, basic-auth password, jwt-auth secret, hmac-auth secret_key are fully visible: open the credential's detail page → the plugin card's View drawer shows e.g. {"key": "SUPER-SECRET-KEY-12345"} verbatim in the Monaco editor, in read-only view mode, with zero interaction needed beyond opening the card. (Verified live on master 9979b31.) Path: FormPartCredential.tsx → FormItemPlugins → PluginEditorDrawer — no masking concept exists anywhere in the Monaco render path.
- SSL private keys — the SSL detail page renders
key / keys[i] through FormItemTextareaWithUpload, a plain textarea showing whatever the Admin API returns, unmasked, expanded via autosize in view mode (FormPartSSL/FormItemCertKeyList.tsx).
- Plugin configs containing tokens — any plugin whose config embeds secrets (
ai-proxy/ai-proxy-multi API keys, limit-* redis_password, logger auth headers, forward-auth, …) displays them verbatim in the same editor drawer on routes/services/global rules detail views.
- Transient echo in error toasts — the interceptor toasts raw Admin API error bodies (
error_msg), which for schema-validation failures can include the submitted secret value (src/config/req.ts).
Expected behavior
- In view mode, known-sensitive JSON paths are redacted by default (
"key": "•••••") with an explicit reveal affordance, mirroring how FormPartSecret already treats vault/aws/gcp fields.
- SSL private key fields use a masked control in view mode (certs can stay plaintext).
- Error toasts truncate long bodies rather than echoing full submitted payloads.
Suggested implementation
Maintain a small per-plugin sensitive-path registry (key-auth.key, basic-auth.password, jwt-auth.secret, hmac-auth.secret_key, ai-proxy.auth.*, *.redis_password, …) and apply it as a display-transform in PluginEditorDrawer's view mode before handing the JSON to Monaco. Edit mode can stay unmasked (an editor must show what it edits), which still removes the shoulder-surfing/screen-share exposure from the default read path.
Environment
- apisix-dashboard: master (
9979b31)
Related: #3218 (the original report; this issue records the verified residual scope after d32c945)
Issue description
Sensitive values are rendered in plaintext across the dashboard's read paths. The masking added in d32c945 covers only the secret-manager forms (Vault token, AWS keys, GCP private key) and the Settings admin-key field; the most common credential paths are unprotected:
key, basic-authpassword, jwt-authsecret, hmac-authsecret_keyare fully visible: open the credential's detail page → the plugin card's View drawer shows e.g.{"key": "SUPER-SECRET-KEY-12345"}verbatim in the Monaco editor, in read-only view mode, with zero interaction needed beyond opening the card. (Verified live on master9979b31.) Path:FormPartCredential.tsx→FormItemPlugins→PluginEditorDrawer— no masking concept exists anywhere in the Monaco render path.key/keys[i]throughFormItemTextareaWithUpload, a plain textarea showing whatever the Admin API returns, unmasked, expanded via autosize in view mode (FormPartSSL/FormItemCertKeyList.tsx).ai-proxy/ai-proxy-multiAPI keys,limit-*redis_password, logger auth headers,forward-auth, …) displays them verbatim in the same editor drawer on routes/services/global rules detail views.error_msg), which for schema-validation failures can include the submitted secret value (src/config/req.ts).Expected behavior
"key": "•••••") with an explicit reveal affordance, mirroring howFormPartSecretalready treats vault/aws/gcp fields.Suggested implementation
Maintain a small per-plugin sensitive-path registry (
key-auth.key,basic-auth.password,jwt-auth.secret,hmac-auth.secret_key,ai-proxy.auth.*,*.redis_password, …) and apply it as a display-transform inPluginEditorDrawer's view mode before handing the JSON to Monaco. Edit mode can stay unmasked (an editor must show what it edits), which still removes the shoulder-surfing/screen-share exposure from the default read path.Environment
9979b31)Related: #3218 (the original report; this issue records the verified residual scope after d32c945)