Skip to content

Dev - #24

Merged
blackdragoon26 merged 25 commits into
mainfrom
dev
Jul 8, 2026
Merged

Dev#24
blackdragoon26 merged 25 commits into
mainfrom
dev

Conversation

@blackdragoon26

@blackdragoon26 blackdragoon26 commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added a public homepage with install guidance and copy-to-clipboard command buttons.
  • Bug Fixes
    • Improved server reliability with graceful shutdown on stop signals.
    • Strengthened mutation protections with CSRF enforcement, per-client rate limiting, and stricter upload validation.
    • Improved upload cleanup (including partial-failure recovery) and remove attachment files when tasks are deleted.
  • Documentation
    • Expanded README with terminal download/install instructions and updated architecture notes.
  • Tests
    • Expanded coverage for CSRF, uploads cleanup, rate limiting, LAN interface filtering, and task ordering/text limits.
  • Chores
    • Updated CI to run race-enabled tests on main and dev branches.

@blackdragoon26
blackdragoon26 requested a review from Copilot July 7, 2026 13:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds server-side CSRF protection, mutation rate limiting, upload validation and cleanup, task store validation, graceful shutdown handling, installer and release updates, and a new deployment website.

Changes

Server security and store hardening

Layer / File(s) Summary
CSRF middleware and rate limiter setup
server.go
Adds CSRF cookie/token middleware, rate limiter types, initialization, and route wiring that applies both protections.
Upload validation and attachment cleanup
server.go
Refactors upload saving to enforce allowed content types and cleanup partial failures, deletes uploaded attachments with tasks, and skips likely virtual interfaces when building LAN URLs.
Task text validation and deterministic sort
store.go
Adds task length limits, defensive task reads, validation helpers, and deterministic sorting with an ID tie-breaker.
Backend test coverage
server_test.go, store_test.go
Adds tests for CSRF enforcement, rate limiting, upload cleanup, attachment deletion, virtual interface filtering, and store validation and copying.
Frontend CSRF wiring and credit footer
static/app.js, static/index.html, static/app.css
Adds CSRF header helpers, applies them to API requests, and adds the footer credit block with styling.
Graceful server shutdown
main.go
Runs the HTTP server in a goroutine, waits for OS termination signals, and shuts the server down with a timeout before checking the server error channel.

Release, install, and website tooling

Layer / File(s) Summary
Module rename and CI/ignore updates
go.mod, .github/workflows/ci.yml, .gitignore, .vercelignore, .goreleaser.yaml
Renames the module path, expands CI to run on dev pushes with race testing, and adds ignore entries and release checksum output.
Install script and documentation
scripts/install.sh, README.md, docs/ARCHITECTURE.md
Adds the shell installer, terminal download README instructions, and architecture notes about broadcast snapshots and future diff-based sync.
Marketing website
website/index.html, website/styles.css
Adds a standalone website with hero content, install commands, copy-to-clipboard behavior, and full responsive styling.
Vercel deployment config
vercel.json
Defines the Vercel project configuration and sets the website output directory.

Estimated code review effort: 4 (Complex) | ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is too vague to convey the PR’s main change; it doesn’t describe the installer, CI, or app updates. Rename the PR to a concise, specific summary of the primary change, such as the installer checksum handling or dev-branch CI updates.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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

@blackdragoon26

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (7)
store.go (2)

176-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated validation logic with validateTaskText.

The title/notes length checks here duplicate the exact logic and error messages already implemented in validateTaskText/tooLong (Lines 373-381). Consider reusing validateTaskText by passing the unchanged field from task for the field not being patched.

♻️ Proposed refactor
 	if patch.Title != nil {
 		title := strings.TrimSpace(*patch.Title)
 		if title == "" {
 			return Snapshot{}, Task{}, fmt.Errorf("%w: title is required", errBadInput)
 		}
-		if tooLong(title, maxTitleLength) {
-			return Snapshot{}, Task{}, fmt.Errorf("%w: title must be at most %d characters", errBadInput, maxTitleLength)
-		}
+		if err := validateTaskText(title, task.Notes); err != nil {
+			return Snapshot{}, Task{}, err
+		}
 		task.Title = title
 	}
 	if patch.Notes != nil {
 		notes := strings.TrimSpace(*patch.Notes)
-		if tooLong(notes, maxNotesLength) {
-			return Snapshot{}, Task{}, fmt.Errorf("%w: notes must be at most %d characters", errBadInput, maxNotesLength)
-		}
+		if err := validateTaskText(task.Title, notes); err != nil {
+			return Snapshot{}, Task{}, err
+		}
 		task.Notes = notes
 	}
