Skip to content

chore: close OSS-readiness Medium + Nice-to-have findings (M1–M13, N1–N13) - #1

Merged
KiwiGaze merged 16 commits into
mainfrom
chore/oss-readiness-cleanup
Jun 28, 2026
Merged

chore: close OSS-readiness Medium + Nice-to-have findings (M1–M13, N1–N13)#1
KiwiGaze merged 16 commits into
mainfrom
chore/oss-readiness-cleanup

Conversation

@KiwiGaze

@KiwiGaze KiwiGaze commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the open Medium (M1–M13) and Nice-to-have (N1–N13) findings from the 2026-06-26 OSS-readiness audit. No new runtime dependencies; changes are surgical and the project's zero-dependency property is preserved. (High-severity Homebrew items H4/H5 are intentionally out of scope.)

Plan: ~/agent-docs/codex-status-bar/plans/2026-06-26-oss-readiness-mediums-nice-to-haves.md.

What changed

App / hooks (Swift + JS)

  • M4 / N7 — installer now surfaces an NSAlert when the bundled install.js fails, and prepends /opt/homebrew/bin:/usr/local/bin to broaden node resolution; removed the dead ownerExited(_:) and the write-only installedVersion default.
  • N13 — VoiceOver accessibility label on the status item.
  • M6install.js now quotes the interpolated node and script paths (safe under a $HOME with spaces).
  • N8 — shell-free execFileSync for pgrep (lifecycle) and pkill (uninstall).
  • M3 — bundle id renamed com.local.codexstatusbario.github.kiwigaze.codexstatusbar (build.sh + lifecycle.js).

Build

  • N3 / N4build.sh gains a VERSION var with an interpolated Info.plist heredoc, and guards an empty TEAM_ID so it can no longer sign with an unrelated Developer ID.

Tests / CI

  • M7 — new Tests/install.test.mjs covering the hook-merge logic (idempotency, only-our-hooks removal, one-time .bak-statusbar).
  • M8 / N6.github/workflows/ci.yml (macOS build + Swift logic tests + Node tests + node --check) and a .swift-format editor-assist config. No Swift lint gate: the hand-tuned compact style cannot pass swift-format without a disruptive reformat (verified), so the gate would be noise.

Docs / metadata

  • M1SECURITY.md (private reporting + an accurate account of every shell-out the hooks and the app make).
  • M2 — README Privacy section (exact fields read/written, no network egress).
  • M5 / N1 / N12.codex-plugin/plugin.json version 0.2.2 + homepage/repository filled; author set to Yijiazhen Qi in LICENSE, NOTICE, and the manifest.
  • N2 — not-affiliated disclaimer added to NOTICE, which already ships into the app + DMG, so it travels with the artifact.
  • M9 / N10 — CHANGELOG [0.2.0] entry + Keep-a-Changelog reference.
  • M10 / N11 — README Development section; Releases and License links; fixed a stale state.json reference.
  • M11 / M12 / M13 / N9CONTRIBUTING.md, CODE_OF_CONDUCT.md (Contributor Covenant 2.1, GitHub-profile contact), issue + PR templates.
  • N5RELEASING.md.

Repo-settings actions (done outside this PR)

  • M1 — enable Private Vulnerability Reporting.
  • N12 — set the repository homepage URL.

Verification

Local CI gate is green: node --test Tests/install.test.mjs Tests/update.test.mjs (all pass), Swift logic tests (ALL OK), node --check on all hooks, and SKIP_NOTARIZE=1 ./build.sh produces a universal (x86_64 arm64) binary.

Review

Branch was reviewed (code-correctness + docs-accuracy passes); findings were addressed — most notably the CI Node version was set to 24 so the using/Symbol.dispose test syntax parses, and SECURITY.md now discloses the app's own /bin/zsh -lc / osascript / pgrep calls.

