Skip to content

Conversation

@AnnatarHe
Copy link
Contributor

Summary

  • Add CLI version check when extension starts
  • Call server API to check if current daemon version is latest
  • Show warning notification if update available with update command
  • Read apiEndpoint and webEndpoint from config file

Test plan

  • Configure apiEndpoint and webEndpoint in ~/.shelltime/config.yaml
  • Start Neovim with daemon running
  • Verify version check API is called (check with debug=true)
  • Test with outdated version to see warning notification
  • Verify update command is copied to clipboard

🤖 Generated with Claude Code

Check daemon CLI version against server API when extension starts.
If a newer version is available, show warning notification with
update command: curl -sSL {webEndpoint}/i | bash

- Add version.lua module with async curl HTTP request
- Read apiEndpoint and webEndpoint from config file
- Show warning only once per session
- Auto-copy update command to clipboard

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@gemini-code-assist
Copy link

Summary of Changes

Hello @AnnatarHe, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates an essential feature for maintaining an up-to-date CLI environment. It introduces an automatic version check that runs when the extension starts, querying a remote server to determine if the currently installed daemon is the latest. If an update is available, the user receives a clear notification with instructions and an update command, which is conveniently copied to their clipboard, streamlining the update process and ensuring users are always running the most current and secure version of the CLI.

Highlights

  • Automatic CLI Version Check: Introduced automatic CLI version checking upon extension startup to ensure users are running the latest daemon.
  • Server API Integration: Implemented a mechanism to query a remote server API for the latest available daemon version.
  • Update Notifications: Added a warning notification for outdated CLI versions, which includes a convenient update command that is also copied to the clipboard.
  • Configurable Endpoints: Configured the extension to read apiEndpoint and webEndpoint from the user's configuration file, allowing for flexible endpoint management.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@claude
Copy link

claude bot commented Jan 7, 2026

Pull Request Review: feat(version): add CLI version check on extension startup

Summary

This PR adds version checking functionality that calls a server API on startup to verify if the daemon version is up-to-date, and displays a notification if an update is available. Overall, the implementation is solid, but there are several areas that need attention.

Code Quality & Best Practices

✅ Positives

  • Clean separation of concerns with a dedicated version.lua module
  • Follows project conventions (snake_case, 2-space indentation, single quotes)
  • Non-blocking async implementation using vim.fn.jobstart
  • Session-based warning tracking to avoid notification spam
  • Proper error handling with pcall() and debug logging

⚠️ Areas for Improvement

1. Missing Test Coverage (Critical)

The PR adds 154 lines of new code in version.lua but includes no tests. According to the project's testing conventions, this module should have:

  • Unit tests for url_encode() function
  • Tests for parse_json() with valid/invalid JSON
  • Tests for check_version() with mocked vim.fn.jobstart
  • Tests for show_update_warning() notification behavior
  • Tests for session warning tracking (has_shown_warning)

Recommendation: Add tests/version_spec.lua following the existing test patterns in tests/config_spec.lua and tests/sender_spec.lua.

2. Incomplete URL Encoding (Bug Risk)

The url_encode() function has issues:

  • Missing nil check return: if str is nil, the function returns nil instead of empty string
  • The order of operations is wrong: spaces should be encoded before the general pattern, or use %20 instead of +
  • Consider edge cases: what if version contains + or % characters?

3. Fragile JSON Parsing (Bug Risk)

The manual JSON parsing using pattern matching:

  • Doesn't handle escaped quotes in version strings
  • Fails silently on malformed JSON
  • Pattern matching is fragile and error-prone
  • Why not use vim.json.decode() which is already used elsewhere in the codebase (see socket.lua:94)?

Recommendation: Use vim.json.decode() with pcall() instead of manual pattern matching.

4. Missing Error Handling in sender.lua (Moderate)

In sender.lua:72-76, there's no error handling if version.check_version() throws an error. Should use pcall() to prevent crashes.

Performance Considerations

✅ Good

  • Async HTTP request using jobstart doesn't block the main thread
  • 5-second timeout prevents hanging requests (-m 5)
  • Session-based tracking avoids redundant API calls
  • Version check only runs once on startup

