Skip to content

feat(credentials): /ops:credentials audit + plugin.json field hints (v2.0.6)#184

Merged
auroracapital merged 1 commit intomainfrom
feat/ops-credentials-audit
May 1, 2026
Merged

feat(credentials): /ops:credentials audit + plugin.json field hints (v2.0.6)#184
auroracapital merged 1 commit intomainfrom
feat/ops-credentials-audit

Conversation

@auroracapital
Copy link
Copy Markdown
Collaborator

@auroracapital auroracapital commented May 1, 2026

Summary

Claude Code's plugin settings UI cannot introspect external credential stores. If a user has STRIPE_SECRET_KEY in macOS Keychain or klaviyo_api_key in Doppler, the settings panel shows those fields as empty — even when those integrations are working. This PR ships a separate audit surface so users can see configured-vs-missing at a glance.

Adds:

  • bin/ops-credentials — credential audit CLI. Scans shell env, preferences.json, Doppler (resolves doppler:KEY references live), macOS Keychain, and Dashlane. Reports configured-vs-missing with first6•••last4 masking. Output: human table | --json | --service <name>. Compact mode on SSH/mobile per Rule 7.
  • skills/ops-credentials/SKILL.md/ops:credentials slash command wrapping the bin with follow-up actions (configure missing, re-audit, JSON export).
  • Description hints on 22 sensitive fields in plugin.json: "If already configured via /ops:setup or stored in keychain/Doppler, leave blank — runtime resolves automatically."

Smoke test result

Run on dev machine — detected 21/27 tracked credentials across 4 sources (env, keychain, Dashlane, Doppler). 6 missing flagged for /ops:setup.

Privacy

  • Never prints raw values
  • JSON export contains only {service, label, configured, masked, source}
  • Read-only — no writes to keychain/Doppler/prefs

Test plan

  • bash -n passes
  • End-to-end run detects creds across all 4 source backends
  • Mask format: first6•••last4 for 12+ char values, ••• for shorter
  • --json output shape stable
  • CI green

Note

Medium Risk
Adds a new credential-auditing CLI/skill that reads from env, prefs, Doppler, macOS Keychain, and Dashlane; while secrets are masked and read-only, it touches sensitive credential-resolution paths and external CLIs.

Overview
Introduces a new /ops:credentials skill backed by bin/ops-credentials to audit which integration credentials are configured vs missing across shell env, preferences.json, Doppler refs (live-resolved), macOS Keychain, and Dashlane, with masked output plus --json and --service filtering.

Updates plugin.json credential field descriptions to explicitly tell users they can leave fields blank when credentials are already available via /ops:setup or external stores, and bumps the marketplace/plugin version to 2.0.6 with an accompanying changelog entry.

Reviewed by Cursor Bugbot for commit 209ea5b. Bugbot is set up for automated code reviews on this repo. Configure here.

…v2.0.6)

Claude Code's plugin settings UI cannot introspect external credential
stores. If a user has STRIPE_SECRET_KEY in macOS Keychain or
klaviyo_api_key in Doppler, the settings panel shows those fields as
empty even when configured. The user can't tell at a glance which
integrations are wired up.

Adds:
- bin/ops-credentials — credential audit CLI. Scans shell env,
  preferences.json, Doppler (with doppler:KEY resolution), Keychain,
  and Dashlane. Reports configured-vs-missing with first6•••last4
  masking. Output: human table | --json | --service <name>. Compact
  mode on SSH/mobile per Rule 7.
- skills/ops-credentials/SKILL.md — /ops:credentials slash command
  wrapping the bin script with follow-up actions (configure missing,
  re-audit, JSON export).
- Description hints on all 22 sensitive fields in plugin.json:
  "If already configured via /ops:setup or stored in keychain/Doppler,
  leave blank — runtime resolves automatically."

Privacy: never prints raw values. JSON export contains only
{service, label, configured, masked, source}, never the raw secret.
Read-only — does not write to keychain/Doppler/preferences.

Bumps marketplace.json + plugin.json to 2.0.6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@auroracapital has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 25 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec951914-7bbb-40cc-a839-4afda4bd47b6

📥 Commits

Reviewing files that changed from the base of the PR and between 305d80e and 209ea5b.

📒 Files selected for processing (5)
  • .claude-plugin/marketplace.json
  • claude-ops/.claude-plugin/plugin.json
  • claude-ops/CHANGELOG.md
  • claude-ops/bin/ops-credentials
  • claude-ops/skills/ops-credentials/SKILL.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ops-credentials-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 16 minutes and 25 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@auroracapital auroracapital merged commit e2d7e50 into main May 1, 2026