Summary by CodeRabbit

  • New Features
    • Added GitHub issue templates and an improved pull request checklist.
    • Introduced a CI pipeline that builds and runs checks on macOS.
  • Bug Fixes
    • Fixed session “Done” visibility timing for expired confirmations.
    • Improved first-launch hook installation safety and failure guidance.
    • Tightened local filesystem permissions and made hook/state updates atomic to reduce corruption risk.
  • Documentation
    • Expanded README/privacy details, plus security, contributing, and releasing guidance.
  • Tests
    • Extended coverage for install/uninstall, atomic writes, installer launch behavior, and restrictive permission modes.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR updates plugin metadata, hardens hook installation and filesystem writes, changes session visibility and logging behavior, and adds CI, tests, and repository documentation/policy files.

Changes

Release, runtime, and repository updates

Layer / File(s) Summary
Versioned bundle metadata
.codex-plugin/plugin.json, build.sh
The build script and plugin manifest now use version 0.2.3, updated bundle identifiers, and named author metadata.
Hook execution and private files
Sources/AppSupport.swift, hooks/fs-utils.js, hooks/install.js, hooks/lifecycle.js, hooks/uninstall.js, Sources/main.swift
Installer launch, hook script setup, private filesystem helpers, and shell/Process command changes are added across the app and hook scripts.
Session display and log writes
Sources/SessionState.swift, Sources/main.swift, hooks/update.js
Session visibility, selection, timeout logging, accessibility labels, and session-state persistence are updated for per-session timing and private file writes.
Validation and repository documentation
.github/workflows/ci.yml, .swift-format, Tests/*, CHANGELOG.md, README.md, RELEASING.md, SECURITY.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md, .github/ISSUE_TEMPLATE/*, .github/pull_request_template.md, LICENSE, NOTICE
The CI workflow, tests, changelog, release notes, contributor guidance, security policy, issue templates, license text, notice text, and formatting config are added or updated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through hooks and version snow,
Private files now softly glow.
The CI runs, the sessions dance,
And release notes get their proper chance.
Ears up, carrots down — all set to go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: closing OSS-readiness audit findings, with the scope called out succinctly.
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 chore/oss-readiness-cleanup

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
CHANGELOG.md

markdownlint-cli2 wrapper config was not available before execution


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

@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 @.github/workflows/ci.yml:
- Around line 10-13: Update the CI workflow to use immutable version pins for
the GitHub Actions steps instead of mutable tags, and disable persisted
credentials on the `actions/checkout` step. Adjust the `actions/checkout` and
`actions/setup-node` entries in the workflow so they are locked to fixed
revisions, and set the checkout credentials option off to prevent the repo token
from being written into git config for later steps.

In `@Tests/install.test.mjs`:
- Around line 88-94: The install/uninstall test is not asserting that the
install step succeeded before verifying uninstall behavior. Update the test
around the INSTALL/UNINSTALL calls in the install test to capture and assert the
install result from run(INSTALL, h.home) before proceeding, so failures in
install.js do not get masked by a no-op uninstall. Use the existing run,
INSTALL, UNINSTALL, and readHooks helpers to keep the flow the same while
explicitly checking the install status first.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8680dda7-709b-4329-8936-680178435329

📥 Commits

Reviewing files that changed from the base of the PR and between e48aa0b and eec91ef.

📒 Files selected for processing (20)
  • .codex-plugin/plugin.json
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/ISSUE_TEMPLATE/feature_request.md
  • .github/pull_request_template.md
  • .github/workflows/ci.yml
  • .swift-format
  • CHANGELOG.md
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • LICENSE
  • NOTICE
  • README.md
  • RELEASING.md
  • SECURITY.md
  • Sources/main.swift
  • Tests/install.test.mjs
  • build.sh
  • hooks/install.js
  • hooks/lifecycle.js
  • hooks/uninstall.js

Comment thread .github/workflows/ci.yml
Comment on lines +10 to +13
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "24"

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

Pin the actions and turn off persisted checkout credentials.

Lines 10-13 currently use mutable action tags, and actions/checkout keeps the workflow token in git config for later steps by default. That weakens the workflow’s supply-chain posture and already trips the repo’s static checks.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 10-10: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 10-10: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 11-11: 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/ci.yml around lines 10 - 13, Update the CI workflow to use
immutable version pins for the GitHub Actions steps instead of mutable tags, and
disable persisted credentials on the `actions/checkout` step. Adjust the
`actions/checkout` and `actions/setup-node` entries in the workflow so they are
locked to fixed revisions, and set the checkout credentials option off to
prevent the repo token from being written into git config for later steps.

Source: Linters/SAST tools

Comment thread Tests/install.test.mjs
Comment on lines +88 to +94
run(INSTALL, h.home);
assert.equal(run(UNINSTALL, h.home).status, 0);
const hooks = readHooks(h.home);
assert.equal(ourCommands(hooks).length, 0, "no status-bar hooks remain");
assert.ok(!("SessionStart" in hooks.hooks), "emptied SessionStart event removed");
assert.ok(hooks.hooks.Stop.flatMap((e) => e.hooks).some((h) => h.command === "echo other"),
"unrelated Stop hook preserved through uninstall");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the install step before checking uninstall behavior.

Line 88 ignores the installer result, so this test can still pass when install.js fails and uninstall.js removes nothing. That hides regressions in the install→uninstall path this case is meant to cover.

Proposed fix
-  run(INSTALL, h.home);
+  assert.equal(run(INSTALL, h.home).status, 0);
+  assert.ok(readHooks(h.home).hooks.SessionStart?.length, "install added SessionStart hook");
   assert.equal(run(UNINSTALL, h.home).status, 0);
📝 Committable suggestion

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

Suggested change
run(INSTALL, h.home);
assert.equal(run(UNINSTALL, h.home).status, 0);
const hooks = readHooks(h.home);
assert.equal(ourCommands(hooks).length, 0, "no status-bar hooks remain");
assert.ok(!("SessionStart" in hooks.hooks), "emptied SessionStart event removed");
assert.ok(hooks.hooks.Stop.flatMap((e) => e.hooks).some((h) => h.command === "echo other"),
"unrelated Stop hook preserved through uninstall");
assert.equal(run(INSTALL, h.home).status, 0);
assert.ok(readHooks(h.home).hooks.SessionStart?.length, "install added SessionStart hook");
assert.equal(run(UNINSTALL, h.home).status, 0);
const hooks = readHooks(h.home);
assert.equal(ourCommands(hooks).length, 0, "no status-bar hooks remain");
assert.ok(!("SessionStart" in hooks.hooks), "emptied SessionStart event removed");
assert.ok(hooks.hooks.Stop.flatMap((e) => e.hooks).some((h) => h.command === "echo other"),
"unrelated Stop hook preserved through uninstall");
🤖 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 `@Tests/install.test.mjs` around lines 88 - 94, The install/uninstall test is
not asserting that the install step succeeded before verifying uninstall
behavior. Update the test around the INSTALL/UNINSTALL calls in the install test
to capture and assert the install result from run(INSTALL, h.home) before
proceeding, so failures in install.js do not get masked by a no-op uninstall.
Use the existing run, INSTALL, UNINSTALL, and readHooks helpers to keep the flow
the same while explicitly checking the install status first.

- main.swift: collapse the duplicated installer-failure handling into one
  path; simplify the accessibility label
- tests: extract seedHooks helper and OTHER_HOOKS fixture
- build.sh: derive VERSION from plugin.json (single source) and use a
  default-then-guard for SIGN_ID
- RELEASING.md: bump version only in plugin.json

@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: 1

🤖 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 `@build.sh`:
- Line 8: The VERSION assignment in build.sh can still produce an empty value
when parsing .codex-plugin/plugin.json fails, so add an explicit non-empty
validation immediately after the VERSION line and before any Info.plist
generation. If VERSION is missing or blank, fail the script with a clear error
instead of continuing; keep the check near the existing VERSION parsing logic so
the build stops before writing empty CFBundleVersion fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd80654e-de48-4349-aec7-b3facf2d3c7e

📥 Commits

Reviewing files that changed from the base of the PR and between eec91ef and 1b1c341.

📒 Files selected for processing (4)
  • RELEASING.md
  • Sources/main.swift
  • Tests/install.test.mjs
  • build.sh
✅ Files skipped from review due to trivial changes (2)
  • RELEASING.md
  • Sources/main.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Tests/install.test.mjs

Comment thread build.sh

APP="build/Codex Status Bar.app"
BIN="$APP/Contents/MacOS/CodexStatusBar"
VERSION="${VERSION:-$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' .codex-plugin/plugin.json | head -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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fail fast if manifest version parsing returns empty.

Line 8 can resolve VERSION to an empty string when .codex-plugin/plugin.json is missing, malformed, or reformatted in a way this sed expression no longer matches, and the script would still emit blank CFBundleVersion fields below. Add an explicit non-empty check before writing Info.plist.

Suggested fix
 VERSION="${VERSION:-$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' .codex-plugin/plugin.json | head -1)}"
+if [[ -z "$VERSION" ]]; then
+  echo "Failed to determine VERSION from .codex-plugin/plugin.json. Set VERSION=x.y.z explicitly." >&2
+  exit 1
+fi
📝 Committable suggestion

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

Suggested change
VERSION="${VERSION:-$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' .codex-plugin/plugin.json | head -1)}"
VERSION="${VERSION:-$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' .codex-plugin/plugin.json | head -1)}"
if [[ -z "$VERSION" ]]; then
echo "Failed to determine VERSION from .codex-plugin/plugin.json. Set VERSION=x.y.z explicitly." >&2
exit 1
fi
🤖 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 `@build.sh` at line 8, The VERSION assignment in build.sh can still produce an
empty value when parsing .codex-plugin/plugin.json fails, so add an explicit
non-empty validation immediately after the VERSION line and before any
Info.plist generation. If VERSION is missing or blank, fail the script with a
clear error instead of continuing; keep the check near the existing VERSION
parsing logic so the build stops before writing empty CFBundleVersion fields.

@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: 3

🤖 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 `@hooks/install.js`:
- Line 24: The hook install fingerprint is missing the runtime helper
fs-utils.js, so changes to that file won’t invalidate the installed hook copy.
Update the fingerprinting logic in Sources/main.swift to include fs-utils.js
alongside the other hook resources, and keep the install step in
hooks/install.js aligned with that dependency so any helper change forces a
refresh of the copied hooks.

In `@hooks/update.js`:
- Line 134: The session scanner is picking up the temporary file created by
writeFileAtomic in update.js, which can cause a duplicate session load. Update
Sources/main.swift so the scan for states.d ignores atomic temp files such as
*.tmp (or otherwise excludes writeFileAtomic’s temp naming), keeping only the
real state JSON files in the session list.

In `@Sources/AppSupport.swift`:
- Around line 28-32: The append path in AppSupport.swift updates permissions
after writing, so an existing permissive log can receive new data before being
locked down. In the log-writing flow around the FileHandle(forWritingAtPath:)
branch, set the file’s permissions to 0o600 before seekToEndOfFile/write when
app.log already exists, and keep the existing permission-setting logic for newly
created files as well. Use the existing handle/fm logic in this helper to ensure
old logs are chmod’d before any append occurs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e244cd9-257b-404a-bc42-c04fa0545958

📥 Commits

Reviewing files that changed from the base of the PR and between 1b1c341 and 1c37e5b.

📒 Files selected for processing (15)
  • .codex-plugin/plugin.json
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • Sources/AppSupport.swift
  • Sources/SessionState.swift
  • Sources/main.swift
  • Tests/install.test.mjs
  • Tests/logic_tests.swift
  • Tests/update.test.mjs
  • build.sh
  • hooks/fs-utils.js
  • hooks/install.js
  • hooks/lifecycle.js
  • hooks/uninstall.js
  • hooks/update.js
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/ci.yml
  • hooks/uninstall.js
  • build.sh

Comment thread hooks/install.js
ensurePrivateDir(sbDir);
copyPrivateFile(path.join(__dirname, "update.js"), updateDest);
copyPrivateFile(path.join(__dirname, "lifecycle.js"), lifecycleDest);
copyPrivateFile(path.join(__dirname, "fs-utils.js"), fsUtilsDest);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Include fs-utils.js in the hook install fingerprint.

Line 24 installs a runtime dependency used by copied hooks, but Sources/main.swift fingerprints only the other hook resources. A fs-utils.js-only change can leave users running the stale copied helper.

Suggested fix
-        let names = ["install.js", "update.js", "lifecycle.js", "uninstall.js"]
+        let names = ["install.js", "update.js", "lifecycle.js", "uninstall.js", "fs-utils.js"]
🤖 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 `@hooks/install.js` at line 24, The hook install fingerprint is missing the
runtime helper fs-utils.js, so changes to that file won’t invalidate the
installed hook copy. Update the fingerprinting logic in Sources/main.swift to
include fs-utils.js alongside the other hook resources, and keep the install
step in hooks/install.js aligned with that dependency so any helper change
forces a refresh of the copied hooks.

Comment thread hooks/update.js
fs.renameSync(tmp, statePath);
ensurePrivateDir(dir);
ensurePrivateDir(statesDir);
writeFileAtomic(statePath, JSON.stringify(out));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep atomic temp files out of the session scanner.

writeFileAtomic creates a complete *.tmp next to statePath; Sources/main.swift currently scans every file in states.d, so it can briefly load the temp JSON as a duplicate session.

Suggested fix
         if let names = try? fm.contentsOfDirectory(atPath: statesDir) {
             for name in names {
+                if name.hasSuffix(".tmp") { continue }
                 let p = (statesDir as NSString).appendingPathComponent(name)
🤖 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 `@hooks/update.js` at line 134, The session scanner is picking up the temporary
file created by writeFileAtomic in update.js, which can cause a duplicate
session load. Update Sources/main.swift so the scan for states.d ignores atomic
temp files such as *.tmp (or otherwise excludes writeFileAtomic’s temp naming),
keeping only the real state JSON files in the session list.

Comment thread Sources/AppSupport.swift
Comment on lines +28 to +32
if let handle = FileHandle(forWritingAtPath: path) {
handle.seekToEndOfFile()
handle.write(data)
handle.closeFile()
try? fm.setAttributes([.posixPermissions: 0o600], ofItemAtPath: path)

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

Chmod existing logs before appending.

If app.log already exists with permissive permissions from an older version, Line 30 writes the new timeout line before Line 32 restricts the file.

Suggested fix
+    if fm.fileExists(atPath: path) {
+        try? fm.setAttributes([.posixPermissions: 0o600], ofItemAtPath: path)
+    }
     if let handle = FileHandle(forWritingAtPath: path) {
         handle.seekToEndOfFile()
         handle.write(data)
         handle.closeFile()
📝 Committable suggestion

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

Suggested change
if let handle = FileHandle(forWritingAtPath: path) {
handle.seekToEndOfFile()
handle.write(data)
handle.closeFile()
try? fm.setAttributes([.posixPermissions: 0o600], ofItemAtPath: path)
if fm.fileExists(atPath: path) {
try? fm.setAttributes([.posixPermissions: 0o600], ofItemAtPath: path)
}
if let handle = FileHandle(forWritingAtPath: path) {
handle.seekToEndOfFile()
handle.write(data)
handle.closeFile()
try? fm.setAttributes([.posixPermissions: 0o600], ofItemAtPath: path)
🤖 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 `@Sources/AppSupport.swift` around lines 28 - 32, The append path in
AppSupport.swift updates permissions after writing, so an existing permissive
log can receive new data before being locked down. In the log-writing flow
around the FileHandle(forWritingAtPath:) branch, set the file’s permissions to
0o600 before seekToEndOfFile/write when app.log already exists, and keep the
existing permission-setting logic for newly created files as well. Use the
existing handle/fm logic in this helper to ensure old logs are chmod’d before
any append occurs.

@KiwiGaze
KiwiGaze merged commit 1c37e5b into main Jun 28, 2026
2 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.

1 participant