⚠️ Potential Issues

  1. Startup Latency: The version check runs on every Neovim startup. Consider:

    • Caching the result with a TTL (e.g., 24 hours)
    • Making it optional via config flag
    • Moving it to background after a delay to avoid slowing down startup
  2. Network Dependency: Startup now depends on external API availability. The 5-second timeout is good, but failures should be truly silent.

Security Concerns

🔴 Critical Issues

1. Command Injection Risk (High)

In version.lua:136:

local update_command = 'curl -sSL ' .. web_endpoint .. '/i | bash'

Problem: If web_endpoint is user-controlled via config file, this could be exploited:

  • Malicious config: webEndpoint: "http://evil.com/malware;rm -rf /"
  • Result: Dangerous command copied to clipboard and suggested to user

Mitigation Required:

  1. Validate web_endpoint format (must be valid HTTPS URL)
  2. Escape shell special characters
  3. Consider using a safer update mechanism
  4. Add a warning about the security implications

2. Unvalidated HTTP Response

  • No validation that the API response is from the expected server
  • No HTTPS enforcement for api_endpoint
  • Consider adding certificate validation to curl command or at minimum require HTTPS

Test Coverage

Missing Tests

This PR adds 0 tests for the new functionality. Required test coverage:

  1. tests/version_spec.lua - Unit tests for all functions
  2. Tests for config loading of apiEndpoint and webEndpoint
  3. Integration test for version check flow in sender.lua

Additional Recommendations

  1. Documentation: Update README or add inline docs explaining:

    • How to configure apiEndpoint and webEndpoint
    • Privacy implications of version checking
    • How to disable version checking
  2. Config Schema: Add validation for endpoint URLs in config.lua

  3. Graceful Degradation: Ensure the plugin works perfectly even if:

    • Endpoints are not configured
    • Network is unavailable
    • API returns unexpected responses

Summary

Category Rating Notes
Code Quality ⚠️ Good Follows conventions but needs refinement
Test Coverage ❌ Poor No tests added
Performance ✅ Good Async, non-blocking implementation
Security 🔴 Critical Command injection risk needs addressing
Documentation ⚠️ Minimal Needs user-facing docs

Verdict

Changes Requested - This PR needs work before merging:

Must Fix (Blocking)

  1. ❌ Add comprehensive test coverage
  2. 🔴 Fix command injection security vulnerability
  3. ⚠️ Replace manual JSON parsing with vim.json.decode()
  4. ⚠️ Fix URL encoding function

Should Fix (Recommended)

  1. Add URL validation for endpoints
  2. Add user documentation
  3. Consider making version check optional via config
  4. Add error handling in sender.lua

Great work on the feature implementation! The architecture is sound, but the security and testing gaps need to be addressed before this can be merged safely.

🤖 Generated with Claude Code

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a CLI version check on startup, which is a great feature for keeping users up-to-date. The implementation is well-structured in a new version.lua module and uses an asynchronous approach for the check, which is good for performance.

However, I have identified a few issues that should be addressed:

  • A critical security concern with the suggested update command, which executes a script directly from the internet.
  • The use of a fragile custom JSON parser instead of the robust built-in vim.json.decode.
  • A bug in the URL encoding logic.

Additionally, the new functionality in version.lua is not covered by automated tests. It would be beneficial to add tests for this new module to ensure its correctness and prevent future regressions.

Please see my detailed comments below.

---@param latest_version string Latest available version
---@param web_endpoint string Web endpoint for update command
function M.show_update_warning(current_version, latest_version, web_endpoint)
local update_command = 'curl -sSL ' .. web_endpoint .. '/i | bash'

Choose a reason for hiding this comment

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

security-critical critical

Piping the output of curl directly into bash is a significant security risk. This pattern can lead to remote code execution if the download URL is compromised, or if the user is on a malicious network (e.g., via DNS spoofing). The script is executed without any opportunity for the user to inspect it. While this is a common installation method, it's dangerous to promote within an editor plugin, especially by copying the command to the clipboard which encourages blind execution. Consider providing just the URL to the user and advising them to inspect the script before running it.

Comment on lines 30 to 44
local function parse_json(json_str)
-- Simple JSON parser for { "isLatest": bool, "latestVersion": "...", "version": "..." }
local is_latest = json_str:match('"isLatest"%s*:%s*(true)')
local latest_version = json_str:match('"latestVersion"%s*:%s*"([^"]+)"')
local version = json_str:match('"version"%s*:%s*"([^"]+)"')

