chore: security hardening, supply-chain gates, and AI PR review - #2
Conversation
Tighten capabilities/CSP/IPC validation, add license+audit scripts, unit tests, and CodeRabbit/Qodo review workflows so PRs get automated security and quality feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
Allow scripts/supply-chain/license-policy.json through gitignore so license gates stay reproducible in CI. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 26 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
WalkthroughThe change adds centralized Tauri validation, supply-chain license and dependency checks, release-gate scripts, automated review configuration, Rust unit tests, dependency pins, and security scanning workflows. ChangesApplication security controls
Supply-chain compliance
Review and CI automation
Frontend dependency updates Estimated code review effort: 4 (Complex) | ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
Allowlist unit-test OAuth fixtures, pin aquasecurity/trivy-action@v0.36.0, and stage onnxruntime DLLs beside Windows test binaries so PR checks can pass. Co-authored-by: Cursor <cursoragent@cursor.com>
Bump openssl to 0.10.79 and pin Next.js 15.5.21 with overrides for postcss, nanoid, and sharp so the SCA quality gate can pass. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 33
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.coderabbit.yaml:
- Around line 63-65: Update the CodeRabbit configuration’s knowledge_base
settings to add an explicit code_guidelines.filePatterns mapping that loads
.github/ai-review-rules.md for reviewed files, while preserving the existing
learnings.scope value.
In @.github/workflows/ai-review.yml:
- Around line 26-40: Restrict the issue_comment branch of the workflow condition
to trusted pull-request commenters by requiring
github.event.comment.author_association to be an approved repository role, such
as OWNER, MEMBER, or COLLABORATOR. Keep the existing command matching and
pull-request checks unchanged, while preserving the current non-Bot exclusion.
- Line 58: Pin every mutable GitHub Action reference to a verified full commit
SHA, and pin the semgrep/semgrep container to a verified immutable image digest.
Apply this to all affected sites: .github/workflows/ai-review.yml lines 58 and
85; .github/workflows/rust-unit-tests.yml lines 31, 34, and 37; and
.github/workflows/security.yml lines 26, 31, 45, 48, 75, and 78. Also audit
mutable references in .github/workflows/discord-release.yml and
.github/workflows/release.yml and apply the same pinning there.
- Around line 13-17: Update the permissions configuration in the ai-review
workflow to remove global issues, pull-requests, and checks write access; scope
issues: write and pull-requests: write only to the qodo-pr-agent job, and enable
restricted_mode when contents: write is unnecessary. Keep coderabbit-ruleset
limited to the read permissions it requires.
In @.github/workflows/rust-unit-tests.yml:
- Around line 49-52: Update the focused test commands in the workflow and
package scripts so each Cargo test filter runs separately rather than combining
filters with “|”. Preserve the existing focused test targets, invoke Cargo once
per filter, and ensure the command still uses the existing test options such as
--lib and --nocapture.
In @.gitignore:
- Around line 35-36: Remove the redundant .env.example negation patterns from
.gitignore, while preserving the effective license-policy and capabilities
negations shown in the diff.
In `@docs/LICENSE_COMPLIANCE_CHECKLIST.md`:
- Line 39: Update the heading for section F, “릴리스 노트,” to use the same level as
the sibling section G, “자동화 게이트 (빌드 전),” so both appear as top-level sibling
sections in the table of contents.
In `@package.json`:
- Around line 13-15: Update the package.json scripts check:licenses, audit:deps,
and prebuild:supply-chain to use the existing cross-platform shell wrappers
instead of directly invoking python or PowerShell scripts; preserve the current
outputs and command behavior, and ensure Linux/macOS runners use python3 through
the wrapper while Windows support remains available.
- Line 12: Update the test:rust script to pass each Rust test module filter
separately after -- rather than combining them into one pipe-delimited filter.
Preserve all existing filters and the current cargo manifest and library test
options.
In `@scripts/audit-deps.ps1`:
- Around line 56-58: Update the cargo and npm audit JSON report writes near the
cargo audit command and npm audit command to use an explicit UTF-8 encoding
without a BOM, while preserving the existing output paths and audit behavior.
Ensure both reports are parseable by Python json.loads and jq under Windows
PowerShell 5.1.
- Around line 40-42: Update the $SkipInstall branch in scripts/audit-deps.ps1 to
terminate with exit code 2 when cargo-audit is unavailable, matching the Bash
gate’s missing-tool behavior while preserving the existing error message.
- Line 21: Update the native command execution flows in the audit script for
cargo install --list, cargo audit --json, and npm audit --json to temporarily
set $ErrorActionPreference to Continue, then restore the original preference
afterward using guaranteed cleanup. Preserve the existing failure recording and
aggregate reporting behavior while preventing native stderr from terminating the
script under Windows PowerShell 5.1.
In `@scripts/check-licenses.ps1`:
- Line 13: Update the Python interpreter initialization and its use in the
license-check script to prefer a usable python3 command, fall back to python,
and validate availability before running the scanner. If neither command is
available, emit a clear error and stop instead of allowing a
CommandNotFoundException or Microsoft Store alias to proceed.
- Around line 23-28: Update Invoke-Py to preserve the wrapped Python process’s
non-zero exit code instead of throwing, so callers retain the distinction
between exit codes 1 and 2. Keep the existing argument forwarding and success
behavior, and ensure the script terminates or propagates failure using
$LASTEXITCODE without converting it to 1.
In `@scripts/prebuild-supply-chain.ps1`:
- Around line 15-24: Preserve child-process exit codes across all PowerShell
supply-chain gates: in scripts/prebuild-supply-chain.ps1 lines 15-24, check
$LASTEXITCODE after each $lic and $aud invocation and exit immediately with that
code; in scripts/audit-deps.ps1 lines 40-42, replace the missing cargo-audit
throw with exit 2; and in scripts/check-licenses.ps1 lines 23-28, make Invoke-Py
exit with $LASTEXITCODE so Python statuses propagate to callers.
In `@scripts/prebuild-supply-chain.sh`:
- Around line 18-19: Set the executable permission bit on both scripts invoked
by the prebuild flow, scripts/check-licenses.sh and scripts/audit-deps.sh, so
their existing direct invocations succeed on Linux and macOS.
In `@scripts/supply-chain/check_model_license_compat.py`:
- Around line 384-394: Update the catalog loop that builds the separation
entries in the compatibility matrix to resolve each model’s license from the
available documentation tables or policy data instead of assigning
"PROVISIONAL-MIT" universally. Use "UNKNOWN" when no source provides a license,
and pass the resolved value to both the matrix license field and
classify_model_license.
- Around line 224-244: Update the rejected-model detection loop around
rejected_needles so hit evaluates every derived marker against catalog_text
without filtering through the hardcoded model-name set or cc-by-nc prefix.
Preserve the existing marker tokenization and findings behavior, ensuring
entries from documented_rejected_models and MODEL_LICENSING.md are enforced
uniformly.
- Around line 324-334: Collapse the nested MIT-license checks in the surrounding
license-validation logic into one condition, preserving the existing requirement
that neither the early notices section nor the full notices text contains MIT
licensing markers before appending the “notices_missing_mit” finding. Remove the
redundant inner if without changing the finding or message.
- Around line 291-300: Remove both no-op blocks: the conditional checking
"AutumnColor77/Live-MR-Manager" and the subsequent align_models loop. Keep the
existing model_url policy validation that follows unchanged, since it already
enforces the intended behavior.
- Around line 83-90: Update the alias matching condition in the aliases loop to
use explicit parentheses around the intended or/and grouping, satisfying RUF021
without changing the desired matching behavior. In the CC-BY-NC return path,
remove the no-op replacement and return the matched uppercase license string
directly, preserving the existing fallback.
In `@scripts/supply-chain/inventory_licenses.py`:
- Around line 242-247: Remove the unused --fail-on-forbidden parser argument
from the argument setup in the inventory script, including its associated
configuration, since args.fail_on_forbidden is not consumed by main and the flag
provides no behavior.
- Around line 145-153: Update the subprocess error handlers around the cargo
invocation: in both FileNotFoundError and CalledProcessError cases, print the
existing diagnostic message and raise SystemExit with status 2 instead of
passing a string directly. Preserve the current stderr/stdout fallback and error
context.
- Around line 181-187: Rename the npm_lock_packages loop variable rel to
pkg_path so it no longer shadows the module-level rel() helper, and update all
references within the loop, including the package path assignment, to use
pkg_path.
In `@scripts/supply-chain/license-policy.json`:
- Line 31: Remove the duplicate MPL-2.0 entry from either
linked_dependencies.allowed or linked_dependencies.review, retaining only the
classification that matches the intended policy and classify_expression
behavior.
- Line 57: Update the forbidden-license entries in the policy configuration to
include SSPL-1.1 alongside SSPL-1.0, so the existing inventory license
classification recognizes it as forbidden and blocks dependencies declaring that
identifier.
In `@src-tauri/capabilities/default.json`:
- Around line 17-28: Replace the broad URL patterns in the opener allowlist with
concrete routes actually opened by the frontend, especially for the nine remote
origins. Restrict Google/Naver OAuth, GitHub, Discord invite, and other static
destinations to their narrowest supported paths, and retain a wildcard only
where a documented variable route is required. Ensure direct
plugin:opener|open_url calls remain functional without exposing entire origins.
In `@src-tauri/src/ipc_validate.rs`:
- Around line 90-94: Update the GitHub repository prefix checks in the path
validation logic to accept only an exact repository root or a path beginning
with that root followed by “/”, rejecting suffixes such as “.evil” and “-fork”
for all three repositories. Add negative tests covering these invalid suffixes.
- Around line 100-126: Update validate_external_open_url and host_path_allowed
to share the opener capability’s exact allowlist, permitting only
http://localhost:5173/* and rejecting other localhost ports and all 127.0.0.1
URLs. Add or update tests covering disallowed ports and 127.0.0.1 while
preserving the existing HTTPS validation behavior.
In `@src-tauri/src/model_commands.rs`:
- Around line 357-360: Update add_custom_model’s download flow beyond the
existing HTTPS check to permit only approved hosts, disable or control automatic
redirects so every redirect target is revalidated, and reject private, loopback,
and link-local destinations at connection time. Apply validation to each
resolved connection rather than relying on one pre-request DNS lookup, while
preserving the existing streamed download and SHA-256/ONNX validation flow.
In `@src-tauri/src/songbook_auth.rs`:
- Around line 203-206: Update parse_oauth_callback_url and the desktop login
flow to enforce one exact callback route, structurally parse the URL, and
extract only the token query key. Bind each login attempt to a generated state
or nonce and validate it on callback before accepting the token; reject
arbitrary hosts, paths, and similarly named parameters, and ensure raw callback
URLs are never logged.
In `@src-tauri/src/youtube_url.rs`:
- Around line 109-113: Update extract_youtube_video_id to parse and validate the
input URL before extracting an ID, accepting only approved YouTube hosts and
supported YouTube path formats; reject embedded YouTube-like values from
non-YouTube hosts. Extend rejects_non_youtube_for_id_extract with spoofed query
URLs and additional non-YouTube host cases, while preserving valid YouTube
extraction behavior used by cache_key_variants.
In `@src-tauri/tauri.conf.json`:
- Around line 28-38: Restrict the assetProtocol scope in the tauri configuration
by removing the broad "$HOME/**" entry. Retain only the explicitly required
media and cache directory scopes already listed, adding specific directories
only when required by the application.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 8302251c-2693-49b7-ad59-15e3e8db201b
⛔ Files ignored due to path filters (1)
src-tauri/Cargo.lockis excluded by!**/*.lock,!**/Cargo.lock
📒 Files selected for processing (34)
.coderabbit.yaml.github/ai-review-rules.md.github/workflows/ai-review.yml.github/workflows/rust-unit-tests.yml.github/workflows/security.yml.gitignore.pr_agent.tomldocs/LICENSE_COMPLIANCE_CHECKLIST.mdpackage.jsonscripts/audit-deps.ps1scripts/audit-deps.shscripts/check-licenses.ps1scripts/check-licenses.shscripts/discord_release_announce.pyscripts/prebuild-supply-chain.ps1scripts/prebuild-supply-chain.shscripts/supply-chain/check_model_license_compat.pyscripts/supply-chain/inventory_licenses.pyscripts/supply-chain/license-policy.jsonsrc-tauri/.env.examplesrc-tauri/Cargo.tomlsrc-tauri/capabilities/default.jsonsrc-tauri/src/cache_settings.rssrc-tauri/src/env_config.rssrc-tauri/src/ipc_validate.rssrc-tauri/src/lib.rssrc-tauri/src/library.rssrc-tauri/src/model_commands.rssrc-tauri/src/songbook_auth.rssrc-tauri/src/system.rssrc-tauri/src/updater.rssrc-tauri/src/vocal_remover.rssrc-tauri/src/youtube_url.rssrc-tauri/tauri.conf.json
| knowledge_base: | ||
| learnings: | ||
| scope: auto |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 'ai-review-rules\.md|code_guidelines|filePatterns' .coderabbit.yamlRepository: AutumnColor77/Live-MR-Manager
Length of output: 433
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- .coderabbit.yaml ---'
cat -n .coderabbit.yaml | sed -n '1,100p'
printf '%s\n' '--- candidate guideline files ---'
git ls-files '.github/ai-review-rules.md' '.github/*' | sed -n '1,80p'
if [ -f .github/ai-review-rules.md ]; then
printf '%s\n' '--- .github/ai-review-rules.md ---'
cat -n .github/ai-review-rules.md | sed -n '1,180p'
fi
printf '%s\n' '--- repository references ---'
rg -n -C 2 'ai-review-rules|code_guidelines|filePatterns|applyTo' . --glob '!node_modules' --glob '!dist' --glob '!build' | sed -n '1,240p'Repository: AutumnColor77/Live-MR-Manager
Length of output: 6179
🌐 Web query:
CodeRabbit configuration knowledge_base code_guidelines filePatterns applyTo custom guideline files official documentation
💡 Result:
CodeRabbit automatically detects and applies coding guidelines from various standard configuration files (such as.cursorrules,.github/copilot-instructions.md, CLAUDE.md, and others) by default [1][2][3]. You can manage and extend these guidelines using the knowledge_base.code_guidelines configuration in your.coderabbit.yaml file [1][2]. Configuration Options - enabled: Set to true (default) to enable auto-detection. Set to false to disable all code guidelines [2][4]. - filePatterns: An array used to define additional guideline files or to override default behavior [1][2]. Entries can be provided in two forms: - String entries: Paths (as glob patterns) that act as guidelines for the directory they reside in and all subdirectories [2][5]. - Object entries: Use the structure { files: , applyTo: } to explicitly map a guideline file to specific source-file patterns [2][5]. Key Features of applyTo The applyTo property allows you to decouple your guideline documents from your repository's directory structure [2][6]. By using an object entry, you can store a guideline file anywhere in your repository (e.g., in a shared docs/ folder) and explicitly define which source files it should govern, regardless of their location [2][6]. Custom guideline files can be added to the existing defaults; they do not replace them [2][5]. All file patterns are case-sensitive [1][2]. For comprehensive control, including disabling specific guidelines or editing instructions, repository administrators can also manage these settings directly via the CodeRabbit UI [6].
Citations:
- 1: https://docs.coderabbit.ai/reference/configuration
- 2: https://docs.coderabbit.ai/knowledge-base/code-guidelines
- 3: https://www.coderabbit.ai/blog/code-guidelines-bring-your-coding-rules-to-coderabbit
- 4: https://coderabbit.mintlify.app/reference/configuration
- 5: https://coderabbit.mintlify.app/knowledge-base/code-guidelines
- 6: https://docs.coderabbit.ai/changelog
Load .github/ai-review-rules.md as a CodeRabbit guideline.
The YAML comment does not load this non-default guideline file. Add an explicit code_guidelines.filePatterns mapping so its rules apply to reviewed files.
Proposed fix
knowledge_base:
+ code_guidelines:
+ filePatterns:
+ - files: ".github/ai-review-rules.md"
+ applyTo: "**/*"
learnings:
scope: auto🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.coderabbit.yaml around lines 63 - 65, Update the CodeRabbit configuration’s
knowledge_base settings to add an explicit code_guidelines.filePatterns mapping
that loads .github/ai-review-rules.md for reviewed files, while preserving the
existing learnings.scope value.
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| issues: write | ||
| checks: write |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 'permissions:|checks: write|pull-requests: write|issues: write|GITHUB_TOKEN' \
.github/workflows/ai-review.yml .pr_agent.tomlRepository: AutumnColor77/Live-MR-Manager
Length of output: 1399
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n .github/workflows/ai-review.yml
printf '\n--- PR-Agent configuration references ---\n'
rg -n -C 3 '(^|[.]|/)(pr-agent|qodo|github|permission|check|issue|pull.request)|checks|issues|pull-requests|comment|command' \
.pr_agent.toml .github README.md 2>/dev/null || trueRepository: AutumnColor77/Live-MR-Manager
Length of output: 21040
🌐 Web query:
PR-Agent GitHub Action required GITHUB_TOKEN permissions checks write issues write pull-requests write official documentation
💡 Result:
For the PR-Agent GitHub Action, the required GITHUB_TOKEN permissions depend on the features you intend to use [1][2]. Standard Configuration The standard, recommended configuration requires the following permissions to ensure all PR-Agent tools (such as /review, /describe, and /improve) function correctly [1][3][2]: permissions: issues: write pull-requests: write contents: write These permissions allow PR-Agent to read and post comments on issues and pull requests, and manage repository contents when necessary [1][2]. Restricted Mode (Reduced Permissions) If you prefer not to grant contents: write permissions, PR-Agent offers a restricted mode [1][2]. By setting config.restricted_mode = true in your PR-Agent configuration, you can limit the required permissions to [2]: permissions: issues: write pull-requests: write In this mode, tools that require repository modification (specifically /update_changelog with push_changelog_changes enabled) will be disabled, while standard PR feedback tools will continue to function normally [2]. Important Considerations - Token Access: The GITHUB_TOKEN is generated automatically by GitHub for each job [1][4]. You must explicitly define the permissions block in your workflow YAML file, as any permission omitted defaults to none [4]. - Forked Repositories: By default, workflows triggered by pull_request from a forked repository have read-only access to the GITHUB_TOKEN and cannot access repository secrets [1]. To support PRs from forks, consider using the pull_request_target event, which runs in the context of the base repository and has access to secrets [1]. - Least Privilege: It is a best practice to grant only the minimum permissions required for your specific workflow needs to adhere to the principle of least privilege [5][4].
Citations:
- 1: https://docs.pr-agent.ai/installation/github/
- 2: feat: add restricted mode for reduced GitHub permissions (#2279) The-PR-Agent/pr-agent#2491
- 3: https://github.com/The-PR-Agent/pr-agent/blob/main/docs/docs/installation/github.md
- 4: https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/
- 5: https://docs.github.com/actions/reference/authentication-in-a-workflow
🌐 Web query:
site:docs.pr-agent.ai/installation/github PR-Agent permissions checks write restricted_mode contents write
💡 Result:
In PR-Agent, restricted_mode is a configuration setting that allows the tool to operate with reduced GitHub or GitLab permissions by skipping operations that require elevated access, such as pushing changes to the repository [1]. When restricted_mode is enabled, you can set the repository workflow permissions to exclude contents: write, as the tool will automatically avoid any operations that would necessitate that scope [2][1]. To enable restricted_mode, add the following to your configuration file: [config] restricted_mode = true When using this mode, the minimum required permissions in your workflow configuration are: permissions: issues: write pull-requests: write It is important to note that when you explicitly define a permissions block, any scope not listed is set to none [1]. If you omit the permissions block entirely, your repository or organization defaults may grant broader access than intended [1]. With restricted_mode active, tools like /review, /describe, and /improve continue to function normally using only pull-requests: write permissions [1].
Citations:
- 1: https://docs.pr-agent.ai/usage-guide/additional_configurations/
- 2: https://docs.pr-agent.ai/installation/github/
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/ai-review.yml')
text = p.read_text()
for name in ('qodo-pr-agent', 'coderabbit-ruleset'):
start = text.index(f' {name}:')
end = text.find('\n ', start + 3)
if end == -1:
end = len(text)
block = text[start:end]
print(f'[{name}]')
print('workflow_permissions_inherited=', 'permissions:' not in block)
print('uses_checkout=', 'actions/checkout@' in block)
print('runs_shell=', 'run:' in block)
print()
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 358
Scope write permissions to qodo-pr-agent.
coderabbit-ruleset only checks two files and does not need write access. Move issues: write and pull-requests: write to qodo-pr-agent. Enable restricted_mode if contents: write is not required. Remove checks: write; PR-Agent documentation does not require it.
🧰 Tools
🪛 zizmor (1.29.0)
[error] 15-15: overly broad permissions (excessive-permissions): pull-requests: write is overly broad at the workflow level
(excessive-permissions)
[error] 16-16: overly broad permissions (excessive-permissions): issues: write is overly broad at the workflow level
(excessive-permissions)
[warning] 17-17: overly broad permissions (excessive-permissions): checks: write is overly broad at the workflow level
(excessive-permissions)
[warning] 15-15: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ai-review.yml around lines 13 - 17, Update the permissions
configuration in the ai-review workflow to remove global issues, pull-requests,
and checks write access; scope issues: write and pull-requests: write only to
the qodo-pr-agent job, and enable restricted_mode when contents: write is
unnecessary. Keep coderabbit-ruleset limited to the read permissions it
requires.
Source: Linters/SAST tools
| if: > | ||
| github.event.sender.type != 'Bot' && | ||
| ( | ||
| github.event_name == 'pull_request' || | ||
| ( | ||
| github.event_name == 'issue_comment' && | ||
| github.event.issue.pull_request && | ||
| ( | ||
| contains(github.event.comment.body, '/review') || | ||
| contains(github.event.comment.body, '/improve') || | ||
| contains(github.event.comment.body, '/describe') || | ||
| contains(github.event.comment.body, '/ask') | ||
| ) | ||
| ) | ||
| ) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 18 'issue_comment|comment\.body|author_association|OPENAI_KEY' \
.github/workflows/ai-review.ymlRepository: AutumnColor77/Live-MR-Manager
Length of output: 3279
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/ai-review.yml
printf '%s\n' '--- related configuration and references ---'
rg -n -C 4 'pr-agent|auto_review|auto_describe|pr_actions|GITHUB_TOKEN|permissions|issue_comment|author_association' \
.github README.md .pr_agent.toml 2>/dev/null || trueRepository: AutumnColor77/Live-MR-Manager
Length of output: 12370
🌐 Web query:
GitHub Actions issue_comment event comment.author_association context secrets default branch documentation
💡 Result:
The issue_comment event in GitHub Actions is a powerful but highly privileged trigger that requires careful security management [1][2][3]. Key Technical Context: - Event Requirements: Workflows triggered by issue_comment must exist on the repository's default branch to run [4][5]. - Default Behavior: When triggered, the workflow runs in the context of the base repository with full access to secrets and repository-level GITHUB_TOKEN permissions [1][2][3]. - Security Risk: Because any user who can comment on an issue or pull request can trigger this event, workflows using it without authorization checks are vulnerable to unauthorized code execution, secret exfiltration, and supply-chain attacks [1][2][3]. Unlike the pull_request event, which automatically restricts permissions and hides secrets when triggered by forks, issue_comment does not provide this isolation [6][7][3]. Authorization Best Practices: To secure workflows using issue_comment, you must explicitly validate the commenter's authorization level using the github.event.comment.author_association context [1][2]. Recommended authorization check (job-level if condition): jobs: process-comment: if: | github.event_name!= 'issue_comment' || contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) runs-on: ubuntu-latest steps: - run: echo "Authorized commenter, proceeding..." Security Precautions: - Forked Repository Code: Never check out and execute code from a pull request head or external reference within a privileged issue_comment workflow [7][3]. If you must process a fork, strictly use metadata (like labels or comments) and avoid running untrusted code [3]. - Input Validation: Treat all github.event data, particularly comment.body, as untrusted input. Do not interpolate this data directly into shell commands or scripts, as this can lead to code injection vulnerabilities [6][2]. - Explicit Documentation: If you use an author_association gate, document this requirement clearly within the workflow file to ensure future maintainers understand that it is the primary barrier preventing unauthorized access [8].
Citations:
- 1: [static-analysis] RGS-004: Comment-Triggered Workflow Without Author Authorization Check github/gh-aw#29460
- 2: https://sisaku-security.github.io/lint/docs/ghsl/ghsl-2025-091/
- 3: https://github.com/github/awesome-copilot/blob/main/skills/github-actions-hardening/references/triggers-and-privilege.md
- 4: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows
- 5: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows
- 6: https://github.com/github/docs/blob/962a1c8dccb8c0f66548b324e5b921b5e4fbc3d6/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md
- 7: Security: example fro-bot.yaml exposes secrets to fork PRs via issue_comment checkout fro-bot/agent#919
- 8: https://whisperx.ai/en/intel/39312
🌐 Web query:
PR-Agent GitHub Action issue_comment slash commands /review /improve /describe /ask documentation
💡 Result:
PR-Agent provides a set of slash commands that can be used directly in Pull Request (PR) comments to trigger various automated analysis and feedback tasks [1][2][3]. ### Available Slash Commands When PR-Agent is installed as a GitHub Action or App, you can invoke these tools by typing the command as a comment on a PR [2][3]: * /describe: Automatically generates a PR description, including title, type, summary, code walkthrough, and labels [2]. * /review: Provides comprehensive feedback on the PR, including potential issues, security concerns, review effort, and test analysis [2][4]. * /improve: Scans code changes and provides actionable, meaningful suggestions for code improvements [2][5]. * /ask "[question]": Allows for free-text Q&A about the PR or specific lines of code (e.g., /ask "How does this function handle edge cases?") [2]. ### Additional Functionality Other supported commands include: * /add_docs: Generates documentation for code components [2]. * /generate_labels: Creates custom labels based on code changes [2]. * /similar_issue: Finds related issues in the repository [2]. * /update_changelog: Automatically updates the CHANGELOG.md file [2]. * /help: Displays a list of all available tools [2]. ### Advanced Usage You can customize the behavior of these tools by adding configuration arguments directly to the slash command [3][5][4]. For example: /review --pr_reviewer.extra_instructions="Focus on performance" /improve --pr_code_suggestions.commitable_code_suggestions=true Automated runs can also be configured by defining a list of commands in the configuration file (e.g., configuration.toml) under the [github_app] section [3][5][4]: [github_app] pr_commands = ["/describe", "/review", "/improve"] Ensure that your GitHub Action workflow configuration includes the issue_comment event type if you intend to trigger these commands via comments [6][7].
Citations:
- 1: https://github.com/The-PR-Agent/pr-agent
- 2: https://docs.pr-agent.ai/tools/
- 3: https://docs.pr-agent.ai/usage-guide/automations_and_usage/
- 4: https://docs.pr-agent.ai/tools/review/
- 5: https://docs.pr-agent.ai/tools/improve/
- 6: https://docs.pr-agent.ai/installation/github/
- 7: https://github.com/The-PR-Agent/pr-agent/blob/main/docs/docs/installation/github.md
Restrict PR comment commands to trusted commenters.
For issue_comment events, any non-Bot commenter can trigger PR-Agent. The job passes OPENAI_KEY and a write-capable GITHUB_TOKEN to the action. Add an author_association check:
Proposed fix
(
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
+ contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) &&
(📝 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.
| if: > | |
| github.event.sender.type != 'Bot' && | |
| ( | |
| github.event_name == 'pull_request' || | |
| ( | |
| github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request && | |
| ( | |
| contains(github.event.comment.body, '/review') || | |
| contains(github.event.comment.body, '/improve') || | |
| contains(github.event.comment.body, '/describe') || | |
| contains(github.event.comment.body, '/ask') | |
| ) | |
| ) | |
| ) | |
| if: > | |
| github.event.sender.type != 'Bot' && | |
| ( | |
| github.event_name == 'pull_request' || | |
| ( | |
| github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request && | |
| contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) && | |
| ( | |
| contains(github.event.comment.body, '/review') || | |
| contains(github.event.comment.body, '/improve') || | |
| contains(github.event.comment.body, '/describe') || | |
| contains(github.event.comment.body, '/ask') | |
| ) | |
| ) | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ai-review.yml around lines 26 - 40, Restrict the
issue_comment branch of the workflow condition to trusted pull-request
commenters by requiring github.event.comment.author_association to be an
approved repository role, such as OWNER, MEMBER, or COLLABORATOR. Keep the
existing command matching and pull-request checks unchanged, while preserving
the current non-Bot exclusion.
|
|
||
| - name: Run PR-Agent | ||
| if: steps.creds.outputs.configured == 'true' | ||
| uses: the-pr-agent/pr-agent@main |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*'
printf '%s\n' '--- ai-review.yml ---'
cat -n .github/workflows/ai-review.yml
printf '%s\n' '--- rust-unit-tests.yml ---'
cat -n .github/workflows/rust-unit-tests.yml
printf '%s\n' '--- security.yml ---'
cat -n .github/workflows/security.yml
printf '%s\n' '--- all workflow dependency references ---'
rg -n '(^|[[:space:]])uses:|(^|[[:space:]])image:|container:|pull_request_target|secrets\.|GITHUB_TOKEN|OPENAI_KEY' .github/workflowsRepository: AutumnColor77/Live-MR-Manager
Length of output: 13185
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
sha = re.compile(r"^[0-9a-fA-F]{40}$")
digest = re.compile(r"^sha256:[0-9a-fA-F]{64}$")
for path in sorted(Path(".github/workflows").glob("*.yml")):
for lineno, line in enumerate(path.read_text().splitlines(), 1):
m = re.search(r"\buses:\s*([^#\s]+)", line)
if m:
ref = m.group(1).rsplit("@", 1)[-1] if "@" in m.group(1) else ""
kind = "immutable commit SHA" if sha.fullmatch(ref) else "mutable/non-SHA reference"
print(f"{path}:{lineno}: uses={m.group(1)} -> {kind}")
m = re.search(r"^\s*image:\s*([^#\s]+)", line)
if m:
image = m.group(1)
ref = image.rsplit("@", 1)[-1] if "@" in image else ""
kind = "immutable image digest" if digest.fullmatch(ref) else "mutable/non-digest image reference"
print(f"{path}:{lineno}: image={image} -> {kind}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 1771
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- discord-release.yml ---'
cat -n .github/workflows/discord-release.yml
printf '%s\n' '--- release.yml ---'
cat -n .github/workflows/release.ymlRepository: AutumnColor77/Live-MR-Manager
Length of output: 3559
Pin workflow dependencies to immutable references.
Pin every action to a verified full commit SHA. Pin semgrep/semgrep to a verified image digest. Apply this to all mutable references in .github/workflows/ai-review.yml, .github/workflows/discord-release.yml, .github/workflows/release.yml, .github/workflows/rust-unit-tests.yml, and .github/workflows/security.yml.
🧰 Tools
🪛 zizmor (1.29.0)
[error] 58-58: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
📍 Affects 3 files
.github/workflows/ai-review.yml#L58-L58(this comment).github/workflows/ai-review.yml#L85-L85.github/workflows/rust-unit-tests.yml#L31-L31.github/workflows/rust-unit-tests.yml#L34-L34.github/workflows/rust-unit-tests.yml#L37-L37.github/workflows/security.yml#L26-L26.github/workflows/security.yml#L31-L31.github/workflows/security.yml#L45-L45.github/workflows/security.yml#L48-L48.github/workflows/security.yml#L75-L75.github/workflows/security.yml#L78-L78
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ai-review.yml at line 58, Pin every mutable GitHub Action
reference to a verified full commit SHA, and pin the semgrep/semgrep container
to a verified immutable image digest. Apply this to all affected sites:
.github/workflows/ai-review.yml lines 58 and 85;
.github/workflows/rust-unit-tests.yml lines 31, 34, and 37; and
.github/workflows/security.yml lines 26, 31, 45, 48, 75, and 78. Also audit
mutable references in .github/workflows/discord-release.yml and
.github/workflows/release.yml and apply the same pinning there.
Source: Linters/SAST tools
| - name: Run focused unit tests | ||
| shell: pwsh | ||
| run: | | ||
| cargo test --lib "songbook_auth::|youtube_url::|ipc_validate::|updater::|library::tests::" -- --nocapture |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -nF 'songbook_auth::|youtube_url::|ipc_validate::|updater::|library::tests::' \
package.json .github/workflows/rust-unit-tests.ymlRepository: AutumnColor77/Live-MR-Manager
Length of output: 490
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/rust-unit-tests.yml | sed -n '35,60p'
printf '%s\n' '--- package script ---'
cat -n package.json | sed -n '1,20p'
printf '%s\n' '--- relevant Rust test declarations ---'
rg -n '(^|[[:space:]])(#\[test\]|#\[tokio::test\]|fn[[:space:]]+(songbook_auth|youtube_url|ipc_validate|updater|library))' \
src-tauri --glob '*.rs' | head -200
printf '%s\n' '--- standalone substring check ---'
python3 - <<'PY'
filters = [
"songbook_auth::",
"youtube_url::",
"ipc_validate::",
"updater::",
"library::tests::",
]
combined = "|".join(filters)
candidate_names = [
"songbook_auth::tests::valid_token",
"youtube_url::tests::parses_url",
"ipc_validate::tests::rejects_invalid_input",
"updater::tests::checks_update",
"library::tests::loads_library",
]
assert all(f in name for f, name in zip(filters, candidate_names))
assert not any(combined in name for name in candidate_names)
print("Cargo's single filter value cannot match these candidate test names as an alternation.")
print("individual matches:", [any(f in name for name in candidate_names) for f in filters])
print("combined match:", any(combined in name for name in candidate_names))
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 4216
Run each Cargo test filter separately.
Cargo treats the value before -- as one substring filter, not regular-expression alternation. The combined filter matches none of the focused tests, while Cargo can exit successfully after running zero tests. Update .github/workflows/rust-unit-tests.yml and package.json.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/rust-unit-tests.yml around lines 49 - 52, Update the
focused test commands in the workflow and package scripts so each Cargo test
filter runs separately rather than combining filters with “|”. Preserve the
existing focused test targets, invoke Cargo once per filter, and ensure the
command still uses the existing test options such as --lib and --nocapture.
| /// External browser open allowlist (mirrors `capabilities/default.json` opener rules). | ||
| pub fn validate_external_open_url(url: &str) -> Result<String, String> { | ||
| let trimmed = require_nonempty(url, "URL")?; | ||
| require_max_len(trimmed, MAX_URL_LEN, "URL")?; | ||
|
|
||
| let parsed = url::Url::parse(trimmed).map_err(|_| "URL 형식이 올바르지 않습니다.".to_string())?; | ||
| let scheme = parsed.scheme(); | ||
| let host = parsed.host_str().ok_or_else(|| "URL 호스트가 없습니다.".to_string())?; | ||
| let path = parsed.path(); | ||
|
|
||
| match scheme { | ||
| "https" => { | ||
| if host_path_allowed(host, path) { | ||
| Ok(trimmed.to_string()) | ||
| } else { | ||
| Err("허용되지 않은 외부 URL입니다.".into()) | ||
| } | ||
| } | ||
| "http" => { | ||
| if matches!(host, "localhost" | "127.0.0.1") && host_path_allowed(host, path) { | ||
| Ok(trimmed.to_string()) | ||
| } else { | ||
| Err("http는 localhost만 허용됩니다.".into()) | ||
| } | ||
| } | ||
| _ => Err("http(s) URL만 허용됩니다.".into()), | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline src-tauri/src/ipc_validate.rs --items all --match validate_external_open_url
rg -n -C 4 'validate_external_open_url|open_url|opener' src-tauri/srcRepository: AutumnColor77/Live-MR-Manager
Length of output: 5367
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- URL validator and helpers ---'
sed -n '1,145p' src-tauri/src/ipc_validate.rs
printf '%s\n' '--- opener capability rules ---'
fd -i -t f 'default.json' src-tauri/capabilities --exec sh -c 'for f do echo "--- $f"; cat -n "$f"; done' sh
printf '%s\n' '--- updater command context ---'
sed -n '110,140p' src-tauri/src/updater.rs
printf '%s\n' '--- all capability opener references ---'
rg -n -C 5 'opener|openUrl|open_url|localhost|127\.0\.0\.1' src-tauri/capabilities src-tauri/tauri.conf.json src-tauriRepository: AutumnColor77/Live-MR-Manager
Length of output: 29741
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- frontend callers ---'
rg -n -C 6 'open_app_update_page|opener\.openUrl|openUrl\(' src --glob '!src-tauri/**'
printf '%s\n' '--- backend command registration and update URL construction ---'
rg -n -C 8 'open_app_update_page|default_release_url|release_url|invoke\(' src-tauri/src src
printf '%s\n' '--- policy difference probe ---'
python3 - <<'PY'
from urllib.parse import urlparse
capability = {
"https://www.livemrsongbook.com/*",
"https://livemrsongbook.com/*",
"https://live-mr-songbook.boohun2771.workers.dev/*",
"http://localhost:5173/*",
"https://accounts.google.com/*",
"https://nid.naver.com/*",
"https://github.com/AutumnColor77/Live-MR-Manager/*",
"https://github.com/BtbN/FFmpeg-Builds/*",
"https://github.com/yt-dlp/yt-dlp*",
"https://lmrm.vercel.app/*",
"https://discord.gg/*",
"https://discord.com/invite/*",
}
def backend(u):
p = urlparse(u)
if p.scheme == "https":
allowed = (
p.hostname in {
"www.livemrsongbook.com", "livemrsongbook.com",
"live-mr-songbook.boohun2771.workers.dev",
"accounts.google.com", "nid.naver.com", "lmrm.vercel.app",
"discord.gg",
}
or (p.hostname == "discord.com" and p.path.startswith("/invite/"))
or (p.hostname == "github.com" and (
p.path.startswith("/AutumnColor77/Live-MR-Manager")
or p.path.startswith("/BtbN/FFmpeg-Builds")
or p.path.startswith("/yt-dlp/yt-dlp")
))
)
return allowed
return p.scheme == "http" and p.hostname in {"localhost", "127.0.0.1"}
cases = [
"http://localhost:5173/",
"http://localhost:1420/",
"http://localhost:9999/private",
"http://127.0.0.1:5173/",
"http://127.0.0.1:9999/private",
]
for case in cases:
print(f"{case}: backend={backend(case)} capability={case == 'http://localhost:5173/' or case.startswith('http://localhost:5173/')}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 50386
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- frontend callers ---'
rg -n -C 6 'open_app_update_page|opener\.openUrl|openUrl\(' src --glob '!src-tauri/**'
printf '%s\n' '--- backend command registration and update URL construction ---'
rg -n -C 8 'open_app_update_page|default_release_url|release_url|invoke\(' src-tauri/src src
printf '%s\n' '--- policy difference probe ---'
python3 - <<'PY'
from urllib.parse import urlparse
def backend(u):
p = urlparse(u)
if p.scheme == "https":
return (
p.hostname in {
"www.livemrsongbook.com", "livemrsongbook.com",
"live-mr-songbook.boohun2771.workers.dev",
"accounts.google.com", "nid.naver.com", "lmrm.vercel.app",
"discord.gg",
}
or (p.hostname == "discord.com" and p.path.startswith("/invite/"))
or (p.hostname == "github.com" and (
p.path.startswith("/AutumnColor77/Live-MR-Manager")
or p.path.startswith("/BtbN/FFmpeg-Builds")
or p.path.startswith("/yt-dlp/yt-dlp")
))
)
return p.scheme == "http" and p.hostname in {"localhost", "127.0.0.1"}
def capability(u):
p = urlparse(u)
return p.scheme == "http" and p.hostname == "localhost" and p.port == 5173
for case in [
"http://localhost:5173/",
"http://localhost:1420/",
"http://localhost:9999/private",
"http://127.0.0.1:5173/",
"http://127.0.0.1:9999/private",
]:
print(f"{case}: backend={backend(case)} capability={capability(case)}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 50386
🏁 Script executed:
#!/bin/bash
set -euo pipefail
log=/tmp/coderabbit-shell-logs/shell-output-hW3Tj2
printf '%s\n' '--- policy probe output ---'
rg -n '^(http|https)://.*: backend=' "$log" || true
printf '%s\n' '--- focused URL callers ---'
sed -n '1,125p' src/js/songbook-requests.js
sed -n '1,40p' src/js/events/songbook-auth.js
sed -n '50,70p' src/js/settings-api.jsRepository: AutumnColor77/Live-MR-Manager
Length of output: 6245
Align the backend URL policy with the opener capability.
default.json permits only http://localhost:5173/*, but validate_external_open_url accepts every localhost and 127.0.0.1 port. The fallback in songbook-requests.js and songbook-auth.js can therefore bypass the capability allowlist. Use one shared allowlist and add tests for disallowed ports and 127.0.0.1.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src-tauri/src/ipc_validate.rs` around lines 100 - 126, Update
validate_external_open_url and host_path_allowed to share the opener
capability’s exact allowlist, permitting only http://localhost:5173/* and
rejecting other localhost ports and all 127.0.0.1 URLs. Add or update tests
covering disallowed ports and 127.0.0.1 while preserving the existing HTTPS
validation behavior.
| crate::ipc_validate::require_max_len(url, crate::ipc_validate::MAX_URL_LEN, "다운로드 URL")?; | ||
| if !url.starts_with("https://") { | ||
| return Err(format!("HTTPS URL만 허용됩니다: {}", url)); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- candidate symbols ---'
ast-grep outline src-tauri/src/model_commands.rs --match 'download_custom_model_https' --view expanded || true
ast-grep outline src-tauri/src/model_commands.rs --match 'add_custom_model' --view expanded || true
printf '%s\n' '--- relevant declarations and calls ---'
rg -n -C 5 'download_custom_model_https|add_custom_model|Client::builder|reqwest::Client|redirect|require_max_len|starts_with\("https://' src-tauri/src/model_commands.rs src-tauri/src
printf '%s\n' '--- command attributes and model command module ---'
sed -n '280,430p' src-tauri/src/model_commands.rsRepository: AutumnColor77/Live-MR-Manager
Length of output: 34281
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- download helper ---'
sed -n '180,285p' src-tauri/src/model_commands.rs
printf '%s\n' '--- dependency metadata ---'
fd -i 'Cargo.toml|Cargo.lock' . --max-depth 3 -t f -x sh -c 'echo --- "$1"; rg -n -C 3 "reqwest|url" "$1"' sh {}
printf '%s\n' '--- relevant URL/security helpers and tests ---'
rg -n -C 4 'allowlist|allowed.*host|private|loopback|link.?local|redirect|custom model|custom_model|MAX_URL_LEN' src-tauri/src src-tauri/Cargo.toml src-tauri/Cargo.lock 2>/dev/null || true
printf '%s\n' '--- command registration ---'
sed -n '105,130p' src-tauri/src/lib.rsRepository: AutumnColor77/Live-MR-Manager
Length of output: 34482
🌐 Web query:
reqwest 0.12 ClientBuilder default redirect policy follows redirects documentation
💡 Result:
In reqwest 0.12, the default redirect policy for a Client automatically follows HTTP redirects with a maximum limit of 10 consecutive hops [1][2]. If this limit is exceeded, or if a redirect loop is detected, the client will return an error [3][4]. To customize this behavior, you can use the redirect::Policy type in conjunction with ClientBuilder [1][2]. The following options are available: - Policy::limited(max: usize): Sets the maximum number of allowed redirect hops [3][4]. - Policy::none: Disables all redirect following [3][4]. - Policy::custom(policy: T): Allows you to define custom logic for redirect handling via a closure [3][4]. Note that unlike the default policy, a custom policy does not automatically handle loop detection, so you must implement it yourself if desired [3][5][4]. Example of applying a custom policy: let custom = reqwest::redirect::Policy::custom(|attempt| { if attempt.previous.len > 5 { attempt.error("too many redirects") } else { attempt.follow } }); let client = reqwest::Client::builder.redirect(custom).build?;
Citations:
- 1: https://docs.rs/reqwest/%3E=0.12,%20%3C0.13/
- 2: https://docs.rs/reqwest/latest/reqwest/redirect/index.html
- 3: https://docs.rs/reqwest/latest/reqwest/redirect/struct.Policy.html
- 4: https://docs.rs/reqwest/latest/src/reqwest/redirect.rs.html
- 5: https://github.com/seanmonstar/reqwest/blob/master/src/redirect.rs
Restrict custom-model downloads to approved hosts.
add_custom_model accepts arbitrary HTTPS URLs, and the default reqwest::Client follows redirects. The response is streamed to disk before SHA-256 and ONNX validation. Enforce a host allowlist, validate every redirect, and reject private, loopback, and link-local destinations at connection time. Do not rely on a single pre-request DNS check.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src-tauri/src/model_commands.rs` around lines 357 - 360, Update
add_custom_model’s download flow beyond the existing HTTPS check to permit only
approved hosts, disable or control automatic redirects so every redirect target
is revalidated, and reject private, loopback, and link-local destinations at
connection time. Apply validation to each resolved connection rather than
relying on one pre-request DNS lookup, while preserving the existing streamed
download and SHA-256/ONNX validation flow.
Source: Path instructions
| #[test] | ||
| fn accepts_oauth_query_variant() { | ||
| let url = "live-mr-manager://oauth?token=shortForm"; | ||
| assert_eq!(parse_oauth_callback_url(url).as_deref(), Some("shortForm")); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline src-tauri/src/songbook_auth.rs --items all
rg -n -C 4 'parse_oauth_callback_url|apply_session_token|oauth|state=|nonce' src-tauri/srcRepository: AutumnColor77/Live-MR-Manager
Length of output: 7735
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- songbook_auth.rs ---'
sed -n '1,230p' src-tauri/src/songbook_auth.rs
printf '%s\n' '--- OAuth-related symbols and configuration ---'
rg -n -C 5 'songbook|oauth|OAuth|state|nonce|deep.?link|live-mr-manager' src-tauri/src src-tauri/Cargo.toml package.json 2>/dev/null || true
printf '%s\n' '--- URL parser edge-case probe ---'
python3 - <<'PY'
from urllib.parse import unquote
def parser(raw):
trimmed = raw.strip().strip("\"'")
if not trimmed.startswith("live-mr-manager:"):
return None
if "oauth/callback" not in trimmed and "oauth?" not in trimmed and "://oauth" not in trimmed:
return None
marker = "token="
idx = trimmed.find(marker)
if idx < 0:
return None
token = trimmed[idx + len(marker):]
for delimiter in ("&", "#"):
token = token.split(delimiter, 1)[0]
token = unquote(token)
return token or None
cases = [
"live-mr-manager://oauth/callback?token=abc",
"live-mr-manager://oauth/anything?token=abc",
"live-mr-manager://oauth-malicious.example/?token=abc",
"live-mr-manager://not-oauth?x=1&token=abc",
"live-mr-manager://oauth/callback?x=token=abc",
"live-mr-manager://oauth/callback?not_token=abc",
"live-mr-manager://oauth/callback?token=abc%26state%3Dwrong",
]
for value in cases:
print(f"{value!r} -> {parser(value)!r}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 50386
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- session-token validator ---'
rg -n -A 35 -B 8 'pub fn validate_session_token|fn validate_session_token' src-tauri/src/ipc_validate.rs
printf '%s\n' '--- focused OAuth references ---'
rg -n -S -g '*.rs' -g '*.ts' -g '*.tsx' -g '*.js' -g '*.jsx' \
'oauth|OAuth|state=|nonce|callback\?token|live-mr-manager://' \
src src-tauri/src 2>/dev/null | head -n 240 || true
printf '%s\n' '--- deep-link registration and handler call sites ---'
rg -n -C 8 'handle_deep_link_urls|deep_link|DeepLink|register|live-mr-manager' \
src-tauri/src src-tauri/tauri.conf.json src-tauri 2>/dev/null | head -n 260 || true
printf '%s\n' '--- deterministic parser cases ---'
python3 - <<'PY'
from urllib.parse import unquote
def parse(raw):
trimmed = raw.strip().strip("\"'")
if not trimmed.startswith("live-mr-manager:"):
return None
if ("oauth/callback" not in trimmed
and "oauth?" not in trimmed
and "://oauth" not in trimmed):
return None
idx = trimmed.find("token=")
if idx < 0:
return None
rest = trimmed[idx + len("token="):]
for delimiter in ("&", "#", " ", '"'):
rest = rest.split(delimiter, 1)[0]
token = unquote(rest)
return token or None
cases = [
"live-mr-manager://oauth/callback?token=abc",
"live-mr-manager://oauth/anything?token=abc",
"live-mr-manager://oauth-malicious.example/?token=abc",
"live-mr-manager://not-oauth?x=1&token=abc",
"live-mr-manager://oauth/callback?x=token=abc",
"live-mr-manager://oauth/callback?not_token=abc",
"live-mr-manager://oauth/callback?token=abc%26state%3Dwrong",
]
for value in cases:
print(f"{value!r} -> {parse(value)!r}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 22246
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- frontend Songbook URL helpers ---'
sed -n '1,110p' src/js/companion-links.js
printf '%s\n' '--- frontend Songbook auth flow ---'
sed -n '1,240p' src/js/events/songbook-auth.jsRepository: AutumnColor77/Live-MR-Manager
Length of output: 11589
Define one authenticated desktop callback contract.
Keep live-mr-manager://oauth?token=... only if it is an intentional exact route. Parse the URL structurally and extract token only from a query pair. The current parser accepts arbitrary OAuth-like hosts and paths, plus names such as not_token.
The desktop login flow has no state or nonce check. Bind each callback to a value created when login starts. Do not log the raw callback URL because it contains the bearer token.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src-tauri/src/songbook_auth.rs` around lines 203 - 206, Update
parse_oauth_callback_url and the desktop login flow to enforce one exact
callback route, structurally parse the URL, and extract only the token query
key. Bind each login attempt to a generated state or nonce and validate it on
callback before accepting the token; reject arbitrary hosts, paths, and
similarly named parameters, and ensure raw callback URLs are never logged.
Source: Path instructions
| #[test] | ||
| fn rejects_non_youtube_for_id_extract() { | ||
| assert!(extract_youtube_video_id("https://vimeo.com/123").is_none()); | ||
| assert!(extract_youtube_video_id("C:\\local\\track.mp3").is_none()); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- target file outline ---'
ast-grep outline src-tauri/src/youtube_url.rs 2>/dev/null || true
printf '%s\n' '--- target implementation and tests ---'
cat -n src-tauri/src/youtube_url.rs | sed -n '1,180p'
printf '%s\n' '--- related symbols and call sites ---'
rg -n -C 3 'extract_youtube_video_id|cache_key_variants' src-tauri/srcRepository: AutumnColor77/Live-MR-Manager
Length of output: 12978
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- URL parsing dependencies and existing URL validation ---'
rg -n -C 2 'url::|Url::parse|youtube\.com|youtu\.be|extract_youtube_video_id' \
--glob '*.rs' --glob 'Cargo.toml' --glob 'Cargo.lock' .
printf '%s\n' '--- exact behavioral probe for the current helper ---'
python3 - <<'PY'
def extract(u):
u = u.strip()
for marker, separators in (
("youtu.be/", "?&/#"),
("watch?v=", "&/#?"),
("/shorts/", "?&/#"),
("/embed/", "?&/#"),
):
idx = u.find(marker)
if idx >= 0:
tail = u[idx + len(marker):]
ident = tail.split(separators[0], 1)[0]
for sep in separators[1:]:
ident = ident.split(sep, 1)[0]
ident = ident.strip()
if ident:
return ident
return None
def variants(path):
result = [path.strip().replace("\\", "/")]
ident = extract(path)
if ident is not None:
result += [
f"https://youtu.be/{ident}",
f"https://www.youtube.com/watch?v={ident}",
f"https://youtube.com/watch?v={ident}",
]
return sorted(set(result))
cases = [
"https://evil.example/?next=https://youtu.be/abc123",
"https://evil.example/?next=https://www.youtube.com/watch?v=abc123",
"https://evil.example/watch?v=abc123",
"https://notyoutube.com/watch?v=abc123",
"C:\\local\\track.mp3",
"https://youtu.be/abc123?t=10",
"https://www.youtube.com/watch?v=xyz789&list=foo",
]
for case in cases:
print(f"{case!r} -> id={extract(case)!r}")
print(" variants:", variants(case))
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 13155
Parse and validate the URL before extracting the YouTube video ID.
extract_youtube_video_id accepts embedded YouTube strings from non-YouTube hosts. cache_key_variants then adds canonical YouTube keys and can select media for a different URL. Allow only approved YouTube hosts and supported path formats. Add regression tests for spoofed query URLs and non-YouTube hosts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src-tauri/src/youtube_url.rs` around lines 109 - 113, Update
extract_youtube_video_id to parse and validate the input URL before extracting
an ID, accepting only approved YouTube hosts and supported YouTube path formats;
reject embedded YouTube-like values from non-YouTube hosts. Extend
rejects_non_youtube_for_id_extract with spoofed query URLs and additional
non-YouTube host cases, while preserving valid YouTube extraction behavior used
by cache_key_variants.
| "assetProtocol": { | ||
| "scope": [ | ||
| "$APPDATA/**", | ||
| "$LOCALAPPDATA/**", | ||
| "**" | ||
| "$DOCUMENT/**", | ||
| "$DOWNLOAD/**", | ||
| "$DESKTOP/**", | ||
| "$AUDIO/**", | ||
| "$HOME/Music/**", | ||
| "$HOME/**", | ||
| "$TEMP/**" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
Restrict the asset protocol scope.
$HOME/** permits asset-protocol access to all files below the user's home directory. This can include credentials and unrelated application data. Remove this scope. Add only the specific media and cache directories that the application must serve.
As per path instructions, src-tauri/tauri.conf.json must flag overly broad assetProtocol scopes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src-tauri/tauri.conf.json` around lines 28 - 38, Restrict the assetProtocol
scope in the tauri configuration by removing the broad "$HOME/**" entry. Retain
only the explicitly required media and cache directory scopes already listed,
adding specific directories only when required by the application.
Source: Path instructions
Skip copying DLLs that already live in target/debug/deps so the unit-test job can proceed to execution. Co-authored-by: Cursor <cursoragent@cursor.com>
Drop fragile DLL copy staging and rely on target/debug(+deps) PATH so unit tests can execute after a successful build. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rust-unit-tests.yml:
- Around line 55-56: Update the Visual C++ redistributable installation step
after Start-Process and the existing exit-code log to accept only exit codes 0,
1641, and 3010; throw for any other $p.ExitCode so the job stops before running
tests.
- Around line 52-55: Update the VC redistributable setup around the PowerShell
variables $url, $out, and Start-Process to download from an approved immutable
versioned URL instead of the moving aka.ms alias, then verify the downloaded
installer’s SHA-256 hash or Authenticode signature against the approved value
before execution. Abort the workflow when verification fails, and only invoke
Start-Process after successful validation.
In @.github/workflows/security.yml:
- Line 78: Update the Trivy action reference in the security workflow to use a
reviewed full commit SHA instead of the movable v0.36.0 tag, and retain the
version identifier in an adjacent comment.
- Line 88: Update the Trivy configuration in the security workflow by removing
ignore-unfixed: true so unfixed HIGH and CRITICAL vulnerabilities remain in the
report and blocking exit decision; only retain an exclusion if the workflow also
documents the exception and publishes a separate report containing those
findings.
In @.gitleaks.toml:
- Around line 6-23: Restrict the [allowlist] entries to AND rules pairing each
exact OAuth fixture file path with its complete expected token value, rather
than globally matching token substrings. Remove the broad reports/ path
exemption and allow only the specifically required generated report files,
preserving exclusions for .env.example as appropriate.
In `@web/companion/package.json`:
- Around line 24-27: Update the dependency pins in the package.json overrides
block for nanoid, postcss, and sharp to exact versions matching the lockfile,
then regenerate the lockfile so it reflects those exact overrides.
- Line 27: Update the companion package configuration to require Node.js 20.9.0
or newer, matching sharp@0.35.3, and add the corresponding Node.js runtime
configuration for the Vercel deployment. Ensure this applies to the Vercel
companion runtime rather than relying on the release workflow’s Node.js 22
setting.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0b41a1cc-d992-4de5-9cfc-9ca6ed510a4c
⛔ Files ignored due to path filters (2)
src-tauri/Cargo.lockis excluded by!**/*.lock,!**/Cargo.lockweb/companion/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (5)
.github/workflows/rust-unit-tests.yml.github/workflows/security.yml.gitleaks.tomlsrc-tauri/src/songbook_auth.rsweb/companion/package.json
| $url = "https://aka.ms/vs/17/release/vc_redist.x64.exe" | ||
| $out = Join-Path $env:TEMP "vc_redist.x64.exe" | ||
| Invoke-WebRequest -Uri $url -OutFile $out -UseBasicParsing | ||
| $p = Start-Process -FilePath $out -ArgumentList "/install","/quiet","/norestart" -Wait -PassThru |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
file=".github/workflows/rust-unit-tests.yml"
printf '%s\n' "=== workflow outline ==="
if command -v ast-grep >/dev/null 2>&1; then
ast-grep outline "$file" || true
fi
printf '%s\n' "=== workflow lines 1-120 ==="
cat -n "$file" | sed -n '1,120p'
printf '%s\n' "=== related workflow security and installer references ==="
rg -n -C 3 'pull_request_target|checkout@|permissions:|vc_redist|aka\.ms|Start-Process|Invoke-WebRequest|Get-FileHash|rust-unit-tests' .github/workflowsRepository: AutumnColor77/Live-MR-Manager
Length of output: 10263
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '=== aka.ms redirect chain ==='
curl -sSIL --max-redirs 5 'https://aka.ms/vs/17/release/vc_redist.x64.exe' |
sed -n '/^HTTP\//p;/^[Ll]ocation:/p;/^[Cc]ontent-[Ll]ength:/p;/^[Ll]ast-[Mm]odified:/p'
printf '%s\n' '=== repository references to VC redist versions or hashes ==='
rg -n -i -C 2 'vc.redist|vc_redist|visual c\+\+ redistributable|sha256|authenticode' . --glob '!target/**' --glob '!node_modules/**' || trueRepository: AutumnColor77/Live-MR-Manager
Length of output: 23161
Pin and verify the VC redistributable before execution.
The workflow executes a file from a moving aka.ms alias without integrity validation. Pin an approved immutable installer URL and verify its SHA-256 or Authenticode signature before Start-Process.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/rust-unit-tests.yml around lines 52 - 55, Update the VC
redistributable setup around the PowerShell variables $url, $out, and
Start-Process to download from an approved immutable versioned URL instead of
the moving aka.ms alias, then verify the downloaded installer’s SHA-256 hash or
Authenticode signature against the approved value before execution. Abort the
workflow when verification fails, and only invoke Start-Process after successful
validation.
| $p = Start-Process -FilePath $out -ArgumentList "/install","/quiet","/norestart" -Wait -PassThru | ||
| Write-Host "vc_redist exit: $($p.ExitCode)" |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/**'
printf '%s\n' '--- target workflow ---'
cat -n .github/workflows/rust-unit-tests.yml
printf '%s\n' '--- installer references ---'
rg -n -C 4 'vc_redist|Start-Process|Visual C|3010|allowedExitCodes' .github/workflows README.md 2>/dev/null || trueRepository: AutumnColor77/Live-MR-Manager
Length of output: 5506
🌐 Web query:
Microsoft Visual C++ Redistributable latest supported downloads installer return codes 0 3010 1641
💡 Result:
When deploying Microsoft Visual C++ Redistributables, the return codes 0, 1641, and 3010 are all considered successful outcomes [1][2][3]. These codes indicate the following: - 0 (ERROR_SUCCESS): The operation completed successfully [1][2]. - 1641 (ERROR_SUCCESS_REBOOT_INITIATED): The operation completed successfully and the installer has initiated a restart [1][2][4]. - 3010 (ERROR_SUCCESS_REBOOT_REQUIRED): The operation completed successfully, but a system restart is required before the installed software can be used [1][2][3][5]. Because codes 1641 and 3010 are technically success codes that notify the system or administrator of a pending or active restart, automation tools and deployment scripts often incorrectly interpret them as failures [3][6][7][8]. To ensure robust deployments, scripts should be configured to treat 0, 1641, and 3010 as successful outcomes [3][6][8]. If desired, a system restart can be enforced after receiving 3010, or allowed to proceed naturally if 1641 was returned [3].
Citations:
- 1: https://github.com/MicrosoftDocs/visualstudio-docs/blob/main/docs/install/includes/install-error-codes-md.md
- 2: https://learn.microsoft.com/en-us/windows/win32/msi/error-codes
- 3: https://knowledge.broadcom.com/external/article/152125/installation-or-a-scripted-install-retur.html
- 4: https://help.pdq.com/hc/en-us/articles/220530547-Error-code-1641
- 5: https://community.jaspersoft.com/knowledgebase/troubleshooting/running-windows-bundled-installer-we-receive-error-running-vcredistgx64-exit-code-3010/
- 6: win_package not accepting exit code 3010 as success ansible/ansible-modules-extras#3067
- 7: https://stackoverflow.com/questions/63767288/inno-setup-and-vc-redistributable-and-handling-exit-code-3010-gracefully
- 8: msiexec return code 1641 is not an "error" saltstack/salt#44389
Fail the job when installation fails.
Treat only exit codes 0, 1641, and 3010 as successful. Throw for all other $p.ExitCode values before running tests.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/rust-unit-tests.yml around lines 55 - 56, Update the
Visual C++ redistributable installation step after Start-Process and the
existing exit-code log to accept only exit codes 0, 1641, and 3010; throw for
any other $p.ExitCode so the job stops before running tests.
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Scan dependency lockfiles for CVEs | ||
| uses: aquasecurity/trivy-action@v0.36.0 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/**'
printf '%s\n' '--- security workflow excerpt ---'
nl -ba .github/workflows/security.yml | sed -n '1,130p'
printf '%s\n' '--- scanner and action configuration ---'
rg -n -C 4 'trivy|semgrep|ignore-unfixed|severity|exit-code|uses:' .github/workflows/security.ymlRepository: AutumnColor77/Live-MR-Manager
Length of output: 3762
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- security workflow ---'
sed -n '1,125p' .github/workflows/security.yml
printf '%s\n' '--- Trivy tag resolution ---'
git ls-remote --tags https://github.com/aquasecurity/trivy-action.git \
'refs/tags/v0.36.0' 'refs/tags/v0.36.0^{}'Repository: AutumnColor77/Live-MR-Manager
Length of output: 3971
Pin the Trivy action to a full commit SHA.
aquasecurity/trivy-action@v0.36.0 uses a movable tag. A tag change can alter the scanner code used by this security gate. Use a reviewed full commit SHA and retain the version in a comment.
🧰 Tools
🪛 zizmor (1.29.0)
[error] 78-78: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/security.yml at line 78, Update the Trivy action reference
in the security workflow to use a reviewed full commit SHA instead of the
movable v0.36.0 tag, and retain the version identifier in an adjacent comment.
Source: Linters/SAST tools
| severity: CRITICAL,HIGH | ||
| exit-code: "1" | ||
| format: table | ||
| ignore-unfixed: true |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow excerpt ---'
sed -n '1,150p' .github/workflows/security.yml
printf '%s\n' '--- workflow references ---'
rg -n --hidden -S 'trivy|semgrep|ignore-unfixed|exit-code|severity|security' .github/workflows README.md 2>/dev/null || true
printf '%s\n' '--- repository files relevant to gates ---'
git ls-files | rg '(^|/)(security|semgrep|trivy|quality|workflow)' | head -100Repository: AutumnColor77/Live-MR-Manager
Length of output: 5869
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
path = Path(".github/workflows/security.yml")
text = path.read_text()
match = re.search(
r"uses:\s*aquasecurity/trivy-action@(?P<ref>\S+)(?P<body>.*?)(?=\n\s{4}- name:|\Z)",
text,
re.S,
)
if not match:
raise SystemExit("Trivy action step not found")
body = match.group("body")
inputs = dict(re.findall(r"^\s{10}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.+?)\s*$", body, re.M))
print("trivy_ref:", match.group("ref"))
print("trivy_inputs:", inputs)
print("high_critical_gate:", inputs.get("severity") == "CRITICAL,HIGH" and inputs.get("exit-code") == '"1"')
print("ignore_unfixed_enabled:", inputs.get("ignore-unfixed") == "true")
print("quality_gate_checks_trivy:", bool(re.search(r'needs\.trivy\.result.*?!=.*?success', text, re.S)))
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 461
🌐 Web query:
aquasecurity trivy fs scan ignore-unfixed true official documentation exit-code severity
💡 Result:
When running an Aqua Security Trivy filesystem (fs) scan, the --ignore-unfixed, --exit-code, and --severity flags interact to determine the scan outcome and exit status as follows: --ignore-unfixed: This flag instructs Trivy to report only vulnerabilities that have a defined fixed version [1][2]. Vulnerabilities without a fix are excluded from the scan results. --severity: This flag filters the vulnerabilities displayed in the report (e.g., --severity HIGH,CRITICAL) [1][3][4]. Only issues matching the specified severity levels are included in the output. --exit-code: By default, Trivy exits with code 0 regardless of whether security issues are found [4]. To use Trivy in CI/CD pipelines, you must explicitly set --exit-code to a non-zero value (typically 1). When set, Trivy will exit with that code if any vulnerabilities are detected that meet your filter criteria (i.e., those that are fixed, if --ignore-unfixed is used, and those that match your specified --severity) [4]. Important Usage Notes: - Flag Placement: The --exit-code flag is a global option [5]. For best results, place it before the subcommand (e.g., trivy --exit-code 1 fs /path/to/scan) to ensure it is correctly parsed and applied [5]. - Combined Logic: If you run a command such as trivy --exit-code 1 fs --ignore-unfixed --severity HIGH,CRITICAL., Trivy will only trigger the non-zero exit code if it finds vulnerabilities that are both fixed AND classified as HIGH or CRITICAL severity [1][4]. If a vulnerability is HIGH but has no fix, or if it has a fix but is only MEDIUM severity, it will be ignored, and Trivy will exit with code 0 [4][6].
Citations:
- 1: https://trivy.dev/docs/v0.53/guide/references/configuration/cli/trivy_filesystem/
- 2: https://trivy.dev/docs/v0.50/guide/references/configuration/cli/trivy_filesystem/
- 3: https://github.com/aquasecurity/trivy/blob/main/docs/guide/references/configuration/cli/trivy_filesystem.md
- 4: https://trivy.dev/docs/latest/guide/configuration/others/
- 5: Setting --exit-code when using the fs option in a certain order has no effect aquasecurity/trivy#529
- 6: *--ignore-unfixed* does not work as expected aquasecurity/trivy#3973
Do not exclude unfixed HIGH/CRITICAL vulnerabilities from the blocking gate.
ignore-unfixed: true excludes vulnerabilities without a published fix from the Trivy report and exit decision. Remove this option, or document the exception and publish a separate report containing the ignored findings.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/security.yml at line 88, Update the Trivy configuration in
the security workflow by removing ignore-unfixed: true so unfixed HIGH and
CRITICAL vulnerabilities remain in the report and blocking exit decision; only
retain an exclusion if the workflow also documents the exception and publishes a
separate report containing those findings.
Source: Path instructions
| [allowlist] | ||
| description = "Unit-test fixtures and documented non-secrets (not real credentials)" | ||
| paths = [ | ||
| '''(^|/)reports/''', | ||
| '''(^|/)\.env\.example$''', | ||
| ] | ||
| regexes = [ | ||
| # OAuth deep-link parser fixtures in Rust unit tests (current + historical PR commits) | ||
| '''token=abc\.def-123_XYZ''', | ||
| '''token=hello%2Bworld%2F~''', | ||
| '''token=hello%2Bworld%2Fplain''', | ||
| '''token=quotedToken''', | ||
| '''token=shortForm''', | ||
| '''token=part1''', | ||
| '''token=unittest-''', | ||
| '''token=test-fixture''', | ||
| '''token=test-fixture-plain''', | ||
| ] |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- .gitleaks.toml ---'
cat -n .gitleaks.toml
printf '%s\n' '--- matching tracked paths ---'
git ls-files | rg '(^|/)reports/|(^|/)\.env\.example$' || true
printf '%s\n' '--- occurrences of allowlisted token fragments ---'
rg -n --hidden --glob '!.git' \
'token=(abc\.def-123_XYZ|hello%2Bworld%2F~|hello%2Bworld%2Fplain|quotedToken|shortForm|part1|unittest-|test-fixture|test-fixture-plain)' \
. || true
printf '%s\n' '--- gitleaks availability ---'
if command -v gitleaks >/dev/null 2>&1; then
gitleaks version
else
printf '%s\n' 'gitleaks: unavailable'
fiRepository: AutumnColor77/Live-MR-Manager
Length of output: 2111
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- gitleaks stdin help ---'
gitleaks stdin --help 2>&1 | sed -n '1,180p'
printf '%s\n' '--- gitleaks detect help ---'
gitleaks detect --help 2>&1 | sed -n '1,180p'
printf '%s\n' '--- configured regex matches ---'
python3 - <<'PY'
import re
from pathlib import Path
text = Path(".gitleaks.toml").read_text()
patterns = re.findall(r"'''([^']+)'''", text.split("regexes =", 1)[1])
samples = [
"token=part1",
"token=part1-extra-secret",
"token=unittest-",
"token=unittest-real-secret",
"token=test-fixture",
"token=test-fixture-plain",
"token=test-fixture-real-secret",
"token=abc.def-123_XYZ",
]
for sample in samples:
matched = [pattern for pattern in patterns if re.search(pattern, sample)]
print(f"{sample}: {matched}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 9986
🌐 Web query:
Gitleaks configuration allowlist paths regexes semantics regexTarget multiple allowlists
💡 Result:
In Gitleaks, allowlists are used to ignore findings based on criteria such as paths, regex patterns, commits, or stop words [1][2]. The configuration supports both global allowlists (defined at the top level) and rule-specific allowlists (defined within a specific rule) [3][4][2]. Key semantics and configurations for allowlists include: Multiple Allowlists: You can define multiple allowlist blocks for a single rule or globally [4][2]. A finding is ignored if it matches ANY of the provided allowlist blocks [4][2]. Condition Logic: Within a single allowlist block, you can specify a condition (default is "OR") to determine how different criteria are evaluated [1][2]. - OR: The finding is ignored if any of the criteria (e.g., paths OR regexes) match [1][2]. - AND: The finding is ignored only if ALL specified criteria within that block match [3][2]. RegexTarget Semantics: The regexTarget field determines what content the regexes are tested against [1][5]. - secret (default): The regex is tested against the captured secret [3][2]. - match: The regex is tested against the entire matched string (the full result of the rule's regex) [6][2]. - line: The regex is tested against the entire line where the leak was found [6][2]. Note: regexTarget applies to the regexes field within the same allowlist block [1][5]. If you need to check different targets (e.g., one regex against the secret and another against the line), you should define separate [[rules.allowlists]] blocks, as each block allows only one regexTarget [7][8]. Global vs. Rule-specific: - Global allowlists: Applied to all rules unless targetRules is specified [8][4][2]. Global allowlists take higher precedence [4][2]. - Rule-specific allowlists: Defined under [[rules.allowlists]] and apply only to the parent rule [3][9][2]. Configuration syntax has evolved; as of recent versions, [[allowlists]] and [[rules.allowlists]] (using the double bracket array syntax) are the standard for defining multiple allowlist entries [4][2].
Citations:
- 1: https://github.com/gitleaks/gitleaks/blob/master/config/allowlist.go
- 2: https://github.com/gitleaks/gitleaks/blob/master/README.md
- 3: Allowlist entry in gitleaks.toml does not skip detection of pattern gitleaks/gitleaks#1878
- 4: https://openaitx.github.io/projects/gitleaks/gitleaks/README-en.html
- 5: https://github.com/gitleaks/gitleaks/blob/master/config/config.go
- 6: Feat/allowlist regex target gitleaks/gitleaks#1107
- 7: Define multiple allowlists per rule gitleaks/gitleaks#1496
- 8: Define multiple global allowlists gitleaks/gitleaks#1777
- 9: Allowlists not working gitleaks/gitleaks#1590
🏁 Script executed:
#!/bin/bash
set +e
for value in \
'token=part1' \
'token=part1-real-secret-1234567890abcdef' \
'token=unittest-real-secret-1234567890abcdef' \
'token=test-fixture-real-secret-1234567890abcdef'
do
printf '%s\n' "--- $value ---"
printf '%s\n' "$value" |
gitleaks stdin --config .gitleaks.toml --no-banner --report-format json --report-path - 2>&1
printf 'exit=%s\n' "$?"
doneRepository: AutumnColor77/Live-MR-Manager
Length of output: 695
Bind token exceptions to exact fixture paths.
The reports/ pattern exempts every path under any reports directory. The global, unanchored regexes also match extended values such as token=part1-real-secret. Use AND allowlists that combine each exact fixture path with its complete fixture value. Exclude only the required generated report files.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.gitleaks.toml around lines 6 - 23, Restrict the [allowlist] entries to AND
rules pairing each exact OAuth fixture file path with its complete expected
token value, rather than globally matching token substrings. Remove the broad
reports/ path exemption and allow only the specifically required generated
report files, preserving exclusions for .env.example as appropriate.
| "overrides": { | ||
| "nanoid": "^3.3.17", | ||
| "postcss": "^8.5.23", | ||
| "sharp": "^0.35.0" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python - <<'PY'
import json
import re
from pathlib import Path
data = json.loads(Path("web/companion/package.json").read_text())
overrides = data.get("overrides", {})
pattern = re.compile(r"^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$")
for name, spec in overrides.items():
if not pattern.fullmatch(spec):
raise SystemExit(f"{name} is not an exact version: {spec}")
print("All overrides use exact versions.")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 207
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- package manifest ---'
cat -n web/companion/package.json | sed -n '1,60p'
printf '%s\n' '--- lockfiles ---'
find web/companion -maxdepth 2 -type f \( -name 'package-lock.json' -o -name 'npm-shrinkwrap.json' -o -name 'yarn.lock' -o -name 'pnpm-lock.yaml' \) -print
printf '%s\n' '--- override references and dependency context ---'
rg -n -C 3 '"(nanoid|postcss|sharp)"|overrides|pin' web/companion package.json package-lock.json npm-shrinkwrap.json yarn.lock pnpm-lock.yaml 2>/dev/null || trueRepository: AutumnColor77/Live-MR-Manager
Length of output: 4397
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
import json
import re
from pathlib import Path
data = json.loads(Path("web/companion/package.json").read_text())
overrides = data.get("overrides", {})
exact = re.compile(r"^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$")
for name, spec in overrides.items():
print(f"{name}: {spec} | exact={bool(exact.fullmatch(spec))} | range={spec.startswith(('^', '~', '>', '<', '='))}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 296
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- companion lockfile header and root metadata ---'
cat -n web/companion/package-lock.json | sed -n '1,45p'
printf '%s\n' '--- resolved package entries ---'
python3 - <<'PY'
import json
from pathlib import Path
lock = json.loads(Path("web/companion/package-lock.json").read_text())
root = lock.get("packages", {}).get("", {})
print("lockfileVersion:", lock.get("lockfileVersion"))
print("root overrides:", root.get("overrides"))
for name in ("nanoid", "postcss", "sharp"):
matches = []
for path, entry in lock.get("packages", {}).items():
if path == f"node_modules/{name}" or path.endswith(f"/node_modules/{name}"):
matches.append((path, entry.get("version"), entry.get("resolved")))
print(name, matches)
PY
printf '%s\n' '--- repository references to override intent ---'
rg -n -i -C 3 'override|pin(ned|ning)?|nanoid|postcss|sharp' --glob '!web/companion/package-lock.json' .Repository: AutumnColor77/Live-MR-Manager
Length of output: 50385
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path
manifest = json.loads(Path("web/companion/package.json").read_text())
lock = json.loads(Path("web/companion/package-lock.json").read_text())
root = lock["packages"][""]
print("manifest overrides:", manifest.get("overrides"))
print("lock root overrides:", root.get("overrides"))
for name, spec in manifest.get("overrides", {}).items():
print(f"{name}: manifest={spec!r}, lock={root.get('overrides', {}).get(name)!r}")
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 391
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
import json
from pathlib import Path
manifest = json.loads(Path("web/companion/package.json").read_text())
lock = json.loads(Path("web/companion/package-lock.json").read_text())
def caret_allows(spec, candidate):
base = tuple(map(int, spec[1:].split(".")))
version = tuple(map(int, candidate.split(".")))
if version < base:
return False
if base[0] > 0:
return version[0] == base[0]
if base[1] > 0:
return version[:2] == base[:2]
return version[:3] == base[:3]
for name, spec in manifest["overrides"].items():
current = lock["packages"][f"node_modules/{name}"]["version"]
later = ".".join(map(str, tuple(map(int, current.split(".")))[:2] + (int(current.split(".")[2]) + 1,)))
print(
f"{name}: spec={spec}, lock={current}, "
f"lock_satisfies={caret_allows(spec, current)}, "
f"later_patch={later}, later_patch_satisfies={caret_allows(spec, later)}"
)
PYRepository: AutumnColor77/Live-MR-Manager
Length of output: 476
Use exact versions for dependency pins.
The lockfile currently resolves nanoid, postcss, and sharp to exact versions, but their caret ranges allow later compatible releases during regeneration. Use exact manifest overrides and update the lockfile, or document that these are compatible-release ranges.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/companion/package.json` around lines 24 - 27, Update the dependency pins
in the package.json overrides block for nanoid, postcss, and sharp to exact
versions matching the lockfile, then regenerate the lockfile so it reflects
those exact overrides.
| "overrides": { | ||
| "nanoid": "^3.3.17", | ||
| "postcss": "^8.5.23", | ||
| "sharp": "^0.35.0" |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n --hidden \
-g 'package.json' \
-g '.nvmrc' \
-g '*.yml' \
-g '*.yaml' \
'(engines|node-version|setup-node|FROM node|NODE_VERSION)' \
web/companion .github || true
npm view sharp@0.35.0 engines --jsonRepository: AutumnColor77/Live-MR-Manager
Length of output: 633
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- companion package metadata ---'
cat -n web/companion/package.json | sed -n '1,120p'
printf '%s\n' '--- repository manifests and version files ---'
git ls-files '*package.json' '*package-lock.json' '*yarn.lock' '*pnpm-lock.yaml' '.nvmrc' '.node-version' 'Dockerfile*' \
| sed -n '1,160p'
printf '%s\n' '--- all workflow files ---'
git ls-files '.github/workflows/*' | while IFS= read -r f; do
printf '\n--- %s ---\n' "$f"
cat -n "$f"
done
printf '%s\n' '--- Node version declarations and sharp references ---'
rg -n --hidden \
-g 'package.json' \
-g '.nvmrc' \
-g '.node-version' \
-g 'Dockerfile*' \
-g '*.yml' \
-g '*.yaml' \
'(engines|node-version|setup-node|FROM node|NODE_VERSION|sharp)' \
. || trueRepository: AutumnColor77/Live-MR-Manager
Length of output: 16461
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- root package metadata ---'
cat -n package.json | sed -n '1,180p'
printf '%s\n' '--- companion lockfile sharp/next metadata ---'
python3 - <<'PY'
import json
from pathlib import Path
for name in ("package-lock.json", "web/companion/package-lock.json"):
p = Path(name)
print(f"--- {name} ---")
if not p.exists():
print("missing")
continue
data = json.loads(p.read_text())
print("lockfileVersion:", data.get("lockfileVersion"))
packages = data.get("packages", {})
for key, meta in packages.items():
if key == "" or any(x in key.lower() for x in ("/sharp", "/next", "/node_modules/@img/")):
selected = {k: meta[k] for k in ("version", "engines", "resolved") if k in meta}
print(key or "<root>", selected)
PY
printf '%s\n' '--- companion references and install/build commands ---'
rg -n --hidden \
-g '!*.lock' \
-g '!node_modules' \
-g '!dist' \
-g '!build' \
'(web/companion|companion|npm (install|ci|run)|next (build|start|dev)|node-version|NODE_VERSION|Node\.js|nodejs|engines)' \
. || true
printf '%s\n' '--- deployment/container/configuration files ---'
git ls-files | rg '(^|/)(Dockerfile[^/]*|docker-compose[^/]*|render\.yaml|vercel\.json|netlify\.toml|fly\.toml|railway\.json|\.nvmrc|\.node-version|.*cloudbuild.*|.*workflow.*)$' || trueRepository: AutumnColor77/Live-MR-Manager
Length of output: 15912
Set the companion runtime to Node.js 20.9 or newer.
web/companion/package-lock.json resolves sharp@0.35.3, which requires Node.js >=20.9.0. Add the same Node.js floor to web/companion/package.json and configure the Vercel runtime. The release workflow's Node.js 22 setting does not cover the Vercel companion deployment.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/companion/package.json` at line 27, Update the companion package
configuration to require Node.js 20.9.0 or newer, matching sharp@0.35.3, and add
the corresponding Node.js runtime configuration for the Vercel deployment.
Ensure this applies to the Vercel companion runtime rather than relying on the
release workflow’s Node.js 22 setting.
…d failures. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
cargo-audit/npm auditpre-build scripts.Test plan
OPENAI_KEYrepo secret and confirmAI Review/ Qodo workflow runs./scripts/check-licenses.ps1(or.sh) passes.envremains gitignored)Summary by CodeRabbit
Security
Quality & Reliability
Configuration
Documentation