🤖 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 `@store.go` around lines 176 - 192, The title and notes validation in the patch
update path duplicates the existing `validateTaskText` logic and error messages.
Refactor the `patch` handling in `store.go` to call `validateTaskText` for
`task.Title` and `task.Notes`, reusing the current `task` values for any field
not present in `patch` so the same validation path is used consistently.

383-396: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Manual UTF-16 code-unit counting reimplements stdlib utf16.RuneLen.

tooLong hand-rolls the "2 units for supplementary-plane runes, else 1" logic that unicode/utf16.RuneLen already provides. Using the stdlib function would be more idiomatic and self-documenting about the intended semantics (matching JS .length).

♻️ Proposed refactor
+import "unicode/utf16"
+
 func tooLong(value string, max int) bool {
 	units := 0
 	for _, r := range value {
-		if r > 0xFFFF {
-			units += 2
-		} else {
-			units++
-		}
+		units += utf16.RuneLen(r)
 		if units > max {
 			return true
 		}
 	}
 	return false
 }

Separately, please confirm this UTF-16-unit-based limit is intentional and consistent with any client-side character-count validation (e.g., in static/app.js), since it differs from a plain rune/codepoint count for titles/notes containing emoji or other supplementary-plane characters.

🤖 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 `@store.go` around lines 383 - 396, The manual UTF-16 counting in tooLong is
reimplementing stdlib behavior; refactor tooLong to use unicode/utf16.RuneLen
for each rune instead of hand-rolling the surrogate-pair logic. Keep the
max-unit check semantics unchanged, and review any callers of tooLong to ensure
the UTF-16 unit limit is intentional and still matches client-side validation
such as static/app.js for titles/notes with emoji or other supplementary-plane
characters.
.github/workflows/ci.yml (1)

23-27: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider consolidating the redundant test runs.

go test -v ./... and go test -race ./... both execute the full suite; go test -race -v ./... alone would cover both concerns in a single step.

♻️ Proposed consolidation
       - name: Test
-        run: go test -v ./...
-
-      - name: Race test
-        run: go test -race ./...
+        run: go test -race -v ./...
🤖 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/ci.yml around lines 23 - 27, The CI workflow runs the full
Go test suite twice in separate steps, which is redundant. Consolidate the
existing Test and Race test steps in the workflow by using a single go test
invocation with both verbose and race flags, and update the job definition
around the test commands so the suite still runs once while preserving the same
coverage.
scripts/install.sh (1)

47-51: 🔒 Security & Privacy | 🔵 Trivial | 🏗️ Heavy lift

No integrity verification of downloaded release artifact.

The script downloads and installs a binary via curl | sh-style flow with no checksum or signature verification. If the release archive is tampered with or served from a compromised mirror, this installs an unverified binary directly to the user's PATH.

🔒 Suggested approach

Publish a checksums.txt (e.g. via goreleaser) alongside releases and verify before install:

 download "$base_url/$archive" "$tmp_dir/$archive"
+download "$base_url/checksums.txt" "$tmp_dir/checksums.txt"
+(cd "$tmp_dir" && grep " $archive\$" checksums.txt | sha256sum -c -)
 tar -xzf "$tmp_dir/$archive" -C "$tmp_dir"
🤖 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 `@scripts/install.sh` around lines 47 - 51, The install flow in install.sh
downloads and extracts the release archive without verifying integrity. Update
the download/install path around download, tar extraction, and install to
validate the artifact first, ideally by fetching a release checksums file and
checking the archive hash before installing doit into install_dir. Keep the
existing install logic but only proceed after verification succeeds, using the
same archive and install_dir variables to locate the affected code.
website/index.html (2)

93-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fallback "select" label doesn't actually select the command text.

When navigator.clipboard.writeText fails (e.g. insecure context or denied permission), the button text changes to "select" but no selection is performed, leaving the user without an easy way to copy the command manually.