if latest_version and version then
return {
isLatest = is_latest ~= nil,
latestVersion = latest_version,
version = version,
}
end
return nil
end

Choose a reason for hiding this comment

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

high

The custom JSON parser implemented here is very fragile as it relies on string matching with regular expressions. This can easily break if the API response format changes in ways not anticipated by the regex (e.g., different value types, number formats). Neovim provides a robust built-in JSON decoder, vim.json.decode, which is already used in other parts of this project (e.g., socket.lua). It's highly recommended to use vim.json.decode for parsing the response to improve robustness and maintainability.

local function parse_json(json_str)
  local ok, result = pcall(vim.json.decode, json_str)
  if ok and type(result) == 'table' and result.isLatest ~= nil and result.latestVersion and result.version then
    return result
  end
  return nil
end

Comment on lines +74 to +76
if status and status.version then
version.check_version(status.version)
end

Choose a reason for hiding this comment

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

medium

The error returned from socket.get_status is not handled. If the call fails, the version check is silently skipped. While this prevents a crash, it would be beneficial to log the error when in debug mode to aid in troubleshooting connection issues. This would make the behavior consistent with other error handling in the codebase.

        if status and status.version then
          version.check_version(status.version)
        elseif err and config.get('debug') then
          vim.notify('[shelltime] Failed to get status for version check: ' .. err, vim.log.levels.DEBUG)
        end

---@return string Encoded string
local function url_encode(str)
if str then
str = string.gsub(str, '\n', '\r\n')

Choose a reason for hiding this comment

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

medium

The handling of newline characters (\n) is incorrect for URL encoding. Newlines should be percent-encoded as %0A, not converted to \r\n which is used for HTTP headers. While version strings are unlikely to contain newlines, this is still a bug in the implementation that could cause issues.

    str = string.gsub(str, '\n', '%%0A')

Replace manual regex-based JSON parser with Neovim's built-in
vim.json.decode for more robust handling of edge cases.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 8, 2026

Code Review: CLI Version Check Feature

Overall Assessment

This PR adds a valuable feature for notifying users about CLI updates. The implementation is generally solid, but there are several important issues that should be addressed before merging.

Critical Issues

1. Missing Test Coverage

The PR adds 146 lines of new code in version.lua but includes no tests. Based on the project testing conventions (all other modules have corresponding *_spec.lua files), you should add tests/version_spec.lua.

Recommended test coverage:

  • url_encode() function with special characters, spaces, and edge cases
  • parse_json() with valid/invalid JSON
  • check_version() with mocked curl responses
  • show_update_warning() notification behavior
  • Session warning flag behavior (has_shown_warning)

2. Security: Command Injection in Update Command

Location: lua/shelltime/version.lua:128

The update command is constructed by concatenating user-configurable input (web_endpoint from config). If a malicious config contains a crafted webEndpoint value, it could lead to command injection.

Recommended fix: Validate the web_endpoint format before using it in the shell command.

3. Error Handling: Silent Failures

Location: lua/shelltime/version.lua:74-120

The curl job does not handle several failure modes:

  • Network timeouts (5s might be too short for slow connections)
  • HTTP error status codes (404, 500, etc.)
  • Malformed JSON responses
  • Empty responses

4. Race Condition in sender.lua

Location: lua/shelltime/sender.lua:73

The version check callback could fire after the plugin is stopped. Add state tracking to prevent this race condition.

Code Quality Issues

5. Inconsistent Error Handling Pattern

The codebase uses pcall() for error handling (per CLAUDE.md), but version.lua uses if-checks instead. Consider wrapping risky operations consistently.

6. URL Encoding Implementation

Location: lua/shelltime/version.lua:16-24

The url_encode() function has issues:

  • Does not properly handle UTF-8 characters
  • The newline handling is unusual for URL encoding
  • Missing test coverage for edge cases

7. Magic Numbers

The curl timeout is hardcoded as 5 seconds. This should be a configurable constant at the module level.

8. Blocking Notification

The clipboard operation vim.fn.setreg could block if the clipboard is unavailable. Consider wrapping in pcall().

Positive Aspects

  1. Good separation of concerns - version checking is isolated in its own module
  2. Respects debug flag - provides helpful debug logging
  3. Session-aware warnings - has_shown_warning prevents spam
  4. Non-blocking design - uses async job API
  5. User-friendly - copies update command to clipboard
  6. Follows commit conventions - proper conventional commit format

Recommendations Summary

Must fix before merge:

  1. Add comprehensive test coverage (tests/version_spec.lua)
  2. Fix command injection vulnerability in update command construction
  3. Add URL validation for web_endpoint
  4. Handle race condition in sender.lua startup

Should fix:
5. Improve error handling for network failures and HTTP errors
6. Add configurable timeout constant
7. Wrap clipboard operation in pcall()

Nice to have:
8. Document the apiEndpoint/webEndpoint config in README or inline comments
9. Consider adding integration test with mock server
10. Add examples to test plan in PR description

Great work on this feature! The core functionality is solid. Address the security and testing concerns, and this will be ready to merge.

Add tests for check_version and show_update_warning functions covering:
- Configuration handling (missing endpoints, already shown warning)
- API call construction (URL encoding, headers, timeout)
- Success responses (version latest vs update available)
- Error handling (curl failures, invalid JSON, empty responses)
- Session state (warning shown once per session)
- Clipboard functionality

Also add _set_for_testing helper to config module for test isolation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Jan 8, 2026

Code Review - PR #12: Version Check Feature

Summary

This PR adds CLI version checking functionality that runs on extension startup. The implementation is well-structured with comprehensive test coverage (584 test lines). Overall, the code follows the project conventions and is production-ready with minor improvements suggested below.

Strengths

  1. Excellent Test Coverage: The test suite is thorough, covering configuration handling, API call construction, success/error scenarios, session state management, and edge cases
  2. Non-blocking Implementation: Version check runs asynchronously without blocking Neovim startup (lua/shelltime/sender.lua:72-78)
  3. Follows Project Conventions: 2-space indentation, snake_case, single quotes, LuaDoc annotations, pcall() for error handling
  4. Good Error Handling: Gracefully handles missing config, network failures, and parse errors

Code Quality Issues

1. Security: Command Injection Risk (High Priority)

Location: lua/shelltime/version.lua:129

The web_endpoint is user-configurable and directly concatenated into a shell command without validation. A malicious config could inject arbitrary commands.

Recommendations: Add URL validation to ensure web_endpoint is a valid HTTPS URL, sanitize command construction, add validation in config.lua

2. Security: Arbitrary URL in curl (Medium Priority)

Location: lua/shelltime/version.lua:74

The api_endpoint is not validated. An attacker could set it to file:///etc/passwd or other protocols.

Recommendation: Validate that both api_endpoint and web_endpoint start with https:// when loaded

3. Logic Issue: Session State

Location: lua/shelltime/version.lua:8

The has_shown_warning flag persists across module reloads. Users won't see warnings after updating daemon until Neovim restarts.

Best Practices & Improvements

  1. Missing Input Validation (lua/shelltime/version.lua:41): No validation that daemon_version is a non-empty string
  2. Hardcoded Timeout (lua/shelltime/version.lua:74): Consider making 5-second timeout configurable
  3. Debug Logging: Include URL in debug output for better diagnostics

Test Coverage

Strengths: 584 lines of tests for 146 lines of code (4:1 ratio) - excellent!

Missing: Version string edge cases, concurrent calls, jobstart failure handling

Security Summary

  • Critical: Command injection risk (line 129)
  • Medium: Missing URL protocol validation
  • Low: No daemon_version input validation

Conclusion

Solid implementation with excellent test coverage. Main concerns are security-related around URL validation and command construction.

Recommended Before Merge:

  1. Add HTTPS validation for api_endpoint and web_endpoint
  2. Sanitize shell command construction
  3. Add input validation for daemon_version

Great work overall! The async implementation and test coverage are particularly commendable.

@AnnatarHe AnnatarHe merged commit 4cd3920 into main Jan 8, 2026
3 checks passed
@AnnatarHe AnnatarHe deleted the feat/cli-version-check branch January 8, 2026 17:30
@codecov
Copy link

codecov bot commented Jan 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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