Skip to content

chore: harden qa-create skill workflow and apply lint --fix#1630

Merged
isamu merged 3 commits into
mainfrom
chore/qa-create-skill-hardening
May 12, 2026
Merged

chore: harden qa-create skill workflow and apply lint --fix#1630
isamu merged 3 commits into
mainfrom
chore/qa-create-skill-hardening

Conversation

@ystknsh

@ystknsh ystknsh commented May 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • .claude/skills/qa-create/SKILL.md を更新し、QA 作成フローの必須ゲートを強化
    • Step 3.5: 実DOM確認(必須)を追加
    • Step 5 を「実行と修正(必須)」に変更(push 前の実行成功を明記)
    • 「推測でセレクタを書かない」ルールを追加
    • Definition of Done を追加
  • lint --fix の適用結果を反映
    • src/main/mulmo/handler_media.ts の軽微な自動修正

Why

  • DOM セレクタの推測実装による QA 失敗の再発防止
  • QA テスト作成時の品質ゲートを明文化し、未実行のまま push されることを防ぐため

Scope

  • ドキュメント/スキル定義の更新が主
  • 実行ロジックへの意図的な機能追加はなし(lint --fix による軽微修正のみ)

Verification

  • yarn lint --fix 実行済み

Summary by CodeRabbit

  • Documentation

    • Updated test generation procedures to enforce stricter selector validation and confirmation requirements before implementation.
  • Chores

    • Removed unnecessary code quality lint suppression.

@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@ystknsh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 12 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: 6e2f5294-fc3f-4d95-b855-6dbfb02a9bca

📥 Commits

Reviewing files that changed from the base of the PR and between ab8dd03 and 9fd0e2b.

📒 Files selected for processing (1)
  • .claude/skills/qa-create/SKILL.md
📝 Walkthrough

Walkthrough

This PR makes two independent updates: a documentation enhancement to the QA test creation procedure requiring mandatory DOM verification via MCP Playwright before selector code generation, and a removal of an eslint-disable lint suppression comment from the media handler.

Changes

Test Generation Procedure Enhancement

Layer / File(s) Summary
Procedural Requirements
.claude/skills/qa-create/SKILL.md
Adds mandatory Step 3.5 for real DOM verification using MCP Playwright. Requires validation of major selectors (rejecting zero/multiple matches and ambiguous partial-match selectors), prioritizing stable selectors, and recording adopted selectors and results in test file comments.
Execution Requirements
.claude/skills/qa-create/SKILL.md
Strengthens Step 5 execution discipline: mandates at least one successful local test run before push, prohibits treating tasks as complete without execution, and requires re-running and success verification upon failures.
Core Rules and Definition of Done
.claude/skills/qa-create/SKILL.md
Adds "Do not guess selectors—verify against real DOM first" as a critical rule. Updates Definition of Done checklist to explicitly include Step 3.5 DOM verification completion.

Lint Suppression Cleanup

Layer / File(s) Summary
Annotation Removal
src/main/mulmo/handler_media.ts
Removes // eslint-disable-next-line sonarjs/no-unsafe-unzip comment preceding the zip.extractAllTo(...) call in uploadBundleToServer function. Runtime behavior unchanged.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A rabbit checked every DOM with care,
Step 3.5 ensures selectors are fair,
No guessing allowed, just real data sight,
Push-time tests prove everything's right,
Clean rules for the path ahead! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: hardening the qa-create skill workflow and applying lint fixes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/qa-create-skill-hardening

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 52 minutes and 12 seconds.

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

@ystknsh ystknsh changed the title Chore/qa create skill hardening chore: harden qa-create skill workflow and apply lint --fix May 3, 2026
@ystknsh
ystknsh marked this pull request as ready for review May 3, 2026 14:10
@ystknsh
ystknsh requested a review from isamu May 3, 2026 14:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.claude/skills/qa-create/SKILL.md (1)

4-4: ⚠️ Potential issue | 🟠 Major

