feat(utils): add helper managers and release automation hardening#31
feat(utils): add helper managers and release automation hardening#31rolling-codes merged 4 commits intomainfrom
Conversation
Reviewer's GuideAdds prompt-length guarding and stale rate-limit bucket pruning to the OpenClaude AI plugin, hardens the auto-fix workflow security, and introduces new release automation plus corresponding tests and release notes for v4.1.1. Sequence diagram for updated ask flow with prompt limit and stale bucket pruningsequenceDiagram
actor User
participant Discord
participant OpenClaudePlugin
participant RateLimiter
User->>Discord: Slash command ask(prompt)
Discord->>OpenClaudePlugin: ask(ctx, prompt)
OpenClaudePlugin->>OpenClaudePlugin: Check len(prompt) <= _max_prompt_chars
alt Prompt too long
OpenClaudePlugin-->>Discord: ctx.respond("ai.prompt_too_long", ephemeral)
Discord-->>User: Error message
else Prompt within limit
OpenClaudePlugin->>OpenClaudePlugin: _prune_old_request_buckets(time.monotonic())
OpenClaudePlugin->>RateLimiter: _rate_limit_retry_after(ctx)
alt Rate limited
RateLimiter-->>OpenClaudePlugin: retry_after
OpenClaudePlugin-->>Discord: ctx.respond("ai.rate_limited", ephemeral)
Discord-->>User: Retry after message
else Allowed
RateLimiter-->>OpenClaudePlugin: None
OpenClaudePlugin->>OpenClaudePlugin: Call Claude API and build reply
OpenClaudePlugin-->>Discord: ctx.respond(answer)
Discord-->>User: AI response
end
end
Class diagram for updated AIPlugin and OpenClaudePlugin prompt and rate-limit behaviorclassDiagram
class AIPlugin {
-int _rate_limit
-float _rate_window
-str _thinking_key
-int _max_prompt_chars
-dict _requests
+__init__(client, db, guild_id, logger, client_session, authorizer, model, rate_limit, rate_window, thinking_key, max_prompt_chars)
+ask(ctx, prompt)
-_rate_limit_retry_after(ctx) float
-_prune_old_request_buckets(now) void
}
class OpenClaudePlugin {
+__init__(client, db, guild_id, logger, client_session, authorizer, model, rate_limit, rate_window, max_prompt_chars)
}
OpenClaudePlugin --|> AIPlugin
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (6)
📒 Files selected for processing (16)
WalkthroughAdds multiple GitHub Actions workflows (auto-fix, release, issue-triage, PR governance, PR Quality), enhances AI plugin prompt-length checks and rate-limiter cleanup, introduces manager/composer helpers, utilities (Paginator, EasyEmbed), and various bot/database/middleware robustness fixes plus corresponding tests. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 53 minutes and 16 seconds.Comment |
Review Summary by QodoSecurity hardening and release automation improvements
WalkthroughsDescription• Add prompt length validation and stale bucket pruning to AIPlugin • Harden auto-fix workflow with command validation and permission checks • Add tag-triggered GitHub Release workflow automation • Add v4.1.1 release notes and PowerShell release script Diagramflowchart LR
A["AIPlugin"] -->|"add max_prompt_chars"| B["Prompt validation"]
A -->|"add _prune_old_request_buckets"| C["Memory management"]
D["auto-fix workflow"] -->|"validate command"| E["Security hardening"]
D -->|"check permissions"| E
F["release.yml"] -->|"tag trigger"| G["GitHub Release"]
H["release.ps1"] -->|"create tag"| G
File Changes1. easycord/plugins/openclaude.py
|
Code Review by Qodo
1. fix_command injection still possible
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The
fix_commandregex inauto-fix-issues.ymlstill allows path traversal (e.g.,python scripts/../../foo.py); consider constraining it to disallow..segments (e.g., by using a path component pattern like[A-Za-z0-9._-]+(/...)*without dots-only segments).
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `fix_command` regex in `auto-fix-issues.yml` still allows path traversal (e.g., `python scripts/../../foo.py`); consider constraining it to disallow `..` segments (e.g., by using a path component pattern like `[A-Za-z0-9._-]+(/...)*` without dots-only segments).
## Individual Comments
### Comment 1
<location path=".github/workflows/auto-fix-issues.yml" line_range="42" />
<code_context>
+ const inputCommand = core.getInput("fix_command");
+
+ // Keep command execution constrained to a known safe prefix.
+ if (!/^python\s+scripts\/[A-Za-z0-9._/-]+\.py(?:\s+.*)?$/.test(inputCommand)) {
+ core.setFailed("fix_command must start with `python scripts/<name>.py`.");
+ return;
</code_context>
<issue_to_address>
**🚨 issue (security):** The `fix_command` regex still allows path traversal via `scripts/../...` despite the intended restriction.
The pattern `[A-Za-z0-9._/-]+` still accepts values like `scripts/../foo.py`, which can escape the intended directory. Tighten this by disallowing `..` segments and redundant slashes, e.g.:
```js
/^python\sscripts\/[A-Za-z0-9_/-]+\.py(?:\s+.*)?$/
```
plus an explicit guard:
```js
if (inputCommand.includes("..")) {
core.setFailed("fix_command may not contain '..' path segments.");
return;
}
```
or encode the path shape more strictly, e.g. `scripts/[A-Za-z0-9_-]+(?:/[A-Za-z0-9_-]+)*\.py`. This keeps execution aligned with the stated safety constraint.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
🤖 Augment PR SummarySummary: This PR hardens automation around issue triage and releases, and adds safety guards to the OpenClaude/AI plugin. Changes:
Technical Notes: The OpenClaude wrapper now forwards 🤖 Was this summary useful? React with 👍 or 👎 |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
easycord/plugins/openclaude.py (1)
83-116:⚠️ Potential issue | 🟡 MinorRun cleanup before the early return.
The new overlong-prompt branch returns before
_prune_old_request_buckets()runs, so stale request buckets can linger indefinitely if a user only sends rejected prompts.♻️ Proposed fix
- if self._max_prompt_chars > 0 and len(prompt) > self._max_prompt_chars: - await ctx.respond( - ctx.t( - "ai.prompt_too_long", - default="Prompt is too long. Maximum length is {limit} characters.", - limit=self._max_prompt_chars, - ), - ephemeral=True, - ) - return - self._prune_old_request_buckets(time.monotonic()) + if self._max_prompt_chars > 0 and len(prompt) > self._max_prompt_chars: + await ctx.respond( + ctx.t( + "ai.prompt_too_long", + default="Prompt is too long. Maximum length is {limit} characters.", + limit=self._max_prompt_chars, + ), + ephemeral=True, + ) + return🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@easycord/plugins/openclaude.py` around lines 83 - 116, The early-return in ask prevents _prune_old_request_buckets from running when prompts exceed _max_prompt_chars; call self._prune_old_request_buckets(time.monotonic()) at the start of ask (or immediately before any return on the overlong-prompt branch) so the request limiter is always cleaned up, ensuring stale keys are removed even for rejected prompts (update the ask method to run _prune_old_request_buckets before the length check or duplicate the call just before the existing return).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/auto-fix-issues.yml:
- Around line 42-48: The current validation for inputCommand (used to set
fix_command) allows arbitrary trailing shell content and is command-injection
vulnerable; tighten the regex to only permit the script path plus safe tokenized
arguments (e.g. change /^[^... ]/ to something like matching
/^python\s+scripts\/[A-Za-z0-9._/-]+\.py(?:\s+[A-Za-z0-9._\-/]+)*$/) or
explicitly reject shell metacharacters (reject if inputCommand contains
characters like ;&|$`<>), and prefer validating tokens instead of allowing ".*";
update the validation around inputCommand/fix_command accordingly so only safe
filenames and alphanumeric/flag-style args are accepted before the command is
later executed by the bash runner.
In @.github/workflows/release.yml:
- Around line 8-10: The workflow currently sets workflow-level permission
"contents: write", which is overly broad; remove or change that top-level
"permissions: contents: write" entry and instead grant "contents: write" only on
the specific release job by adding a job-level permissions block (e.g., under
the release job definition) so other jobs do not inherit write access; locate
the top-level "permissions: contents: write" and the release job name to make
this change and ensure other jobs either inherit default read-only permissions
or are given explicit scoped permissions as needed.
- Line 16: The workflow currently references the mutable tag
"softprops/action-gh-release@v2"; replace that with the action's immutable
commit SHA to prevent tag retargeting—locate the step using "uses:
softprops/action-gh-release@v2" and update it to "uses:
softprops/action-gh-release@<commit-sha>" where <commit-sha> is the full commit
hash from the softprops/action-gh-release repository (choose the desired release
commit), commit the change, and verify the workflow runs against the pinned SHA.
In `@tests/test_openclaude_plugin.py`:
- Around line 546-583: Remove the redundant `@pytest.mark.asyncio` decorators from
the async test functions test_aiplugin_rejects_overlong_prompt and
test_aiplugin_prunes_stale_request_buckets in tests/test_openclaude_plugin.py;
these tests are already async and the project uses asyncio_mode="auto", so
delete the decorator lines above those two function definitions and keep the
async function signatures, fixtures, and assertions unchanged.
---
Outside diff comments:
In `@easycord/plugins/openclaude.py`:
- Around line 83-116: The early-return in ask prevents
_prune_old_request_buckets from running when prompts exceed _max_prompt_chars;
call self._prune_old_request_buckets(time.monotonic()) at the start of ask (or
immediately before any return on the overlong-prompt branch) so the request
limiter is always cleaned up, ensuring stale keys are removed even for rejected
prompts (update the ask method to run _prune_old_request_buckets before the
length check or duplicate the call just before the existing return).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: ffcf35c0-cf7a-4d24-b2df-5295c70c79da
⛔ Files ignored due to path filters (4)
.gitignoreis excluded by none and included by noneRELEASES.mdis excluded by none and included by noneRELEASE_v4.1.1.mdis excluded by none and included by nonerelease.ps1is excluded by none and included by none
📒 Files selected for processing (4)
.github/workflows/auto-fix-issues.yml.github/workflows/release.ymleasycord/plugins/openclaude.pytests/test_openclaude_plugin.py
f9a9834 to
f689ef4
Compare
Summary
Ship a small-but-useful helper release that shortens common EasyCord setup and response patterns while also tightening workflow safety and release automation.
Release Impact
release:patch)release:minor)release:major)Quality Checklist
pytestpasses locallyruff check .passes locallyNotes for Reviewers
Highlights in this release:
Paginator,EasyEmbed,SecurityManager, andFrameworkManager4.2.0Validation
pytest tests -q->612 passedpython -m buildsucceededSummary by CodeRabbit
New Features
EasyEmbedfor quick Discord embed status messages (success, error, info, warning)Paginatorfor interactive embed pagination with page navigationSecurityManagerandFrameworkManagerfor simplified bot and middleware configurationBug Fixes
Improvements