♻️ Suggested fix: select the adjacent `
` text on fallback
           } catch {
             button.textContent = "select";
+            const codeEl = button.previousElementSibling?.querySelector("code") ?? button.previousElementSibling;
+            if (codeEl) {
+              const range = document.createRange();
+              range.selectNodeContents(codeEl);
+              window.getSelection()?.removeAllRanges();
+              window.getSelection()?.addRange(range);
+            }
             window.setTimeout(() => {
🤖 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 `@website/index.html` around lines 93 - 98, The fallback in the clipboard copy
handler only changes the button label to “select” but does not actually select
any text; update the catch path in the click handler around
`navigator.clipboard.writeText` so it selects the adjacent command text from the
related `<pre><code>` block. Use the existing button/label logic in that
handler, and implement a real text selection fallback so the user can manually
copy when clipboard access fails.

69-82: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Unauthenticated GitHub API call from every page load.

Each visitor's browser hits the unauthenticated GitHub REST API directly with no caching, which is subject to GitHub's low anonymous rate limit; heavy traffic behind shared NATs/proxies could see frequent failures (silently falling back to "latest on GitHub", so degradation is graceful).

🤖 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 `@website/index.html` around lines 69 - 82, The release lookup in the page load
script is making an unauthenticated GitHub API request on every visit, so update
the logic around the fetch/releaseLink handling to avoid direct anonymous calls
from each browser session. Use a cached or precomputed release URL/tag source,
or route the lookup through a backend/proxy layer with caching, and keep the
existing fallback behavior in the catch path.
server.go (1)

746-776: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Full client map scan on every Allow() call.

Every mutation request triggers a linear scan of up to maxRateLimitClients (4096) entries to prune expired windows, all inside the single global mutex. This serializes every mutation request behind an O(n) critical section, which will add latency and lock contention as the client map grows.

Consider pruning lazily (e.g., only sweep periodically via a background ticker, or amortize by evicting a bounded number of stale entries per call) rather than scanning the full map on every request.

🤖 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 `@server.go` around lines 746 - 776, The rate limiter’s Allow method is doing a
full l.clients scan on every call, which creates an O(n) critical section under
l.mu and increases contention. Update rateLimiter.Allow to avoid sweeping all
expired entries per request by pruning lazily, such as with a periodic
background cleanup or by evicting only a bounded number of stale clients per
invocation, while preserving the existing limit and window behavior.
🤖 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 `@server.go`:
- Around line 648-673: Keep the HTTP method checks in sync between isMutation
and requiresCSRF in server.go: requiresCSRF currently omits http.MethodPut while
isMutation includes it, so update requiresCSRF to treat PUT the same way as the
other mutating API methods. Use the existing isMutation/requiresCSRF switch
patterns as the source of truth so future API method support cannot bypass CSRF
protection.

In `@website/index.html`:
- Around line 84-100: The copy-button handler in the data-copy click listener
can capture “copied” as the restore label on rapid repeated clicks, leaving the
button stuck. Update the click logic so it preserves the true original label for
each button in a stable place (outside the async handler, keyed by the button or
via a dataset field) and reuse it on every restore. Also clear any pending
timeout before scheduling a new one in the same handler so only the latest click
controls the label reset.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 23-27: The CI workflow runs the full Go test suite twice in
separate steps, which is redundant. Consolidate the existing Test and Race test
steps in the workflow by using a single go test invocation with both verbose and
race flags, and update the job definition around the test commands so the suite
still runs once while preserving the same coverage.

In `@scripts/install.sh`:
- Around line 47-51: The install flow in install.sh downloads and extracts the
release archive without verifying integrity. Update the download/install path
around download, tar extraction, and install to validate the artifact first,
ideally by fetching a release checksums file and checking the archive hash
before installing doit into install_dir. Keep the existing install logic but
only proceed after verification succeeds, using the same archive and install_dir
variables to locate the affected code.

In `@server.go`:
- Around line 746-776: The rate limiter’s Allow method is doing a full l.clients
scan on every call, which creates an O(n) critical section under l.mu and
increases contention. Update rateLimiter.Allow to avoid sweeping all expired
entries per request by pruning lazily, such as with a periodic background
cleanup or by evicting only a bounded number of stale clients per invocation,
while preserving the existing limit and window behavior.

In `@store.go`:
- Around line 176-192: The title and notes validation in the patch update path
duplicates the existing `validateTaskText` logic and error messages. Refactor
the `patch` handling in `store.go` to call `validateTaskText` for `task.Title`
and `task.Notes`, reusing the current `task` values for any field not present in
`patch` so the same validation path is used consistently.
- Around line 383-396: The manual UTF-16 counting in tooLong is reimplementing
stdlib behavior; refactor tooLong to use unicode/utf16.RuneLen for each rune
instead of hand-rolling the surrogate-pair logic. Keep the max-unit check
semantics unchanged, and review any callers of tooLong to ensure the UTF-16 unit
limit is intentional and still matches client-side validation such as
static/app.js for titles/notes with emoji or other supplementary-plane
characters.

In `@website/index.html`:
- Around line 93-98: The fallback in the clipboard copy handler only changes the
button label to “select” but does not actually select any text; update the catch
path in the click handler around `navigator.clipboard.writeText` so it selects
the adjacent command text from the related `<pre><code>` block. Use the existing
button/label logic in that handler, and implement a real text selection fallback
so the user can manually copy when clipboard access fails.
- Around line 69-82: The release lookup in the page load script is making an
unauthenticated GitHub API request on every visit, so update the logic around
the fetch/releaseLink handling to avoid direct anonymous calls from each browser
session. Use a cached or precomputed release URL/tag source, or route the lookup
through a backend/proxy layer with caching, and keep the existing fallback
behavior in the catch path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 52783bc3-6451-42f8-8f4d-a8774024c3f6

📥 Commits

Reviewing files that changed from the base of the PR and between 6a51e0e65a6ce0052d181b6a3ea177c106c6ef51 and 7fb5a60fcba21e9c45b6435131c232606a929e3c.

⛔ Files ignored due to path filters (1)
  • website/assets/product-preview.svg is excluded by !**/*.svg
📒 Files selected for processing (18)
  • .github/workflows/ci.yml
  • .gitignore
  • .vercelignore
  • README.md
  • docs/ARCHITECTURE.md
  • go.mod
  • main.go
  • scripts/install.sh
  • server.go
  • server_test.go
  • static/app.css
  • static/app.js
  • static/index.html
  • store.go
  • store_test.go
  • vercel.json
  • website/index.html
  • website/styles.css

Comment thread server.go
Comment thread website/index.html Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
website/index.html (1)

82-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Copy status changes aren't announced to screen readers.

Button text flips to "copied"/"select" but there's no aria-live/role="status" region, so assistive tech users may not perceive the state change.

♻️ Suggested fix: add a visually-hidden live region
-        button.addEventListener("click", async () => {
+        const status = button.querySelector(".sr-status") ?? (() => {
+          const el = document.createElement("span");
+          el.className = "sr-status visually-hidden";
+          el.setAttribute("aria-live", "polite");
+          button.appendChild(el);
+          return el;
+        })();
+        button.addEventListener("click", async () => {
           clearTimeout(revertTimer);
           try {
             await navigator.clipboard.writeText(button.dataset.copy);
             button.textContent = "copied";
+            status.textContent = "Copied to clipboard";
             restoreLabel();

Also applies to: 92-92

🤖 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 `@website/index.html` at line 82, The copy button state changes in the inline
script are not announced to assistive technologies. Update the copy/select flow
around the button text updates in the relevant handlers so that state changes
are also sent to a visually hidden live region using an aria-live/role=status
element. Use the existing button update logic in the code that sets “copied” and
“select” to keep the live region in sync with those changes.
🤖 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.

Nitpick comments:
In `@website/index.html`:
- Line 82: The copy button state changes in the inline script are not announced
to assistive technologies. Update the copy/select flow around the button text
updates in the relevant handlers so that state changes are also sent to a
visually hidden live region using an aria-live/role=status element. Use the
existing button update logic in the code that sets “copied” and “select” to keep
the live region in sync with those changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ad17952f-d1b7-400e-a848-c8910d592315

📥 Commits

Reviewing files that changed from the base of the PR and between 7fb5a60 and 87e1181.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • .goreleaser.yaml
  • scripts/install.sh
  • server.go
  • server_test.go
  • store.go
  • website/index.html
🚧 Files skipped from review as they are similar to previous changes (3)
  • scripts/install.sh
  • store.go
  • server.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@scripts/install.sh`:
- Around line 88-92: The checksum download fallback in the install flow is too
permissive and skips integrity verification on fetch failures. Update the logic
around download_optional in the install script so the checksums.txt path fails
closed for any transport or fetch error, and only allows the legacy no-checksum
path when the response is a real 404. Use the existing verify_checksum and
download_optional flow to distinguish not-found from transient/network failures,
and abort installation when checksums.txt cannot be retrieved for any other
reason.
- Around line 47-60: The download_optional helper currently treats any curl/wget
failure as a missing checksum, so update its callers and error handling to
distinguish a real 404 from transport/TLS/DNS failures. In download_optional and
the checksum-fetch logic it feeds, inspect the fetch result so only a 404
triggers the “checksums unavailable” fallback; for any other nonzero exit, fail
closed and stop the install. Use the download_optional function and the checksum
download path in scripts/install.sh to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 08849835-1f9b-4921-a05a-346d766792ed

📥 Commits

Reviewing files that changed from the base of the PR and between 87e1181 and bd4a1dd.

📒 Files selected for processing (1)
  • scripts/install.sh

Comment thread scripts/install.sh
Comment on lines +47 to +60
download_optional() {
url="$1"
output="$2"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$url" -o "$output" >/dev/null 2>&1 && return 0
return 1
fi
if command -v wget >/dev/null 2>&1; then
wget -qO "$output" "$url" >/dev/null 2>&1 && return 0
return 1
fi
echo "curl or wget is required" >&2
exit 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file around the referenced lines.
if [ -f scripts/install.sh ]; then
  wc -l scripts/install.sh
  sed -n '1,180p' scripts/install.sh | cat -n
fi

# Find where download_optional is used.
rg -n "download_optional|download\(" scripts/install.sh

Repository: blackdragoon26/Do-It

Length of output: 3633


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether releases always publish checksums.txt or if it is genuinely optional.
rg -n "checksums\.txt|sha256|release" .github scripts README* .

Repository: blackdragoon26/Do-It

Length of output: 4034


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the release workflow and installer docs to see whether checksums.txt is expected.
sed -n '1,220p' .github/workflows/release.yml | cat -n
printf '\n---- README excerpt ----\n'
sed -n '45,90p' README.md | cat -n

Repository: blackdragoon26/Do-It

Length of output: 2869


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check for any GoReleaser config that could disable checksums or rename assets.
fd -a -H -t f '(.goreleaser.*|goreleaser.*)' .

Repository: blackdragoon26/Do-It

Length of output: 194


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect GoReleaser config to see whether checksums.txt is always emitted or can be missing.
wc -l .goreleaser.yaml
sed -n '1,260p' .goreleaser.yaml | cat -n

Repository: blackdragoon26/Do-It

Length of output: 1963


download_optional treats every fetch error as “checksums missing.” scripts/install.sh:47-52,88-91
A transient curl/wget failure now falls back to an unverified install; only skip verification for a real 404 and fail closed on transport/TLS/DNS errors.

🤖 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 `@scripts/install.sh` around lines 47 - 60, The download_optional helper
currently treats any curl/wget failure as a missing checksum, so update its
callers and error handling to distinguish a real 404 from transport/TLS/DNS
failures. In download_optional and the checksum-fetch logic it feeds, inspect
the fetch result so only a 404 triggers the “checksums unavailable” fallback;
for any other nonzero exit, fail closed and stop the install. Use the
download_optional function and the checksum download path in scripts/install.sh
to locate the change.

Comment thread scripts/install.sh
Comment on lines +88 to +92
if download_optional "$base_url/checksums.txt" "$tmp_dir/checksums.txt"; then
verify_checksum "$tmp_dir/$archive" "$tmp_dir/checksums.txt"
else
echo "checksums.txt not found for this release; installing without checksum verification" >&2
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== scripts/install.sh ==\n'
ast-grep outline scripts/install.sh --view expanded || true
printf '\n\n-- relevant lines --\n'
sed -n '1,140p' scripts/install.sh

printf '\n== release config candidates ==\n'
git ls-files | rg '(^|/)(\.goreleaser(\.ya?ml)?|goreleaser(\.ya?ml)?|release).*'

printf '\n== search for checksums.txt and DOIT_VERSION ==\n'
rg -n 'checksums\.txt|DOIT_VERSION|download_optional|verify_checksum' -S .

Repository: blackdragoon26/Do-It

Length of output: 3552


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== .goreleaser.yaml ==\n'
wc -l .goreleaser.yaml
sed -n '1,220p' .goreleaser.yaml

printf '\n== release workflow ==\n'
wc -l .github/workflows/release.yml
sed -n '1,220p' .github/workflows/release.yml

printf '\n== docs mention install script ==\n'
rg -n 'scripts/install\.sh|DOIT_VERSION|checksums\.txt|goreleaser' README.md .github . -g '!scripts/install.sh'

Repository: blackdragoon26/Do-It

Length of output: 3185


Fail closed when checksums.txt can’t be fetched.
.goreleaser.yaml always emits checksums.txt, so any fetch failure here should not silently skip integrity checking. Treat only a real 404 as the legacy fallback; transport errors should abort the install instead of proceeding unverified.

🤖 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 `@scripts/install.sh` around lines 88 - 92, The checksum download fallback in
the install flow is too permissive and skips integrity verification on fetch
failures. Update the logic around download_optional in the install script so the
checksums.txt path fails closed for any transport or fetch error, and only
allows the legacy no-checksum path when the response is a real 404. Use the
existing verify_checksum and download_optional flow to distinguish not-found
from transient/network failures, and abort installation when checksums.txt
cannot be retrieved for any other reason.

@blackdragoon26
blackdragoon26 merged commit d506d18 into main Jul 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants