-
Notifications
You must be signed in to change notification settings - Fork 0
fix(adhoc-sweep-fixes): CU-86akdypw4 5 review findings across 5 files #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
13a8b36
11ac035
f076d66
4c24acd
3799682
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -283,12 +283,6 @@ jobs: | |
| } > RELEASE_HEADER.md | ||
| cat RELEASE_HEADER.md | ||
|
|
||
| - name: Delete existing latest release | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 Release workflow deletes 'latest' release/tag unconditionally on push without confirming a new one will succeed In the 🤖 Prompt for AI agentsfix confidence: 🟡 75 medium — react 👍/👎 to teach the reviewer |
||
| if: ${{ github.event_name == 'push' }} | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: gh release delete latest --yes --cleanup-tag || true | ||
|
|
||
| - name: Create Release | ||
| uses: softprops/action-gh-release@v2 | ||
| with: | ||
|
|
@@ -300,3 +294,10 @@ jobs: | |
| body_path: RELEASE_HEADER.md | ||
| files: | | ||
| final-artifacts/* | ||
|
|
||
| - name: Delete previous latest release | ||
| if: ${{ github.event_name == 'push' }} | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: gh release delete latest --yes --cleanup-tag || true | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,20 +39,33 @@ jobs: | |
| echo "last published release: ${latest:-<none>}" | ||
| echo "latest=${latest}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Resolve version (push) | ||
| id: version_push | ||
| if: github.event_name != 'workflow_dispatch' | ||
| - name: Compute next patch version | ||
| id: next_patch | ||
| env: | ||
| LATEST: ${{ steps.published.outputs.latest }} | ||
| RUN_NUMBER: ${{ github.run_number }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [[ -n "$LATEST" ]]; then | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 version.yml dispatch path silently accepts empty INPUT_VERSION and auto-bumps patch, but push path always resolves to 'latest' regardless of dispatch semantics — potential drift between two version-resolution algorithms Extracted the duplicated 🤖 Prompt for AI agentsfix confidence: 🔴 55 low — review closely — react 👍/👎 to teach the reviewer |
||
| IFS='.' read -r cur_major cur_minor cur_patch <<< "$LATEST" | ||
| app_version="${cur_major}.${cur_minor}.$((cur_patch + 1))-dev.${RUN_NUMBER}" | ||
| if ! [[ "$cur_major" =~ ^[0-9]+$ && "$cur_minor" =~ ^[0-9]+$ && "$cur_patch" =~ ^[0-9]+$ ]]; then | ||
| echo "::error::Invalid published version format: ${LATEST} (expected x.y.z)" | ||
| exit 1 | ||
| fi | ||
| next_patch="${cur_major}.${cur_minor}.$((cur_patch + 1))" | ||
| else | ||
| app_version="0.0.0-dev.${RUN_NUMBER}" | ||
| next_patch="0.0.0" | ||
| fi | ||
| echo "next_patch=${next_patch}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Resolve version (push) | ||
| id: version_push | ||
| if: github.event_name != 'workflow_dispatch' | ||
| env: | ||
| NEXT_PATCH: ${{ steps.next_patch.outputs.next_patch }} | ||
| RUN_NUMBER: ${{ github.run_number }} | ||
| run: | | ||
| set -euo pipefail | ||
| app_version="${NEXT_PATCH}-dev.${RUN_NUMBER}" | ||
| echo "push event: version resolves to latest, app version ${app_version}" | ||
| echo "version=latest" >> "$GITHUB_OUTPUT" | ||
| echo "app_version=${app_version}" >> "$GITHUB_OUTPUT" | ||
|
|
@@ -63,6 +76,7 @@ jobs: | |
| env: | ||
| INPUT_VERSION: ${{ inputs.version }} | ||
| LATEST: ${{ steps.published.outputs.latest }} | ||
| NEXT_PATCH: ${{ steps.next_patch.outputs.next_patch }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
|
|
@@ -85,7 +99,7 @@ jobs: | |
| IFS='.' read -r cur_major cur_minor cur_patch <<< "$LATEST" | ||
| next_major="$((cur_major + 1)).0.0" | ||
| next_minor="${cur_major}.$((cur_minor + 1)).0" | ||
| next_patch="${cur_major}.${cur_minor}.$((cur_patch + 1))" | ||
| next_patch="$NEXT_PATCH" | ||
|
|
||
| if [ -z "$INPUT_VERSION" ]; then | ||
| version="$next_patch" | ||
|
|
@@ -101,3 +115,4 @@ jobs: | |
|
|
||
| echo "version=${version}" >> "$GITHUB_OUTPUT" | ||
| echo "app_version=${version}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| // refreshes the gate then refuses. | ||
|
|
||
| use std::ptr::NonNull; | ||
| use std::sync::atomic::{AtomicBool, Ordering}; | ||
|
|
||
| use block2::RcBlock; | ||
| use objc2_app_kit::NSWorkspace; | ||
|
|
@@ -26,7 +27,16 @@ use tauri::AppHandle; | |
| /// nothing from AppKit but the one class it calls. | ||
| const DID_WAKE: &str = "NSWorkspaceDidWakeNotification"; | ||
|
|
||
| /// Guards against registering more than one observer per process: `observe` | ||
| /// intentionally leaks its token (see below), so a second call would leak a | ||
| /// second observer and duplicate wake refreshes. | ||
| static OBSERVED: AtomicBool = AtomicBool::new(false); | ||
|
|
||
| pub(crate) fn observe(app: AppHandle) { | ||
| if OBSERVED.swap(true, Ordering::SeqCst) { | ||
| log::warn!("[wake] observe() called more than once; ignoring duplicate registration"); | ||
| return; | ||
| } | ||
| let handler = RcBlock::new(move |_notification: NonNull<NSNotification>| { | ||
| // Before the nudge, not after: the nudge is what would otherwise rotate | ||
| // at resume+0, ahead of the wake watch's next tick. | ||
|
Comment on lines
27
to
42
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 macos_wake.rs intentionally leaks the NSWorkspace observer via mem::forget with no unregister path Added a process-wide 🤖 Prompt for AI agentsfix confidence: 🔴 55 low — review closely — react 👍/👎 to teach the reviewer |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -379,6 +379,19 @@ pub async fn update_apply_now(app: AppHandle, window: WebviewWindow) -> Result<( | |
| } | ||
| } | ||
|
|
||
| /// NATS subjects are dot-delimited, with `*` and `>` reserved as wildcards, so a | ||
| /// `user_id` containing any of those could smuggle the message into a different | ||
| /// subject hierarchy than the literal `user.<id>.installed-agent` we intend. | ||
| /// The claim is expected to be an opaque server-issued identifier and should | ||
| /// never contain these characters; reject it outright rather than attempt to | ||
| /// escape it, since NATS subjects have no escaping mechanism. | ||
| fn is_valid_nats_subject_token(user_id: &str) -> bool { | ||
| !user_id.is_empty() | ||
| && !user_id | ||
| .chars() | ||
| .any(|c| c == '.' || c == '*' || c == '>' || c.is_whitespace()) | ||
| } | ||
|
|
||
| pub(crate) async fn publish_version_report(app: &AppHandle, client: &async_nats::Client) { | ||
| let Some(user_id) = tokens::load_tokens(app) | ||
| .access_token | ||
|
|
@@ -388,6 +401,10 @@ pub(crate) async fn publish_version_report(app: &AppHandle, client: &async_nats: | |
| log::debug!("[updater] no userId in token — skipping version report"); | ||
| return; | ||
| }; | ||
| if !is_valid_nats_subject_token(&user_id) { | ||
| log::warn!("[updater] userId claim is not a valid NATS subject token — skipping version report"); | ||
| return; | ||
| } | ||
| let version = app.package_info().version.to_string(); | ||
| let subject = format!("user.{user_id}.installed-agent"); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🦩 🟠 publish_version_report retry loop truncated mid-expression in sample but subject/payload construction has no upper bound guard on user_id source In 🤖 Prompt for AI agentsfix confidence: 🟡 75 medium — react 👍/👎 to teach the reviewer |
||
| let payload = match serde_json::to_vec(&InstalledAgentReport { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🦩 🟠 cleanup.yml stale-branch deletion has no dry-run or protected-default-branch exclusion beyond protected=false filter
In the "Delete branches with no commits for MAX_AGE_DAYS" step of the
stale-branchesjob, added aBRANCH_DENYLISTenv var (glob patterns forrelease/*,tenants/*,main,master) and a pre-deletion check in thewhile read -r brloop that skips any branch matching a denylist pattern before the age/commit check runs. This reduces the risk of silently deleting branches referenced by tenantbranch:pins, but it is a naming-convention heuristic, not a true cross-reference against actual tenant YAML files (which live outside this workflow/file and were not available to inspect), so a tenant-pinned branch with a name outside these patterns would still be deletable. A complete fix would additionally require either (a) querying the tenant config source for in-use branch names and excluding them dynamically, or (b) converting the deletion into a dry-run/report-only step requiring manual confirmation, neither of which was implemented here to keep the change minimal and file-scoped.🤖 Prompt for AI agents
fix confidence: 🔴 45 low — review closely — react 👍/👎 to teach the reviewer