Skill 安装
多 Agent 安装npx skills add https://github.com/bitwarden/ai-plugins --skill implementing-dapper-queries
使用 npx skills 快速安装,支持将 Skill 添加到多个兼容的 AI Agent。
Skill 调研报告
bitwarden/ai-plugins#implementing-dapper-queries
Skill 更新日志
版本列表
无版本号
refactor(bitwarden-delivery-tools): make labeling-changes the single owner of change types (#255)
无版本号
feat(bitwarden-delivery-tools): extract PR conventions into their own skill (#235)
* feat(bitwarden-delivery-tools): extract PR conventions into their own skill
The title format, PR template body, and ai-review label were three steps
inside `creating-pull-request`, which meant any other skill wanting them had
to drive that workflow's numbered steps and inherit its review gate, preview,
and `gh pr create`. `force-multiplier` did exactly that, walking the whole
workflow at its pilot target to collect conventions it then replicated.
`applying-pr-conventions` now owns those values and returns them. Both
callers invoke it as a peer, so neither reaches into the other, and
`creating-pull-request` loses its caller-detection protocol entirely.
* refactor(bitwarden-delivery-tools): narrow the extraction to its objective
Base-branch resolution and the PR title allowlist both rode along with the
conventions extraction rather than belonging to it.
- creating-pull-request: Step 1b, the --base guidance, and the preview's base
row are out. Scoping a review to a release branch needs a base-ref parameter
in bitwarden-code-reviewer first, which lives in another plugin.
- references/pr-title-allowlist.md is deleted. --title "$(cat ...)" is what
closes the injection, since command-substitution output is not re-parsed, and
the pattern refused the conventional-commit ! breaking-change marker. The
file-handoff rule survives inline at each gh pr create.
- creating-pull-request: review findings are assessed with the user before any
are acted on.
- evals: the compound title-and-label query leaves the set, along with the
description clause written to force it. Baseline re-recorded at 9/9 and 12/12.
无版本号
feat(bitwarden-testing-tools): recommend test layer (#240)
* feat(bitwarden-testing-tools): add recommending-test-layers skill
Adds a forward-looking counterpart to assessing-test-coverage that
recommends which tests a change needs and at which layer each belongs
(static / unit / component / contract / integration / E2E), from a Jira
key, Testmo CSV, an assessing-test-coverage report, a PR, or a feature
description. It separates deterministic pre-merge gates from
non-deterministic post-deploy tests and grades criticality against
Bitwarden's Defect Severity Classification Guide.
Tool scoping follows the repo's review conventions: gh api restricted to
--method GET, no git clone, no AskUserQuestion, writes confined to
${CLAUDE_PLUGIN_DATA}. Ships an evals/ trigger harness (baseline recorded
with Sonnet at 7 runs/query: 10/10 should-trigger, 9/9 should-not-trigger)
so the three sibling skills do not cannibalize each other's triggers.
Bumps bitwarden-testing-tools to 1.2.0 across plugin.json,
marketplace.json, the root README catalog, and the plugin README, with a
matching CHANGELOG entry.
* test(bitwarden-testing-tools): re-baseline recommending-test-layers eval on claude-opus-4-8
Align the recommending-test-layers trigger eval with its sibling
assessing-test-coverage eval, which baselines on claude-opus-4-8 (also
the runner's default). Regenerate baseline.json on opus instead of the
prior sonnet run and update the README's documented model. Verdicts are
unchanged: 10/10 should-trigger, 9/9 should-not-trigger.
* fix(bitwarden-testing-tools): address recommending-test-layers review findings
Scope the skill description away from abstract testing-concept questions,
trim redundant trigger phrasings, and drop the unused gh api tool grant.
Reword step 2 so it marks unverifiable coverage rather than gating on it.
Harden both eval runners' read-only carve-out (gh api repos/ only, treat >
as a chain), record the first skill of any kind to expose sibling-first
triggers, and persist timeouts plus model/runs/timestamp so baselines are
self-describing. Re-baseline recommending-test-layers at 10/10 and 10/10,
and narrow the changelog cannibalization claim to the two measured legs.
* fix(bitwarden-testing-tools): correct recommending-test-layers doc mismatches
Address code-review findings on the unreleased 1.2.0 skill:
- evals/README.md: 9 -> 10 should-not-trigger queries (matches trigger-eval.json)
- SKILL.md: add 'unverified' to the Existing coverage legend, matching step 2
* fix(bitwarden-testing-tools): close gh api write-bypass in eval read-only guard
The `gh api repos/` read-only carve-out in both trigger-eval harnesses only
bounded the target path, not the HTTP method. `gh` accepts the endpoint as the
first positional with `-X`/`--method` after it, so `gh api repos/O/R/issues -X
POST` matched the prefix, tripped no SHELL_CHAINS operator, and was waved through
instead of bailed — letting an adversarial should-not-trigger run execute a live
state-changing API call the guard was meant to prevent (CWE-183/CWE-77).
Replace the inline prefix check with `is_read_only_bash()`, which also rejects a
non-GET `-X`/`--method` or any request-body flag, and correct the comment that
overstated the guarantee. Both harnesses stay byte-identical modulo the skill
token.
Re-record the assessing-test-coverage baseline against the updated harness output
schema (model/runs_per_query/recorded_utc, per-result timeouts/first_skills) so
its committed baseline matches the code it runs. 10/10 and 10/10, no regression.
* fix(bitwarden-testing-tools): harden eval read-only guard against joined gh flags
Address PR #240 review findings on both eval runners:
- Normalize gh flag tokens so the read-only carve-out bails on write
requests spelled with joined or =-attached forms (-XPOST,
--method=POST, --field=k=v), not just the space-separated spelling.
- Add "<" to SHELL_CHAINS so process substitution (gh pr view unit -> component -> contract) instead of
being driven by the Defect Severity Guide. Criticality is demoted to a
post-deploy-only input that decides which critical journeys earn E2E or
smoke, resolving the review finding that made `integration` unreachable.
Adopt minimumCD's full taxonomy (adds smoke, synthetic monitoring,
exploratory; renames acceptance->E2E, E2E smoke->smoke), replace the
Gates-deploy column with Pipeline role, and update the README row and
1.2.0 changelog text to match.
* fix(bitwarden-testing-tools): resolve recommending-test-layers layer/gate contradictions
Route integration, synthetic monitoring, and exploratory via explicit triggers so all advertised layers are reachable; scope the deterministic-gate rule to the pre-merge gate and carve out E2E promotion gating; reconcile smoke rollback wording; scope criticality to journeys with an n/a value for non-journey behaviors; map the assessing-test-coverage taxonomy onto this skill's layers; align README and CHANGELOG.
* fix(bitwarden-testing-tools): resolve recommending-test-layers review contradictions
Address code-review findings on recommending-test-layers/SKILL.md:
- Step 4 no longer says recommend 'one' post-deploy layer; states a
top-band journey can earn smoke + E2E (+ synthetic monitoring).
- E2E now owns end-to-end proof of the deployed top-band journey, not
'load/resilience/compliance' that no trigger routes to it.
- Acceptance criteria stay owned 1:1 by the component layer; E2E proves
the assembled artifact, removing the component/E2E overlap.
- Cross-reference the promotion-stage carve-out at the deterministic
pre-merge-gate rule so E2E gating reads as deliberate, not contradictory.
- Reword the deterministic ladder so component is the center of gravity
and contract is a concern-specific choice, not a rung above component.
* fix(bitwarden-testing-tools): resolve recommending-test-layers contradictions
Align synthetic-monitoring trigger with the layer table, README, and
CHANGELOG (any SLO-bound journey, not just top-band); add the missing
unit->static/unit taxonomy mapping in step 2; state the component-vs-unit
tie-breaker in step 3; and let the Recommendations template record a
behavior earning multiple layers via one row per layer.
* refactor(bitwarden-testing-tools): debloat recommending-test-layers skill
Align verbosity and directiveness with the sibling assessing-test-coverage
skill: cut concept explanations the model already knows, remove rules that
were restated up to five times, and drop operational over-specification.
The merge-gate-vs-promotion-gate distinction is now stated once, resolving
the apparent self-contradiction. Objective, layer table, and output template
are preserved.
* refactor(bitwarden-testing-tools): tighten recommending-test-layers step 3 and gotchas
Trim step 3 to a single decision rule and condense the component-scope and E2E-gating gotchas.
* test(bitwarden-testing-tools): re-baseline recommending-test-layers eval after description change
Re-recorded baseline.json on claude-opus-4-8 at 7 runs/query following the
skill's description update (added smoke/synthetic monitoring/exploratory layers,
revised trigger phrasings). Clean run: 10/10 should-trigger, 10/10
should-not-trigger, no timeouts, no PASS/FAIL regressions. Bumped the recorded
date in the eval README to match.
* test(bitwarden-testing-tools): re-baseline assessing-test-coverage eval after sibling description change
Re-recorded baseline.json on claude-opus-4-8 at 7 runs/query following the
recommending-test-layers description edits (widened layer list, narrowed
"pyramid or trophy for this change" trigger), per the cross-skill re-run rule
in evals/README.md. Clean run: 10/10 should-trigger, 10/10 should-not-trigger,
no timeouts, no PASS/FAIL regressions. The 'testing trophy or pyramid'
near-miss still routes 7/7 to recommending-test-layers with no cannibalization.
Bumped the recorded date in the eval README to match.
* fix(bitwarden-testing-tools): make eval shell-chain guard quote-aware
Both trigger-eval harnesses detected shell chaining with a raw substring scan
over SHELL_CHAINS, so the '' entries also matched inside quoted
arguments: a read-only 'gh search code "Task"' was misclassified as a
shell chain, the child was killed, and a legitimate mid-execution skill run was
recorded as a real-work bail. Bails are indistinguishable from clean
non-triggers in trigger_rate, the value the regression gate keys on.
Replace the substring scan with a quote-aware shlex tokenizer (non-posix +
punctuation_chars) so only bare unquoted operator tokens count; command
substitution and newlines are still matched on the raw string, deliberately
quote-blind since "$(...)" executes in a real shell. Track a per-query 'bails'
count so a harness bail is greppable during triage rather than folded silently
into the non-trigger count (it stays in the denominator: a genuine real-work
bail is a legitimate non-trigger).
Re-recorded both baseline.json on claude-opus-4-8 at 7 runs/query: 10/10
should-trigger, 10/10 should-not-trigger, no verdict changes. Residual bails on
should-trigger queries now trace to genuine chains ('... | head') and the known
gh-pr-diff / leading-slash allowlist gaps, not the '' false positive.
Addresses the Major finding in the PR #240 validation review.
Co-authored-by: Claude
* fix(bitwarden-testing-tools): stop eval read-only guard over-bailing genuine reads
Address three over-bail edge cases in both skills' run_real_eval.py harnesses,
plus a cspell ordering nit:
- Detect command substitution quote-aware: a single-quoted '$(...)' literal is
left alone (a real shell suppresses it), while double-quoted "$(...)" and
backticks still bail.
- Match only a short flag's leading letter, so a benign cluster like -sf is no
longer misread as the write flag -f (every write short flag takes a value and
must lead its cluster).
- Restore the broad `gh api` carve-out so gh api graphql, gh api /repos/..., and
gh api user count as reads again; the method/body flag guard, not the prefix,
enforces read-only.
- Sort "shlex" into place in .cspell.json.
Folded into the unreleased 1.2.0 changelog entry; baselines unaffected (changes
only reduce spurious bails).
* refactor(bitwarden-testing-tools): simplify eval harnesses, switch to sonnet
Align both trigger-eval runners with the bitwarden-delivery-tools pattern:
remove the ~95-line quote-aware read-only-bash guard and real-work bail path
in favor of detecting the target skill token as it streams into a Skill/Read
tool input. Contain the adversarial should-not-trigger queries with
--allowedTools Skill Read so they cannot clone repos or run build/test
toolchains, and bound anything that stalls with --timeout.
Fix the memory leak: each claude -p is a ~1GB Node process with its own child
tree, and process.kill() reaped only the parent, orphaning the rest until a
full run exhausted memory. Each subprocess now runs in its own session and is
reaped as a process group on trigger, completion, or timeout, and the default
--num-workers drops from 5 to 3 to cap peak memory.
Default both harnesses to --model claude-sonnet-5 and re-record baselines on
that model (recommending 10/10 + 10/10, assessing 9/10 + 10/10). One assessing
should-trigger phrasing sits below threshold on sonnet where it fired 7/7 on
opus; recorded honestly in the baseline and documented as a known gap.
* refactor(bitwarden-testing-tools): consolidate eval runners into shared engine
The assessing-test-coverage and recommending-test-layers skills each shipped a
byte-identical copy of run_real_eval.py differing only in a hardcoded skill
token, so every engine fix had to be hand-applied to both. Move the runner to a
single plugin-level evals/run_real_eval.py and resolve the target token from
--skill, or infer it from the eval-set path when omitted. Each skill's evals/
now holds only its data (trigger-eval.json, baseline.json) and a README, so
adding evals for a new skill no longer requires copying the engine. Runtime
behavior and the recorded baselines are unchanged.
Bump to 1.2.1 and remove stray review artifacts from the eval dirs.
* test(bitwarden-testing-tools): re-baseline trigger evals on 1.2.1 skills
Re-run both trigger-eval suites against the reinstalled 1.2.1 plugin
(sonnet-5, 7 runs/query) after the eval-engine consolidation and
description tightening on this branch.
One dual-intent query per skill slips from 4/7 to 3/7, straddling the
0.5 threshold as it splits samples with its sibling skill; recorded in
each baseline and documented as a known gap in the eval READMEs.
* fix(bitwarden-testing-tools): address PR review findings
- run_real_eval.py: parse the final stdout chunk on process exit so a
trigger in the last read (or a last event without a trailing newline)
is no longer dropped as a false negative
- recommending-test-layers: enumerate severity bands inline (Critical/
High/Medium/Low) so the guide-unreachable fallback has a vocabulary
- recommending-test-layers: drop "post-deploy" from the step-4 heading
so it no longer contradicts E2E gating production promotion
- recommending-test-layers: remove unused Grep/Glob from allowed-tools
- README: note the step-4 Confluence severity-guide use for any input
- .cspell.json: drop dead pflag/shlex entries
* fix(bitwarden-testing-tools): restore SKILL.md trigger guard and untangle sibling triggers
Address the major findings from the PR #240 validation review:
- Restore the SKILL.md guard the per-skill runners had. The shared engine
matched a bare skill-token substring, so an exploratory Read of the skill's
own evals/ directory (the eval's CWD) counted as a trigger. Detection now
requires a plugin-qualified Skill invocation (:) or a Read of
the skill's own SKILL.md, on both the streaming and assistant paths. The
plugin token is resolved from --plugin or inferred from the eval-set path.
- Tighten the recommending-test-layers description so its exclusion clause
disclaims layer-worded inventory phrasings, which were cannibalizing the
sibling assessing-test-coverage skill's dual-intent triggers.
- Re-record both baselines with the tightened runner and description. The
sibling's "which layers do they land at" query recovers from below threshold
to a pass; correct the 1.2.1 changelog entry that claimed baselines were
unchanged.
* docs(bitwarden-testing-tools): trim run_real_eval docstring to a README pointer
The module docstring restated the conceptual overview already in
evals/README.md (why a custom runner, what counts as a trigger, token
inference). Collapse it to a short pointer so the README is the single
source of truth and the two stop drifting apart. Inline comments left
intact since they explain code-local decisions.
* docs(bitwarden-testing-tools): consolidate eval READMEs and update changelog
Fold the two per-skill eval READMEs into the shared evals/README.md,
correct the trigger-detection wording to match the plugin-qualified
matching, document the --plugin flag, and record the changelog entries
for the consolidation and timeout-accounting fixes.
* docs(bitwarden-testing-tools): record baseline model and align eval docs
Add a top-level model field to both baseline.json files recording the
model each was captured on, so a re-run can match it via --model. Update
the shared evals/README.md to point runners at that field, drop the stale
recommending-test-layers below-threshold entry now that it passes 10/10,
and correct the runner line count in the changelog.
* docs(bitwarden-testing-tools): collapse unreleased changelog to a single 1.2.0 entry
1.2.0 never shipped (main is 1.1.0), so shipping both 1.2.0 and 1.2.1
left the released history documenting intermediate states (bare-token
detection, two per-skill runners) that 1.2.1 undid within the same PR.
Collapse into one 1.2.0 entry describing only the net change from 1.1.0,
and renumber 1.2.1 -> 1.2.0 across plugin.json, marketplace.json, and
the README catalog.
Also fix eval-doc drift and dead code flagged in review:
- evals/README.md: correct the 'Why a custom runner' line that still
described bare-token detection, and note a timed-out run records as a
non-trigger with a stderr warning.
- run_real_eval.py: drop the dead 'triggered' local (always False) in
favor of the literal on the non-trigger return paths.
* fix(bitwarden-testing-tools): record model in eval summary output
The shared eval runner never emitted the model into its JSON summary, so a
baseline updated by copying a run output dropped the top-level model field the
Arguments section tells the next runner to match. Write model into the summary,
add it to the regression-check projection so a diff against a baseline recorded
on a different model fails, and drop the now-redundant manual model-recording
step from the README.
* docs(bitwarden-testing-tools): baseline trigger evals on claude-opus-4-8
Switch the eval runner default to --model claude-opus-4-8, the model most
users run, and re-record both baseline.json files on it. Both skills pass
every eval query at 10/10 should-trigger and 10/10 should-not-trigger.
Update evals/README.md and the plugin README to match, and trim the 1.2.0
changelog eval entries to the end state (1.2.0 is unreleased, so the
sonnet-to-opus switching history is not relevant to a reader).
* docs(bitwarden-testing-tools): debloat 1.2.0 changelog entries
* fix(bitwarden-testing-tools): address PR #240 review findings
- Gate mis-placement flag (step 5) on inputs that reveal current test
placement; skip inference for bare Jira keys and feature descriptions
- Drop drifting layer enumeration from the skill description
- Collapse the thrice-stated trigger-detection rule in evals/README to a
single statement, restore the run-before-merge gate rule
- Distinguish a crashed claude child (non-zero exit) from a genuine
non-trigger in run_real_eval.py so partial error rates don't silently
depress results
- Correct changelog wording (shared harness, not a per-skill harness)
* test(bitwarden-testing-tools): re-record eval baselines on head (a4d0b83)
Re-ran both trigger evals at --runs-per-query 7 --model claude-opus-4-8
against the merged description, resolving the PR #240 finding that the
baselines were recorded before the description trim in a4d0b83.
- recommending-test-layers: 10/10 / 10/10, byte-identical to the prior
baseline (the description trim was inert), so no file change
- assessing-test-coverage: 10/10 / 10/10; one should-trigger query drifted
7/7 -> 6/7 (rate 0.857, verdict unchanged), recorded here
Both diffed clean against the prior baselines (no verdict flips).
* fix(bitwarden-testing-tools): address SaintPatrck's PR #240 review
- Step 4: phrase the severity guide fetch as a directive and stop echoing
its band names, so the skill cannot drift from the live page
- Step 2: drop the assessing-test-coverage taxonomy mapping details
- Step 1: describe what to read from a PR rather than which commands to run
- Remove the redundant References section
* fix(bitwarden-testing-tools): restore Glob/Grep so recommending-test-layers can locate inputs
---------
Co-authored-by: Claude
无版本号
feat(bitwarden-ai-telemetry): attach the signed-in user's email to hook records (#254)
无版本号
build: pin pnpm through packageManager only (#253)
无版本号
feat(bitwarden-ai-telemetry): warn when records are not reaching the collector (#250)
无版本号
fix(bitwarden-ai-telemetry): derive git context from the edited file (#249)
无版本号
[deps]: Update pnpm to v11.11.0 [SECURITY] (#224)
无版本号
fix(bitwarden-atlassian-tools): declare the optional write token optional (#248)
无版本号
fix(bitwarden-atlassian-tools): start the MCP server on Node releases without Corepack (#246)
无版本号
feat(bitwarden-security-engineer): transition scanner triage from Checkmarx/SonarCloud/Grype to Aikido (#226)
* feat(bitwarden-security-engineer): transition scanner triage from Checkmarx/SonarCloud/Grype to Aikido
Bitwarden has moved SAST, IaC, SCA, secrets, cloud, container, malware,
EOL, and license scanning onto Aikido as a single unified feed. Update
the plugin's triage and dependency-review skills, the agent, and
perform-security-review's evidence gathering to match, and bump to
1.4.0.
- triaging-security-findings: query findings via the aikido:issues
skill instead of the GHAS code-scanning API; triage now flows
through Jira (see VULN-665) rather than tool-specific dismissal
states; documents the Jira comment format, severity mapping, and
the group-scoped-action verification trap
- reviewing-dependencies: Aikido replaces Grype for container/SCA
scanning
- perform-security-review: scan-evidence step now sources SAST/IaC
findings from Aikido instead of the now-empty GHAS code-scanning
endpoint
- agent + READMEs updated to reflect the new toolchain
* fix(bitwarden-security-engineer): address pre-PR review findings on Aikido transition
- Add the resolved Aikido MCP tool to perform-security-review's
allowed-tools so the new scan-evidence step can actually reach it
instead of silently returning nothing
- Restore a False Positive Protocol section to triaging-security-findings
(data-flow tracing, sanitizer-vs-validator, full lifecycle,
documented rationale) dropped during the Aikido rewrite
- Stop grouping the cloud_instance Aikido issue type under "container
images" in reviewing-dependencies — it's a distinct cloud VM/instance
category
- Document the aikido plugin as a prerequisite in the plugin README
- Fix grammar and add a read pointer for the VULN-665 reference
* Update plugins/bitwarden-security-engineer/README.md
Co-authored-by: claude[bot]
* Update README.md
Co-authored-by: claude[bot]
* fix(bitwarden-security-engineer): address PR review findings on Aikido transition
Addresses withinfocus's PR review: inlines the Jira triage rubric into
triaging-security-findings instead of pointing to a mutable Jira
ticket, corrects the severity mapping's Hotfix/Critical row, adds
fallback guidance when the aikido:issues skill is unavailable, trims
stale Grype-comparison language, and compresses/re-categorizes the
CHANGELOG entry per Keep a Changelog conventions.
* fix(bitwarden-security-engineer): address remaining PR review findings on Aikido transition
Grants the agent the Aikido MCP tool it was missing so triaging-security-findings
and reviewing-dependencies can reach the feed when routed through it, updates the
README skills table off the retired Checkmarx/SonarCloud/Grype/GHAS descriptions,
and gives perform-security-review's Aikido evidence the same outcome vocabulary as
its other scanners so a missing session can't read as a clean scan. Bumps the
plugin to 2.1.0 and moves the Aikido-transition changelog entries out of the
already-released 1.4.0 section into their own.
* docs(bitwarden-security-engineer): clarify usage examples and aikido install path
The Usage blocks followed "Install the plugin and invoke the agent:" with no
label, so they read as commands to run rather than example prompts. Reframe the
lead-in the way the other plugin READMEs do.
Prerequisites also pointed `aikido` installs at the `claude-plugins-official`
marketplace. Per PR #228 the plugin is pinned as an external entry in this
marketplace instead, so name the upstream author and give the actual install
command. Depends on #228 landing for that command to resolve.
Co-Authored-By: Claude Opus 5 (1M context)
* fix(bitwarden-security-engineer): correct aikido plugin install source in README
The aikido plugin ships from the claude-plugins-official marketplace,
not as a pinned entry in bitwarden-marketplace. Corrects the install
command and changelog entry accordingly.
* fix(bitwarden-security-engineer): give Agent 2 pre-fetched Aikido SCA/container evidence
perform-security-review barred Agent 2 from calling aikido:issues live
while also telling it to invoke reviewing-dependencies, whose Aikido
data source is that same skill. Step 1-C now pre-fetches open_source
and docker_container findings alongside SAST/IaC so Agent 2 has the
data through the SCAN_EVIDENCE block instead.
* fix(bitwarden-security-engineer): resolve remaining Aikido PR review findings
- Disambiguate empty response from None/Not available in perform-security-review
scan-evidence rules; key Not available to failed/unauthenticated responses
- Reflect Aikido SCA/container evidence (not just SAST/IaC) in README skills
table and Prerequisites for perform-security-review
* fix(bitwarden-security-engineer): address second-round Aikido PR review findings
- Reword agent post-fix verification to report the Jira update for an engineer
to apply, rather than confirm a ticket it has no tool to reach
- Restore the released 2.0.0 CHANGELOG entry verbatim; keep the Aikido step-1-C
wording in the 2.1.0 entry
- Name the Aikido MCP tool alongside Skill(aikido:issues) in the perform-security-review
sub-agent constraints and reviewing-dependencies, so a sub-agent holding the grant
cannot re-fetch pre-provided evidence
- Update tool-grants.md to match step 1-C: scope the prompt/deny wording to the two
gh api scanners, and argue the Aikido MCP grant's safety from exact-name tool scoping
(aikido_issues_list only, not the aikido_ignore_issue mutation)
- Dedupe the CVSS convention: state it in triaging-security-findings, point there from
reviewing-dependencies
- Pin the API version header on triaging-security-findings gh api examples for stable
--jq output
- Condense the 2.1.0 CHANGELOG entry
* Update plugins/bitwarden-security-engineer/skills/triaging-security-findings/SKILL.md
Co-authored-by: claude[bot]
* Update plugins/bitwarden-security-engineer/skills/reviewing-dependencies/SKILL.md
Co-authored-by: Patrick Honkonen
* fix(bitwarden-security-engineer): remove unactionable Aikido grant rationale section
SaintPatrck flagged the "Why the Aikido MCP grant is pre-approved" section
in tool-grants.md as reading like a defense of a review comment rather than
actionable guidance. Removes it and the now-dangling cross-reference, and
drops the matching CHANGELOG mention.
---------
Co-authored-by: claude[bot]
Co-authored-by: Graham Walker
Co-authored-by: Claude Opus 5 (1M context)
Co-authored-by: Patrick Honkonen
无版本号
[deps]: Update npm minor (#242)
无版本号
[deps]: Lock file maintenance (#186)
无版本号
[deps]: Update vitest to v4.1.11 [SECURITY] (#241)
Co-authored-by: renovate[bot]
无版本号
[AI-52] feat: Trigger external plugin pin audits in CI (#230)
无版本号
feat(bitwarden-code-review): recognize confirmed stack layers when validating findings (#219)
A stacked PR is reviewed against the layer below it, so a lower layer
legitimately adds exports and types whose callers land above it. The
rejection criteria assumed a self-contained diff, which turns that design
into dead-code findings on every bottom layer.
Detection is gated rather than inferred from the base ref alone: Bitwarden
runs release branches with cherry-picks, so rc, hotfix-rc, release/*, and
develop all have a non-default base while being ordinary self-contained
changes. Confirming a layer needs a corroborating signal, and the
relaxation never covers a layer that cannot build alone or a security or
correctness defect in code the layer does contain.
无版本号
fix: resolve the diff base from origin/HEAD instead of assuming main (#217)
Local-mode code review and branch-comparison security review both diffed
against a literal `main`, which is the wrong base in any repo whose default
branch is named something else. Both now resolve `origin/HEAD` and ask when
it cannot be resolved, and both gained the `git symbolic-ref` grant needed
to run that resolution.
无版本号
[AI-52] feat: Add auditing-external-claude-plugins skill (#227)
* [AI-52] feat: Add auditing-external-claude-plugins skill
Security audit of a third-party Claude Code plugin at a pinned commit,
before it is vendored into this marketplace. Covers the plugin manifest,
MCP server configuration, bundled or runtime-fetched dependencies, tool
permission scope, failure-mode behavior of network-dependent checks,
prompt-injection surface, secrets, and license.
Runs as an isolated bitwarden-security-engineer subagent on the fable
model, blocking, so it can be triggered manually or by a CI workflow
that posts the report as a PR review comment. Ships a bundled
gather-evidence.sh script so allowed-tools can pre-approve one script
invocation instead of enumerating every git/npm/tar/hash command,
keeping the skill runnable unattended.
Bumps bitwarden-security-engineer 1.3.0 -> 1.4.0 (minor, additive skill).
* Trim changelog entry
* Trim README skill table entry
* Revert .gitignore change
* Address PR review: validate repo URL, guard tar/openssl failures, pin js-beautify
- Reject any $repo_url that isn't https:// or git@ before cloning, and add
-- separators on clone/checkout, closing an ext:: remote-helper RCE and
option-injection path via a leading-dash argument.
- Guard the openssl hash and tar extraction so a failure (including GNU
tar refusing a path-traversal member) emits an explicit marker instead
of aborting the script under set -e before the scratch path is printed.
- Pin js-beautify to the exact version already used successfully in
manual dry runs; the bare @* wildcard let npm's alias/tarball spec
syntax resolve to an arbitrary package.
* Address validate-ai findings: failure markers, arg guard, npm arg injection, output path, cleanup
- gather-evidence.sh: guard against missing arguments; emit NPM_VIEW_FAILED,
NPM_PACK_FAILED, ATTESTATIONS_UNAVAILABLE, and MULTIPLE_NPM_PACKAGES_DETECTED
markers instead of failing silently with || true; add -- before the package
spec on npm view/pack so a dash-leading spec can't be read as a flag
(verified: without --, npm silently mis-parses it; with --, it's rejected
as an invalid package name instead). npm flags must stay before -- or npm
also swallows them as positional args, including --registry.
- scratch dir now uses a fixed, predictable /tmp/plugin-audit.XXXXXXXX prefix
instead of a bare mktemp -d, so cleanup can be scoped to it specifically.
- SKILL.md: any stderr marker must be named in the report's Not done field,
not read as a clean result; default OUTPUT_FILE moves to
/plugin-audits/ instead of a repo-visible path;
js-beautify's grant is now tied to an explicit step (deobfuscating a
minified server entry point); added an unconditional final cleanup step
removing the scratch directory, matching perform-security-review's
precedent in this same plugin; README gets a usage example for the
skill's positional-argument invocation.
Tar/symlink extraction and the ext:: remote-helper claims from the same
review round were independently tested against this environment's tar and
git and did not reproduce; not changed. The npm-registry attestations
@-encoding claim also tested identically either way; not changed.
* [AI-52] fix: Scope allowed-tools to used commands, move js-beautify behind a script
Removes the gh api --method GET allowed-tools grant: gh api is never
invoked by this skill or gather-evidence.sh, only warned against as a
mutation to avoid.
Moves the js-beautify invocation into a new beautify.sh script, mirroring
gather-evidence.sh. The version now lives in a scoped package.json/lockfile
that Renovate's npm manager already tracks, instead of a string in
SKILL.md's frontmatter that could only be bumped by hand. Uses npm rather
than pnpm since npm is already a hard dependency of this skill
(gather-evidence.sh shells out to it), while pnpm/corepack availability in
the skill's actual CI execution environment (a reusable workflow in
bitwarden/gh-actions) can't be verified from this repo.
* [AI-52] fix: Scope scratch-directory deletion to a validating script
Bash(rm -rf /tmp/plugin-audit.*) is a literal prefix match on the whole
command string, so extra paths appended to the same invocation (e.g. rm -rf
/tmp/plugin-audit.ab12cd34 ~/src) would also match and run unprompted. That
matters here specifically: this skill treats the cloned repo as adversarial,
and step 7 has the agent compose the rm invocation from a path it derives
after running that untrusted content through several tools.
Moves the deletion into cleanup.sh, mirroring the beautify.sh remedy shape.
It accepts exactly one argument and refuses to delete anything that doesn't
match gather-evidence.sh's exact mktemp pattern
(/tmp/plugin-audit.XXXXXXXX), so an appended or malformed path is rejected
by the script instead of pre-approved by the allowed-tools rule.
* [AI-52] fix: Bump js-beautify to latest (2.0.3)
Verified the beautify.sh CLI usage (js-beautify -o ) behaves
identically on 2.0.3 against a sample minified file before bumping.
* [AI-52] fix: Default beautify.sh output alongside its input, not the caller's cwd
An unspecified output path previously resolved to ./pretty.js relative to
whatever directory the agent happened to be in, so the de-minified copy of
a third-party bundle landed in the working repo instead of the scratch
directory step 7 cleans up. Defaulting to the input file's own directory
keeps it inside that scratch directory.
* [AI-52] refactor: Drop the scratch-directory cleanup step
Deleting the clone buys nothing on an ephemeral CI runner, and locally it
leaves only inert files in /tmp. That payoff doesn't justify a third
script, a wildcard allowed-tools grant, and a path guard that has to hold
against adversarial input. The case pattern it used matched separators
anyway, so /tmp/plugin-audit.../../.. passed validation and resolved to /.
* [AI-52] fix: Neutralize symlinks in the audited clone and tarball
The audit tells the agent to read every file in an untrusted tree, report
any secret it finds with the value, and the caller posts that report to a
public PR comment. A committed symlink turns that into an exfiltration
primitive with no prompt injection required: docs/notes.txt pointing at
../../../.git/config makes the agent read the runner's GITHUB_TOKEN and
dutifully publish it, because every component behaved as designed.
Cloning with core.symlinks=false writes each committed symlink as a plain
file holding its target path, and the config persists into the repo so the
detached checkout stays safe too. Tarball members get recorded and deleted,
since tar refuses to write through an existing symlink but will happily
create one pointing anywhere. Both inventories land in symlinks.txt, where
a target outside the tree is itself a finding.
* [AI-52] refactor: Stop enumerating gather-evidence.sh's stderr markers
The script's own messages already say what evidence is missing, so listing
the marker names here only couples SKILL.md to the script: a new marker
means editing both files, and a forgotten one reads as an approved
omission. NO_NPM_PACKAGE_DETECTED stays named because it calls for extra
manual work rather than just a Not done entry.
* [AI-52] refactor: Have gather-evidence.sh report its own inventory
SKILL.md carried a prose copy of the script's output layout, which only the
script knows at runtime and which drifts the moment either side changes. The
script now ends with an inventory: SCRATCH_DIR as a named key rather than a
positional last line, one row per artifact that exists, and a NOT COLLECTED
list of the markers it emitted. SKILL.md keeps the judgment that does not
belong in a shell script, namely that a NOT COLLECTED entry goes in Not done
and an escaping symlink target is a finding.
Every label in the inventory is a fixed string or a count. Nothing derived
from the audited repo is echoed there, because script output reads as more
trustworthy to the agent than the file contents it describes.
A row is printed only for a non-empty file, and a failed npm view now has
its error-object output removed, so a fetch that failed cannot leave behind
something a later read mistakes for registry metadata.
Also moves the URL validation above mktemp and adds an EXIT trap, so a
rejected URL creates no scratch directory and a mid-script failure removes
the partial clone instead of orphaning adversarial content that the caller
never learned the path to.
* [AI-52] fix: Fail the attestation fetch closed on a missing attestation
A 404 from the attestations endpoint means the package is unsigned, which
is a security-relevant answer rather than a transport error. Without -f
curl exited 0 and saved {"error":"Not found"} to attestations.json, so no
marker fired and the agent was handed a file that reads as attestation
data. The inventory would then vouch for it as collected evidence.
-f makes that a failure, the file is removed so nothing can be mistaken for
provenance, and the marker says to treat the package as unsigned unless
proven otherwise. --max-time bounds a stalled connection, which previously
could hang the audit with no ceiling.
* [AI-52] fix: Keep credential values out of the audit report
The report is published verbatim to a public pull request comment, and the
skill previously told the agent to find hardcoded secrets and describe each
finding concretely, with nothing saying the value itself must stay out. A
secret the audited plugin committed would be disclosed by us on discovery,
and one belonging to the auditing machine would be exfiltrated by the same
sentence that asked for it.
States the publication boundary next to the existing untrusted-data
boundary, since both govern everything the agent does rather than any one
step, and restates it in the Where field where the instinct to paste a
token actually lands. Location, kind, and a SHA-256 prefix keep a finding
verifiable by a human without republishing the credential.
* [AI-52] fix: Treat a failed npm audit as a missing report, not a clean one
npm audit exits non-zero both when it finds vulnerabilities, which is a
real report, and when it fails outright, in which case it writes an error
object to stdout. The exit status cannot tell those apart, so a registry or
proxy failure landed that error object in audit.json, where a non-empty
file read as collected evidence and no marker fired. An audit that never
ran was presented as an audit that found nothing.
Keys off auditReportVersion, which only a real report carries, so a
vulnerabilities-found report survives while a failure removes the file and
raises the marker. That also subsumes the missing-lockfile case, where
audit never ran and the file was never created, which is why there is now
one message for both rather than two that ask for the same action.
无版本号
Update release notes skill (#223)
* Update release notes skill
Improve the release notes writing skill to account for already shipped work that is having its feature flag enabled for the first time with this release.
* fix for refactor flags
* Address review feedback
无版本号
Add jira write tool to write comments (#216)
* AI-49 add jira write tool to write comments
* fixing issues based on comments
* updating based on comments
* fixing addComment instances
* fixing file names
* adding end to end test
无版本号
fix(bitwarden-code-review): grant git status where a local review is told to run it (#220)
`code-review-local` instructs the reviewer to inspect the working tree with
`git status`, and its README documents that behavior, but neither the command
nor the agent listed the grant — so the call prompts or fails on the local
path while the multi-agent path, which already grants it, works.
无版本号
fix(claude-config-validator): close three false negatives in security-scan.sh (#203)
* fix(claude-config-validator): close three false negatives in check 1 and the permission checks
Rebased onto the current #201, which absorbed the secret-filter and verdict work
this branch previously carried. #201 anchors placeholder patterns to value
position and adds the INCOMPLETE verdict with exit 2, both better than what was
here, so only the gaps it does not cover remain.
Check 1 still piped git ls-files into grep -q. Under pipefail a match makes grep
exit first, git dies of SIGPIPE with 141, and the pipeline reports failure, so a
committed settings.local.json read as absent. Reproduced against #201 at 20k
tracked files. It also ran without -C, so the check read whatever repository the
shell was in and reported a pass for a target outside one.
The two permission checks still printed "(OK)" when there was no settings.json to
read, which #201's own skipped accounting then did not see.
Add security-scan.test.sh. Eight of its fifteen assertions fail against #201 as it
stands. Credential fixtures are assembled at run time so the file holds no string
a secret scanner should flag, which keeps the scanner's exclusion list from having
to grow to accommodate its own tests.
* fix(claude-config-validator): answer the open review threads on the scanner
An absent settings.json is a determinate answer rather than a check that could
not run: no file means no rules are set in the scanned directory. Marking it
skipped drove a common .claude shape to INCOMPLETE and exit 2, overstating the
doubt in the direction opposite to the false negatives this branch closes. It
reports the result plainly now, and the README states that rules reaching the
session from elsewhere are outside what the scan sees.
Correct the README sentence saying the settings.local.json check does not report
itself as skipped, which check 1 now does outside a git repository.
Make the clean-configuration case jq-aware. Check 3's rule tests need jq, so
without it the run is legitimately incomplete and the old assertion failed on any
machine lacking it, which contradicted the no-new-dependency claim.
Document the suite and how to run it in the scanner section, including that
nothing runs it automatically.
* fix(claude-config-validator): make case 1 actually reproduce the pipeline failure
The fillers sat in the repository root, outside the scanned directory, so with
-C and a pathspec git wrote one line and the pipe never filled. The case caught
the -C fix but not the no-pipe half: reintroducing `| grep -q` still passed.
Moving the fillers under the scanned directory and matching the pathspec puts
roughly 500 KB past the 64 KB buffer, and reintroducing the pipe now fails the
case. Dropping xargs -n1 collapses 20k process spawns, so the suite runs in
about six seconds.
security-patterns.md documented check 1 as the pre-fix command, teaching the
false pass this branch removes and contradicting the file's own note that only
the check 3 and check 4 blocks mirror the script. The manual form now guards the
repository first and asks git with a pathspec, and the duplicated script block
points at the shipped script instead.
* chore(claude-config-validator): renumber to 2.0.2 above the merged 2.0.1
marketplace.json, plugin.json, and the README catalog row, with the changelog
section retitled so it sits above #208's entry rather than replacing it.
* fix(claude-config-validator): scope check 1 to the repository, not the scanned subtree
The -C fix targeted CLAUDE_DIR, which narrowed the check from the repository to
the .claude subtree: a committed settings.local.json anywhere else reported as
absent, and the paths listed no longer matched the remediation printed below
them. Reproduced with apps/foo/.claude/settings.local.json in a monorepo. It
resolves the scanned directory's worktree root first, which keeps the original
coverage while still targeting the target's repository rather than the shell's.
Route an ls-files failure to skipped rather than the OK branch, so a genuine
error cannot read as a pass.
Give case 5 a positive assertion. Built only from reject assertions, it passed
whenever the scanner produced no output at all, including an early exit, which
made it the one case where a broken scanner still showed green. Add a case for
the repository-wide coverage; reverting to subtree scope fails it.
* fix(claude-config-validator): widen the documented check to the worktree root
5f5d126 widened the script but left the reference's manual command asking git
from .claude, so a human following it on a monorepo got the pass this branch
removes. Same drift class as the README sentence and the Automated Detection
block already corrected here, in a third place.
Verified against a monorepo fixture: the old form returns nothing, the new form
returns apps/foo/.claude/settings.local.json.
无版本号
feat(bitwarden-code-review): route changed SKILL.md to plugin-dev:skill-reviewer (#206)
* feat(bitwarden-code-review): route changed SKILL.md to plugin-dev:skill-reviewer
claude-config-validator 2.0.0 declines SKILL.md, and neither review path
invoked plugin-dev:skill-reviewer, so skill changes were left uncovered
while both paths still advertised progressive-disclosure review.
The multi-agent pipeline gains a conditional Agent 5 on the
plugin-dev:skill-reviewer subagent type, emitting source_agent "skill"
with id prefix skl. The single-agent reviewer gains Task, scoped by its
definition to that one delegation.
* fix(bitwarden-code-review): detect skill support files under references/
Nine of this repo's ten skill support directories are references/, so
enumerating only reference/ dropped them from Claude-configuration
detection.
* fix(bitwarden-code-review): record a skipped skill review instead of omitting silently
SKILL.md sits outside the Claude-configuration bucket, so a missing
plugin-dev leaves no agent applying the skill lens. The report gains a
Not covered line so the omission cannot read as a pass.
* fix(bitwarden-code-review): reconcile Agent 5 with the skill-reviewer contract
plugin-dev:skill-reviewer holds Read, Grep, Glob and emits a fixed prose
report, so it can satisfy neither the Review Rules bundle nor the Finding
Shape schema. The orchestrator now translates its report in Step 3 under
a named carve-out.
SKILL.md also stays in the scope handed to reviewing-claude-config: its
credential scan runs over every file whatever the type, and only its
routing declines the file, so dropping it from that bucket had removed
the secret scan.
Task cannot be scoped per subagent in frontmatter, so the limit is
documented as a convention rather than a boundary.
* fix(bitwarden-code-review): drop the Task grant and tighten Agent 5's contract
A spawned subagent takes its tools from its own definition, so Task put
unrestricted Bash one delegation from an agent that reads contributor
diffs unattended in CI. The single-agent path now states that skill
content review did not run and names the pipeline that covers it.
Agent 5 gains a scope fence, keeps Tool Discipline minus its gh/git
bullet, takes Untrusted Input Boundary widened from hunks to whole files,
harvests the reviewer's section-level issue lists, and is classified
against the pipeline's own severity definitions.
* fix(bitwarden-code-review): give Agent 5 a verbatim input boundary and order the fallback
The carve-out described Agent 5's untrusted-input boundary as a rewording
rather than supplying it, leaving the pipeline's one whole-file reader with
the only boundary composed fresh each run. It is now a verbatim block, and
the three sections asserting propagation to every subagent name the
exception.
In the single-agent path, the general fallback to existing review knowledge
trailed the skill bullet and reversed its instruction not to substitute an
opinion for skill review.
* fix(bitwarden-code-review): rest Agent 5's carve-out on instruction, not declaration
The carve-out justified a reduced rule bundle from the callee's frontmatter,
which this pipeline does not enforce and which harnesses have been seen
ignoring. Agent 5's Tool Discipline variant now instructs the tool
restriction directly, keeps the WebFetch/WebSearch ban that dropping the
first bullet had removed, and carries an exception for the referenced-file
check its brief requires.
Its prompt blocks and translation procedure move to
references/agent-5-skill-review.md. Support-file detection returns to
open-ended, which had silently dropped evals/, contexts/, and templates/.
* fix(bitwarden-code-review): let injection findings survive Agent 5's scope fence
The widened boundary tells Agent 5 to report a file that tries to direct
its review, then the fence dropped anything the diff did not touch, which
is exactly where a planted injection sits. Nothing else covers it:
reviewing-claude-config declines SKILL.md for content and Agent 4 gets
those files for the credential scan only.
The coverage note gains a real slot in posting-review-summary rather than
relying on the reporting agent to improvise one, and its gate keys on
whether Agent 5 returned a usable review rather than on why it did not.
* fix(bitwarden-code-review): stop a clean skill review reporting as uncovered
Gating the Not covered line on translated finding count made the common
case a false positive: the scope fence and the severity bars are meant to
empty the list, so a skill change whose issues are all pre-existing would
report the lens as never run. The gate keys on whether Agent 5 returned
anything reviewable, which the orchestrator can observe without counting
findings.
* fix(bitwarden-code-review): use a word cspell knows for a malformed return
* fix(bitwarden-code-review): carve CWE-1427 out of Step 4's pre-existing test
Letting an injection finding clear Agent 5's scope fence achieved nothing
while Step 4 dismissed it on the same ground one layer down, so the
observation reached the Dismissed block rather than the report.
The README no longer promises the threat-model preamble to every subagent,
which Agent 5 is now a full exception to, and the single-agent path drops
a reviewing-claude-config finding that only restates the coverage gap its
own summary line already owns.
* fix(bitwarden-code-review): give the coverage note a reason its own path can state
The worked example asserted plugin-dev was not installed, but the single
agent path cannot reach skill-reviewer whatever is installed, and it then
pointed at a remedy that same absence would block. As the section's only
example it is what a real comment gets patterned on.
* fix(bitwarden-code-review): launch one Agent 5 per changed SKILL.md
plugin-dev:skill-reviewer's output contract is one skill's report, so a
plural path list can blend two skills into a report whose description,
length, and progressive-disclosure sections name no file — the properties
the agent exists to cover. Number skl ids across instances, and let the
coverage line name only the files that went unreviewed.
* fix(bitwarden-code-review): carry the CWE-1427 exception into Do Not Flag
Step 5 receives the Review Rules, whose unqualified pre-existing bullet
told it to dismiss what Step 4's exception had just kept. Filter the
second list to SKILL.md paths that survive the change, so a deleted skill
does not launch an Agent 5 instance or report a gap for its own removal.
* fix(bitwarden-code-review): close the validator findings on the Agent 5 change
Operating Rules still promised Tool Discipline verbatim to every subagent,
which would hand Agent 5 a Bash instruction contradicting the restriction
its reduced rules bundle rests on. Context Partitioning's ONLY clause
omitted the one path the agent needs, and its diff is now narrowed to the
skill under review. Both coverage-note exemplars name their files, the
support-file-only case is stated rather than left silent, and the summary
skill's render condition no longer turns on a word it never defines.
* fix(bitwarden-code-review): gate the single-agent config review on SKILL.md too
Dropping SKILL.md from both enumerations left a SKILL.md-only pull request
matching neither, so the clause handing those files to reviewing-claude-config
sat inside a bullet that never fired and the credential scan was lost on that
path. Gate on the disjunction the way the pipeline already does. Qualify the
two places that promised Agent 5 the whole diff, and stop the coverage note
firing for an optional skill that falls back silently.
* fix(bitwarden-code-review): narrow the coverage note in the block agents copy
The render condition was tightened in the prose and left broad in the summary
template beside it, which is the copy an agent assembles output from. Scope
both to what this review covered, so naming the path that does cover the gap
no longer reads as a reason to suppress the line. Label the example by the
reviewer whose tool grant the reason belongs to rather than by an output mode
the file defines as something else, and settle the Agent 5 hunk scope on the
skill directory in all three places that state it.
* fix(bitwarden-code-review): filter deleted SKILL.md out of the single-agent gates
The SKILL.md disjunct these gates gained admitted deleted paths, so a skill
split or rename would draw a coverage note asserting the removed file went
unreviewed. The pipeline path already filters on presence at the head of the
change; carry the same condition through all three gates on this path, so a
later edit to one cannot leave the others behind.
无版本号
[BRE-1833] feat(bitwarden-devops-engineer): Add managing-workflow-secrets skill (#205)
无版本号
feat: add creating-jira-tickets delivery skill (#170)
* feat: add creating-jira-tickets delivery skill
Turns a tech breakdown's tasks.md into Jira tickets — an epic plus one
child story/task per task entry, with real ticket titles, a Gherkin
Acceptance criteria ADF section, and wired Blocked-by/Depends-on links.
Creates tickets one at a time behind a human-in-the-loop review gate and
goes through the acli CLI. Includes an acli/ADF reference and a
skill-creator eval set (7 behavior cases + a 10/10 trigger set).
Bumps bitwarden-delivery-tools 2.1.0 -> 2.2.0 (marketplace.json,
plugin.json, README, CHANGELOG) and adds acli/workitem to the cspell
dictionary.
* Fix inverted link-list read-back example in acli reference
* Replace creating-jira-tickets with filing-breakdown-tasks skill
* Removed text
* Trim filing-breakdown-tasks docs and record an eval baseline
Descriptions and changelog cut to the length the surrounding entries use.
The eval runner is a copy of the creating-pull-request sibling with a
repeatable --plugin-dir, which the baseline run needs: filing-jira-tickets
is what the should-not-trigger near-misses are written against, and it is
not in the installed cache. Its output now records model, plugin_dirs and
runs_per_query, so a baseline carries the conditions that produced it.
baseline.json: 20/20 on claude-opus-5 at 3 runs with both plugin roots
loaded.
* Drop vestigial Owner capture from filing-breakdown-tasks
无版本号
[QA]Port over qa test writing skill from test repo (#200)
* Port over qa test writing skill from test repo
* Address PR Feedback, remove AskUserQuestion from allowed-tools, change where the files are written to
* Port over qa test writing skill from test repo
* Slight tweak around mapping priority
* Address Claude review feedback - align canonical example with the underlying rules, add more specificity about escaping double quotes
* Fix typo
* Update plugins/bitwarden-testing-tools/skills/writing-manual-test-cases/SKILL.md
Co-authored-by: Patrick Honkonen
---------
Co-authored-by: Patrick Honkonen
无版本号
fix(claude-config-validator): stop two skills failing to load on quoted shell syntax (#208)
Claude Code expands a bash-execution block on the raw text of any file it
loads, so a skill that quotes the syntax to teach it runs the quoted
fragment as a shell command and fails. reviewing-claude-config carried one
in an inline code span; reviewing-command-definitions carried four plus a
fenced example.
Fenced blocks are not a safe haven: plugin-dev:command-development fails to
load for the same reason, from a fenced block.
无版本号
feat(claude-config-validator)!: fence findings to the changeset, split the review skill, and stop guessing at commands (#201)
* feat(claude-config-validator)!: fence findings to the changeset and split the review skill
Report only what a changeset introduced or worsened, fail only on CRITICAL, and
replace the checklist tree with four targeted review skills.
BREAKING CHANGE: reviewing-claude-config is now a router. The checklists/ and
examples/ directories are removed, and SKILL.md review belongs to
plugin-dev:skill-reviewer.
* fix(claude-config-validator): close review findings in the guardrail change
Restore the hook credential-access check dropped in the checklist split, add a
security floor to the verdict so a widened permission or tool grant fails the run
regardless of tier, exempt CRITICAL and security findings from the filter, add a
fallback routing row for skill support files, and carry the CWE-1427 boundary into
each targeted skill.
Also realign the READMEs and the marketplace escalation with the new structure.
* fix(claude-config-validator): gate the frontmatter skip on actual coverage
Skip agent frontmatter review only where plugin-dev:plugin-validator ran, rather
than wherever the file sits inside a plugin, so an uninstalled plugin-dev cannot
silence a CRITICAL band. Restore the permission-example wildcards, and align both
READMEs with the security floor and the coverage the targeted skills actually have.
* fix(claude-config-validator): correct the permission schema the skills teach
Permission rules are Tool(specifier) under permissions.allow/deny; the skills and
both references taught a top-level autoApprovedTools array with bare Tool:specifier
rules, which Claude Code does not read, so a reviewer following them would grep for
a key that never appears and report an over-broad settings file clean.
Also qualify the routing table's four skill names, cover shell execution and argument
interpolation in command review, gate the command frontmatter skip on actual coverage,
and carry the verdict's security floor into the remaining statements of it.
* fix(claude-config-validator): correct the permission rule grammar and the detectors
A bare Tool rule is valid and is the broadest grant available, so instructing
reviewers that every rule is Tool(specifier) filed a CRITICAL against a working
deny: ["WebFetch"] while calibrating them past the real unbounded grant. Scope that
finding to the two forms Claude Code genuinely does not read.
The detection greps, detect-broad-permissions.sh, and the shipped security-scan.sh
still matched the old notation, so all three reported a filesystem-wide grant as
scoped. Also settle // as absolute against a single leading slash as settings-relative,
give command review a real frontmatter pass, and gate the hook carve-out on actual
coverage like its two siblings.
* fix(claude-config-validator): use low-entropy placeholders for the secret examples
The example credentials had to match the quantifiers in the regexes the skill
points at, but a realistic-looking string of that length reads as a live secret to
a scanner. Repeated EXAMPLE text satisfies the length the regexes require without
the entropy that makes it look real.
* fix(claude-config-validator): stop telling reviewers that quoting closes shell injection
Pass 7 said quoting $ARGUMENTS inside a !`cmd` block closes the hole and is not a
finding. Substitution is textual and happens before the shell parses the line, so
$(rm -rf ~) still executes inside double quotes and 1" ; rm -rf ~ ; " closes the
author's quote outright. Both verified. Interpolation is now a finding either way,
with stdin or a validated allowlist as the remedy.
Also correlate the bare-tool-rule detector against permissions.allow so a bare rule
in deny no longer fails the scan, exclude placeholder text from secret detection now
that the teaching examples live inline, and correct the command frontmatter severity:
frontmatter is optional for commands, so a missing description does not stop loading.
* docs(claude-config-validator): drop the superseded quoting claim from the changelog
* fix(claude-config-validator): guard the documented detector and scope frontmatter severity
The bare-rule detector in security-patterns.md had no jq guard, so with jq absent the
branch was skipped, ISSUES stayed 0, and it printed "Permissions appropriately
scoped" for a check that never ran. The shipped script already guarded this; both
copies now agree.
Frontmatter severity is stated per file type, since commands do not require it and an
unqualified CRITICAL row failed a valid frontmatter-less command. The model-value
correction now reaches all four statements in the reference, including the detection
table a reviewer classifies from.
* fix(claude-config-validator): extend the security floor to shell-execution findings
The floor enumerated permissions, tool grants, and hook capabilities, so a changeset
adding a quoted $ARGUMENTS interpolation inside a !`cmd` block produced one IMPORTANT
finding, widened nothing enumerated, and reported Pass with a live injection vector
listed underneath. It now covers any new path from contributor-controlled input to a
shell, in all three copies of the clause.
Also annotate the Grep-path bare-rule pattern as candidate-only, since it cannot tell
allow from deny and the router has no Bash to reach the jq version, and carry the
model-identifier correction to the last two of six statements.
* fix(claude-config-validator): bring hook input guidance to parity with commands
reviewing-command-definitions asserted that the sibling applied the same rule to
hook input. It did not, and it should not: verified by execution, a quoted value in
a shell variable used directly is safe, the same value handed to a nested shell such
as bash -c is not, and slash-command substitution has no safe quoted form at all.
Both skills now state the three cases rather than one claiming agreement.
Also add a settings pass for the fields that execute or bypass, since defaultMode:
bypassPermissions disables prompting wholesale and matched no rule-shaped check; give
priority-framework hook and settings severity tables, which it had none of while a
skill delegated hook classification to it; settle the command tier in one place; and
write the report rather than asking which PR to validate when nobody can answer.
* fix(claude-config-validator): stop the secret scanner dropping keys by file path
The placeholder filter added in 47ea728 ran over the whole grep -r record, which
begins with the path, so a real key in any file under an example-named path was
discarded and the scan reported a pass. Verified by execution both ways: an identical
40-char sk- key in example-agent.md and prod-agent.md is now reported for both, and
the plugin's own teaching examples still produce no finding.
Also reconcile hook severity, which contradicted itself four lines apart: a quoted
value consumed directly by the command is not a finding, unquoted is CRITICAL, and a
nested shell is CRITICAL either way. Drop the blanket permission elevation in Security
Context, which overrode the per-field tables and falsified the security floor's own
rationale, and extend agent tool-access review to Skill alongside Task.
* fix(claude-config-validator): anchor the third secret filter to the value
e5d374f narrowed the OpenAI and GitHub token filters from the whole grep record to
the value and did not bring the generic-credential check along, so it still discarded
any match whose file path contained example or xxx. Reproduced the reported fixture:
three identical credentials, one reported. All three are reported now, and the
plugin's own teaching examples still produce no finding.
Also take acceptEdits out of the block whose closing sentence makes every item
CRITICAL, since the framework rates it IMPORTANT and asks why rather than blocking,
and state in the verdict that a widening the changeset justifies is not a finding at
all, which is what makes asking possible. Move the Migration heading below the five
Fixed entries it had captured.
* fix(claude-config-validator): close three false negatives in security-scan.sh
Verified by execution before and after. A raw sk- key under any examples/ directory
reported "All security checks passed", because the path exclusion on the two token
filters dropped it and the generic filter cannot see a key with no apiKey: prefix.
Both filters are anchored to the value now, as the generic one already was.
The sensitive-path loop grepped the whole settings file, so a config whose only
entries are hardening denies failed the scan, contradicting the framework row this PR
adds saying such a rule is the control. It reads permissions.allow via jq and records
itself skipped without jq.
Also drop additionalDirectories from the block that tiers every item CRITICAL, hoist
the agent scope exclusion ahead of Pass 1, and reword a CRITICAL checkbox that read as
a platform guarantee rather than a condition to confirm.
Per review: cut the 2.0.0 changelog entry from 78 bullets to 17, and remove the
skill-level README, moving the scanner usage it uniquely held to the plugin root.
* fix(claude-config-validator): make skipped checks reach the verdict and scope check 4
Regressions from 88a366c, all reproduced before and after. With jq absent, a settings
file granting bare Bash and Read(//Users/x/.ssh/**) printed "All security checks
passed" while the same commit added a README line promising skipped checks are never
reported as passed. Skips are counted now and a clean-but-incomplete run exits 2.
That rewrite also dropped /etc from the sensitive-path list, which two other
statements still said was reported, and left the documented twin grepping the whole
settings file so a hardening deny failed it.
Check 4 had the same whole-file problem plus four patterns whose unescaped pipe made
them ERE alternations, so any quoted string containing curl tripped them. Both fixed
in the script and the documented copy, which now share one path list.
* fix(claude-config-validator): anchor .config and finish the skipped-check sweep
484fe38 unified the two sensitive-path lists and moved .config from a whole-file grep
into a contains() loop, where it matches vite.config.ts, jest.config.js, and a
Bash(npx jest --config ...) rule. Reproduced: the scan exited 1 on a benign edit rule.
Anchored to .config/, which still matches Read(//Users/x/.config/**).
The same commit added a skip branch to detect-dangerous-commands.sh without the
counter its sibling block already had, so it printed SKIPPED and then exited 0 while
the changelog claimed both copies gate their verdict. Every skip branch in both files
is now checked to increment: six of six.
Also annotate the reviewer-facing dangerous-command grep as candidates-only and anchor
dd as dd if=. That is the block two skills tell reviewers to reuse, and it read as
allow-only while being file-wide.
* fix(claude-config-validator): report a settings.json that does not parse
jq exits non-zero on a parse error exactly as it does on no-match, and the jq
conversion in this PR swallowed both. A malformed settings.json holding a bare Bash
grant, an ssh read grant, and Bash(rm -rf:*) produced "All security checks passed"
and exit 0. Before the conversion those three checks were greps that fired
regardless of whether the file parsed, so this is a regression the PR introduced.
Invalid JSON is now CRITICAL, matching what reviewing-runtime-configuration already
rates it, in the script and both documented detectors. Verified that valid files
still report all three grants and that the jq-absent path still exits 2.
Also reconcile two pairs of contradicting instructions: whether the agent frontmatter
pass runs by default, and whether a quoted $ARGUMENTS interpolation is CRITICAL when
the same file demonstrates it is arbitrary command execution.
* fix(claude-config-validator): stop checks that did not run reporting as passed
* fix(claude-config-validator): detect the colon-form permission rule it rates CRITICAL
* fix(claude-config-validator): list what a configuration runs instead of classifying it
* docs(claude-config-validator): scope the scanner's coverage claim to the check that makes it
无版本号
[AI-88] docs: Update reference to the retired Claude context guide (#204)
The per-repo .claude/CONTRIBUTING.md convention is retired, so this points repo-specific guidance at the repository's .claude/ directory instead of a file that no longer exists.
无版本号
fix(claude-config-validator): match the skill's documented behavior to its tool grants (#199)
* fix(claude-config-validator): make the skill's documented behavior match its grants
The skill's allowed-tools is Read, Grep, Glob, but its output section made inline
pull request comments the required mode and exempted only the two commands, so the
default path could not be carried out on a direct invocation. Findings are now
per-issue text by default; comment posting and the single-document report contract
are what an invoking context can supply.
The cross-plugin secret-detection enrichment called Skill(detecting-secrets)
without holding Skill. Conditioned on the grant rather than widening allowed-tools,
since a validator that preaches least privilege should not grant itself the ability
to invoke arbitrary skills to reach one optional enrichment.
Narrow Bash(git fetch:*) to origin, which is all step 1 fetches, and record the
shared-host caveat for the fixed /tmp report path.
* fix(claude-config-validator): finish the README coherence pass and order the output cases
The first pass left the same capability claim in five more places: the skill
README's process step and Review Output Format section, and the plugin README's
feedback heading and two output descriptions.
Order the three output cases specific to general with a first-match rule. /validate-ai
holds a comment-posting grant, so it matched two bullets in an unordered list.
* fix(claude-config-validator): retire the inline-comment instruction on the routing path
checklists/agents.md carried the retired instruction under its Process heading,
and SKILL.md step 3 loads checklists into context, so a direct invocation still
received it. The occurrences in checklists/prompts.md and under examples/ are
sample content rather than reviewer instructions and are left alone.
Finish the skill README: the always-first security list claimed the git-tracking
check runs unconditionally and dropped the finish-remaining-checks half of the
rule, the feature list and tree still advertised executable automation, and
Requirements listed Git and Bash as the skill's rather than the helper's.
Also fold in the duplicated priority-level block, which had drifted to contradict
its own earlier copy, point Troubleshooting at /plugin list for plugin installs,
drop an unsourced statistic, and repair a sentence that inverted its own subject.
Thin the 1.2.2 changelog entries to one line each.
* fix(claude-config-validator): finish retiring the inline-comment mode in the output templates
SKILL.md step 5 loads examples/ as the format template for the file type under
review, so the retired instruction in example-prompts-review.md, the two other
examples, and the prompts checklist exemplar still propagated. Reworded all four.
Correct the shared-host caveat: an allow rule applies only when the symlink and
its target both match, so Edit(//tmp/validation-summary.md) prompts rather than
following a hostile symlink. World-readability is the real exposure.
Mirror the git-tracking correction into the plugin README, replace SKILL.md's
"mental checks" with the Grep mechanism the same step prescribes, and drop the
remaining unsourced statistics from the checklists, examples, and plugin README.
* fix(claude-config-validator): drop the never-update rule from the comment branch
The branch carried "Create new comments rather than updating existing ones"
verbatim from the inline mode this PR retires, and it contradicts the plugin's
only comment-posting caller: /validate-ai upserts a single sticky comment keyed
on its marker. Defer to the invoking context's convention instead.
State the precedence ordering in the skill README too, which presented the three
destinations as co-equal.
* fix(claude-config-validator): return findings to the caller instead of posting them
The comment-posting branch had no caller that wanted it. bitwarden-code-reviewer
holds an inline-comment tool but tells the skill to "fold its findings into your
own classification and validation", and the multi-agent config subagent expects
findings per its Finding Shape schema. Posting directly would bypass both
pipelines, plus the false-positive gate and the low-severity cap. Step 5 is now
two cases: the validate-ai report contract, or return the findings as text.
State the grant-composition rule once at the top. The file previously reasoned
from the skill's own allowed-tools in one place and the caller's grants in
another, which cannot both be the ceiling.
Carry the settings.local.json qualification up into both Features lists and the
frontmatter description, scope the CRITICAL CHECKS line to the pattern checks it
actually covers, give the direct-invocation path a defined scope source, note
that only origin is fetched, record that the Edit grant covers the Write tool,
and finish the vocabulary rename across the examples and priority framework.
* fix(claude-config-validator): align the checklists and skill README with step 5
Round-2 validation caught the skill README still documenting the three-case
routing order, including the posting case, and citing the SKILL.md section that
now forbids it.
checklists/settings.md told the reviewer to stop the review on a security
finding, reversing a fix 1.2.0 already shipped, and stated the settings.local.json
check as a git-tracking fact ten lines before stating it correctly.
Harmonize the wording everywhere on "appears in the changeset", and scope the
/validate-ai description to what it does in CI.
* fix(claude-config-validator): close the last capability claims from round-3 validation
The direct-invocation list still opened with detecting recently modified files,
which needs a changed-files list the skill has no way to obtain. Check 1 of the
security patterns reference still framed the settings.local.json check as git
tracking with no changeset alternative, and two headings kept the old vocabulary.
* fix(claude-config-validator): drop review-event vocabulary and repair broken references
The output format still ended in APPROVE / REQUEST CHANGES, which are GitHub
review-event values from the posting mode this release retires, and which the
report contract cannot consume. The verdict is now Pass or Issues found, with the
caller mapping to its own vocabulary, updated across the README and four examples.
Nine relative references were broken: six checklists cited SKILL.md as a sibling,
and two examples cited reference/ and checklists/ as if they sat alongside. This
is the plugin's own broken-file-reference finding class.
Scope the marketplace use case to the component files the skill can review and
point manifest checks at the command that delegates them. Align the skills
pass list with the checklist's five passes, restate the strategy list as the
five actual steps, add checklists/ to the disclosure criteria both commands
pass to the skill reviewer, and finish the changeset wording in the settings and
skills checklists.
Also from the notes: hooks.md licensed abandoning the review on a finding, and a
heading's globs were being eaten by markdown emphasis. Trim both command
descriptions, which are what /help renders.
* fix(claude-config-validator): finish the verdict sweep and tighten step 2
The settings example kept a third verdict spelling, BLOCK, so the earlier sweep for
APPROVE and REQUEST CHANGES missed it. All seven verdict lines now read Pass or
Issues found, which is what the report contract consumes.
Step 2 listed the same credential check twice and omitted the dangerous
auto-approval check that both the README and the security reference advertise as
always run. Step 5 stated the per-issue rule twice; the Per-Issue Rules block is
the more complete of the two.
Note that a changeset touching only skill support files lands in no component
bucket, so the configuration and security row does not fire for it. Widening the
pattern would put the reference out of step with the action it mirrors.
State the 2.1.210 floor in the plugin README's Installation section, convert the
new grant section to imperative bullets to match the rest of the file, and drop a
step number that collided with this skill's own numbering.
* fix(claude-config-validator): correct the scope gloss and repair security-scan.sh
The skill-bucket gloss was written for plugin-hosted skills and inverted for a
repo-level one: .claude/skills/foo/reference/bar.md matches the config bucket, so
components-changed is true and the configuration row does fire, while changed-plugins
is empty so plugin validation does not. A reviewer following it would have skipped
the one review that applied. Qualified both paths, mirroring the hook-files bullet.
security-scan.sh had two silent-pass paths and one false positive. git ls-files ran
against whatever repository the shell was in, and outside a repository the error was
swallowed and the check printed a pass it never performed; it now runs with -C and
reports a non-repository as skipped. grep -v "example" discarded any line containing
that substring, including real credentials; the anchored path exclusions already
cover the fixtures. checklists/ was missing from those exclusions, so the script
reported the plugin's own teaching material as a critical finding. Verified all
three by running it before and after.
security-patterns.md embedded a copy of the script that had drifted 134 lines from
the shipped file, including a different directory resolution that would silently
find nothing; it now points at the file.
Also finish the verdict and finish-remaining-checks sweeps, document allowed-tools
in the frontmatter reference, bridge the two meanings of a changed skill, and record
the omitted changes in the changelog.
* fix(claude-config-validator): close a load-dependent false negative in check 1
Under pipefail, `git ls-files | grep -q` fails when grep matches early: grep exits,
git dies of SIGPIPE with 141, and pipefail makes that the pipeline's status, so the
branch went false and the script printed "OK: settings.local.json not in git" for a
file that was committed. Reproduced at 20k tracked files with a match that sorts
early; a small .claude/ never shows it. Ask git directly instead of piping.
Carry the skipped state into the summary. The branch added in 6017574 left
ISSUES_FOUND at 0, so a non-repository still printed "All security checks passed"
and "No committed local settings" for a check that never ran.
* fix(claude-config-validator): give the verdict a threshold rule
Pass and Issues found were defined as the two values without saying which
findings flip one to the other, so two reviewers with identical findings could
return opposite verdicts and a SUGGESTED-only review was ambiguous.
* refactor(claude-config-validator): move the security-scan.sh fixes out of scope
Those fixes belong in a change where shell behaviour is the subject and can carry
regression coverage. Three consecutive review rounds found the same false-negative
class in that file, each time in code a previous fix was meant to close, and the
script has no tests. They are preserved on fix-security-scan-false-negatives.
Restore the troubleshooting note that described the reverted exclusions, and drop
the changelog bullet that claimed them. The security-patterns.md changes stay:
pointing at the shipped script instead of a copy that had drifted 134 lines is
true regardless of what that script contains.
Attribute the report to the caller in the security-scan step, the last place the
skill still spoke as though it owned one.
* fix(claude-config-validator): correct three defects in this change's own edits
The reworded settings.local.json check fired CRITICAL on any changeset where the
file appeared, including the one that deletes it, which is the remediation the same
section prescribes. Scope it to additions and modifications.
The enrichment section said a direct invocation "cannot invoke another skill",
which contradicts the allowed-tools note added to the requirements reference in
this same change: the field pre-approves rather than restricts. The call prompts,
which makes it unavailable in a non-interactive run, and that is what matters.
The settings example closed by counting three CRITICAL findings where it has two
CRITICAL and one IMPORTANT.
* fix(claude-config-validator): group the settings example's bullets by severity
The CRITICAL heading covered all three bullets, one of which maps to the IMPORTANT
finding at line 62, so the section claimed three CRITICAL findings twelve lines
above a closing line that correctly says two. Regrouped on the shape the skill
example already uses, one sub-heading per severity, which also folds the separate
risk list into the bullets it describes.
暂无评论...