Step 3.5 requires MCP Playwright verification but lacks tool allowance and invocation guidance.

Line 39–47 mandates DOM verification with "MCP Playwright," yet allowed-tools at line 4 omits all Playwright tools (compare: release-xpost and release explicitly list mcp__electron-playwright__* tools). Step 3.5 does not document how to invoke Playwright if tools are unavailable—whether via Task delegating to the mcp-pw skill or another mechanism. This creates ambiguity: developers cannot directly use Playwright tools, and the workflow path is undocumented.

Either add Playwright tools to allowed-tools, or clarify in Step 3.5 that verification occurs via Task invocation of the mcp-pw skill.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/qa-create/SKILL.md at line 4, The README/skill config omits
Playwright tools needed by Step 3.5: update the allowed-tools list or document
the alternative; specifically either add the mcp__electron-playwright__* entries
to the allowed-tools declaration so MCP Playwright can be invoked directly, or
update Step 3.5 to explicitly state that DOM verification is performed by
delegating a Task to the mcp-pw skill (showing the Task invocation pattern and
expected inputs/outputs). Modify the allowed-tools symbol or the Step 3.5 text
accordingly so callers know whether to call MCP Playwright directly (via
mcp__electron-playwright__*) or via a Task to mcp-pw.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/mulmo/handler_media.ts`:
- Around line 74-76: The current code constructs AdmZip with zipFilePath and
calls zip.extractAllTo(extractDir, true) which allows Zip Slip path traversal;
replace this with manual per-entry validation: iterate zip.getEntries(), for
each entry compute the destination path by joining extractDir and
entry.entryName and resolving it (using path.resolve), then ensure the resolved
path startsWith the resolved extractDir; if it does not, skip or throw an error
and log the offending entry; only after validation create parent dirs and write
the entry data (or call entry.extract to the validated path) so no archive entry
can escape extractDir.

---

Outside diff comments:
In @.claude/skills/qa-create/SKILL.md:
- Line 4: The README/skill config omits Playwright tools needed by Step 3.5:
update the allowed-tools list or document the alternative; specifically either
add the mcp__electron-playwright__* entries to the allowed-tools declaration so
MCP Playwright can be invoked directly, or update Step 3.5 to explicitly state
that DOM verification is performed by delegating a Task to the mcp-pw skill
(showing the Task invocation pattern and expected inputs/outputs). Modify the
allowed-tools symbol or the Step 3.5 text accordingly so callers know whether to
call MCP Playwright directly (via mcp__electron-playwright__*) or via a Task to
mcp-pw.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89ccdff6-7ba5-4b39-b69c-78755bb6be2f

📥 Commits

Reviewing files that changed from the base of the PR and between 1254154 and ab8dd03.

📒 Files selected for processing (2)
  • .claude/skills/qa-create/SKILL.md
  • src/main/mulmo/handler_media.ts

Comment on lines 74 to 76
const zip = new AdmZip(zipFilePath);
// eslint-disable-next-line sonarjs/no-unsafe-unzip

zip.extractAllTo(extractDir, true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For adm-zip v0.5.17, does extractAllTo itself prevent Zip Slip/path traversal, and what secure extraction pattern is recommended for untrusted zip files?

💡 Result:

No. For adm-zip v0.5.17, extractAllTo does not provide reliable path-traversal/Zip Slip protection by itself for untrusted ZIPs; issues in adm-zip’s extraction logic show entries can be used to write outside the intended target directory. Grounded evidence: (1) A 2017 issue report states extractAllTo/extractEntryTo do not sanitize zip entry filenames and can extract outside targetPath because they combine targetPath and entryName without checking/sanitizing the resolved path; it recommends verifying the resolved path is under targetPath. (2) GitHub Security Lab’s advisory GHSL-2020-198 describes extractAllTo as attempting to ensure files are not extracted outside the target folder, but the enforcement contains a loophole that can result in extraction into a sibling folder (cross-folder extraction), which can enable overwriting files outside the intended destination. (3) Additional fix discussions/PRs in the adm-zip repo show the project had to add explicit checks/sanitization to prevent archived files being extracted outside the target path. Recommended secure extraction pattern for untrusted ZIPs: - Do not rely on extractAllTo/extractEntryTo as the sole mitigation. - Instead, for each ZIP entry, compute the final extraction path (using path resolution/normalization) and accept the entry only if the computed path is strictly within the intended destination directory (i.e., destination must be a real prefix after resolving both to absolute/canonical paths). Reject otherwise. - Create/operate inside a dedicated “quarantine” directory that is empty and not attacker-controlled, and avoid symlink-related issues by ensuring the destination directory is writable only by the app and not under attacker control. Concrete pattern (Node.js sketch): 1) const path = require('path'); 2) const root = path.resolve(destDir); 3) For each entryName from the archive: - const normalized = entryName.replace(/\/g,'/'); // normalize separators - const target = path.resolve(root, normalized); - if (!target.startsWith(root + path.sep)) reject/skip the entry. - Only then extract/write that entry to target. This “validate resolved path stays under root” approach matches the mitigation guidance described in both the adm-zip maintainer discussions/PRs and general Zip Slip guidance (including verifying child-of destination after resolving paths).

Citations:


🏁 Script executed:

# Check adm-zip version in dependencies
rg '"adm-zip"' --type json

Repository: receptron/mulmocast-app

Length of output: 106


🏁 Script executed:

# Read the actual code at the flagged lines with surrounding context
cat -n src/main/mulmo/handler_media.ts | sed -n '60,100p'

Repository: receptron/mulmocast-app

Length of output: 1498


🏁 Script executed:

# Check if there's any path validation helper or utility used elsewhere in the file
rg 'extractAllTo|normalize|resolve.*extract' src/main/mulmo/handler_media.ts -A 3 -B 3

Repository: receptron/mulmocast-app

Length of output: 264


Implement Zip Slip validation for untrusted archive extraction

Line 76 extracts all entries without validating resolved paths. adm-zip v0.5.17 does not prevent path traversal; a malicious archive can extract files outside extractDir. This is documented in GitHub Security Lab advisory GHSL-2020-198 and confirmed in adm-zip issues #176 and #212.

Validate each entry's resolved path before extraction:

Suggested fix
   // zipファイルを展開
   const zip = new AdmZip(zipFilePath);
+  const extractRoot = path.resolve(extractDir) + path.sep;
+  for (const entry of zip.getEntries()) {
+    const outputPath = path.resolve(extractDir, entry.entryName);
+    if (!outputPath.startsWith(extractRoot)) {
+      throw new Error(`Unsafe zip entry path: ${entry.entryName}`);
+    }
+  }

   zip.extractAllTo(extractDir, true);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const zip = new AdmZip(zipFilePath);
// eslint-disable-next-line sonarjs/no-unsafe-unzip
zip.extractAllTo(extractDir, true);
const zip = new AdmZip(zipFilePath);
const extractRoot = path.resolve(extractDir) + path.sep;
for (const entry of zip.getEntries()) {
const outputPath = path.resolve(extractDir, entry.entryName);
if (!outputPath.startsWith(extractRoot)) {
throw new Error(`Unsafe zip entry path: ${entry.entryName}`);
}
}
zip.extractAllTo(extractDir, true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/mulmo/handler_media.ts` around lines 74 - 76, The current code
constructs AdmZip with zipFilePath and calls zip.extractAllTo(extractDir, true)
which allows Zip Slip path traversal; replace this with manual per-entry
validation: iterate zip.getEntries(), for each entry compute the destination
path by joining extractDir and entry.entryName and resolving it (using
path.resolve), then ensure the resolved path startsWith the resolved extractDir;
if it does not, skip or throw an error and log the offending entry; only after
validation create parent dirs and write the entry data (or call entry.extract to
the validated path) so no archive entry can escape extractDir.

@isamu
isamu merged commit bd13bf3 into main May 12, 2026
7 checks passed
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.

2 participants