9 of 11 checks passed
@auroracapital auroracapital deleted the feat/ops-credentials-audit branch May 1, 2026 05:51
Comment on lines +147 to +153
results_json=$(jq -c --arg s "$service" --arg l "$label" --arg m "$masked" --arg src "$src" \
'. + [{service:$s, label:$l, configured:true, masked:$m, source:$src}]' <<<"$results_json")
else
missing=$((missing+1))
missing_lines+=("$(printf ' ✗ %-32s — run /ops:setup %s' "$label" "$service")")
results_json=$(jq -c --arg s "$service" --arg l "$label" \
'. + [{service:$s, label:$l, configured:false}]' <<<"$results_json")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The script calls jq without first checking if it is installed, which will cause the script to crash if jq is not in the PATH.
Severity: MEDIUM

Suggested Fix

Before the main loop, add a check to ensure jq is installed and exit with an informative error if it is not. For example: if ! command -v jq &> /dev/null; then echo "jq is not installed. Please install it to continue." >&2; exit 1; fi.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: claude-ops/bin/ops-credentials#L147-L153

Potential issue: The script has `set -e` enabled, which causes it to exit immediately if
a command fails. In the main scan loop, the `jq` command is used on lines 147-153
without a prior check to ensure it is installed. If `jq` is not present in the system's
`PATH`, the command will fail, and the script will terminate prematurely. This is
inconsistent with other parts of the script that correctly use `command -v jq` to verify
its existence before use.

Did we get this right? 👍 / 👎 to inform future reviews.

while [[ $# -gt 0 ]]; do
case "$1" in
--json) OUTPUT_MODE="json"; shift ;;
--service) SERVICE_FILTER="$2"; shift 2 ;;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Providing the --service flag without a corresponding value causes the script to crash with an "unbound variable" error due to set -u.
Severity: MEDIUM

Suggested Fix

Before accessing $2 for the --service flag, check if it is set. If $2 is empty or starts with a -, it indicates a missing value. In this case, print a usage error and exit. For example: if [[ -z "$2" || "$2" == -* ]]; then echo "Error: --service requires an argument." >&2; exit 1; fi.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: claude-ops/bin/ops-credentials#L29

Potential issue: The script enables `set -u`, which treats attempts to access unset
variables as errors. The argument parsing for the `--service` flag on line 29 assigns
`$2` to `SERVICE_FILTER`. If a user provides `--service` as the last argument on the
command line, `$2` will be unset. This will trigger an "unbound variable" error and
cause the script to exit, rather than providing a more user-friendly error message about
the missing value.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Missing || true crashes script on malformed preferences
    • Added || true to the preferences jq command substitution so invalid JSON yields an empty value and resolution continues under set -e.
  • ✅ Fixed: Unconditional jq usage crashes text mode without jq
    • Incremental results_json updates now run only when OUTPUT_MODE is json, so the default text path no longer invokes jq.

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 209ea5b. Configure here.

# 2. preferences.json
if [[ -n "$prefs_path" && -f "$PREFS" ]] && command -v jq >/dev/null 2>&1; then
local v
v=$(jq -r "${prefs_path} // empty" "$PREFS" 2>/dev/null)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing || true crashes script on malformed preferences

Medium Severity

The jq call reading preferences.json on line 94 is the only external command in resolve() that lacks || true. Under set -euo pipefail, a malformed or empty prefs file causes jq to exit non-zero, terminating the entire audit script. The three other backends (Doppler, Keychain, Dashlane) all guard their commands with || true and degrade gracefully.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 209ea5b. Configure here.

missing=$((missing+1))
missing_lines+=("$(printf ' ✗ %-32s — run /ops:setup %s' "$label" "$service")")
results_json=$(jq -c --arg s "$service" --arg l "$label" \
'. + [{service:$s, label:$l, configured:false}]' <<<"$results_json")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unconditional jq usage crashes text mode without jq

Medium Severity

The scan loop unconditionally builds results_json via jq on lines 147–148 and 152–153 for every credential, even when the output mode is "text". The script explicitly guards against missing jq on line 92 with command -v jq, showing awareness it may be absent — but the JSON-building calls in the loop body have no such guard. Under set -euo pipefail, running bin/ops-credentials (text mode, no --json) on a system without jq crashes the script with a command-not-found error.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 209ea5b. Configure here.

auroracapital added a commit that referenced this pull request May 2, 2026
…release (#199)

The v2.0.5 → v2.0.9 patch series shipped meaningful features (multi-workspace
Slack #195, /ops:credentials audit #184, ops-ci current-state filter #196,
telegram preflight #185, userConfig schema upgrades #182). Per semver these
should have been a minor bump. This release retroactively rolls them up
into v2.1.0 with a single coherent CHANGELOG entry.

No code changes — only:
- plugin.json: 2.0.9 → 2.1.0
- CHANGELOG.md: new [2.1.0] entry consolidating Added/Fixed/Notes for the patch series
- README header + What's-new section: refer to v2.1.0
- 11 docs/*.md badges + agents-reference subtitle + migration latest-stable note: v2.0.9 → v2.1.0

Marketplace pin (.claude-plugin/marketplace.json) bumped in follow-up PR.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant