Skip to content

Conversation

@VictoriousRaptor
Copy link
Contributor

Fixes #4184. A rework of #4185 as it's very tricky to solve all IPv6 related problems so I just leave it.

What's done:

@VictoriousRaptor VictoriousRaptor added this to the 2.1.0 milestone Dec 29, 2025
@VictoriousRaptor VictoriousRaptor self-assigned this Dec 29, 2025
@VictoriousRaptor VictoriousRaptor added the bug Something isn't working label Dec 29, 2025
@prlabeler prlabeler bot added Code Refactor enhancement New feature or request labels Dec 29, 2025
@gitstream-cm
Copy link

gitstream-cm bot commented Dec 29, 2025

🥷 Code experts: Jack251970

Jack251970 has most 👩‍💻 activity in the files.
Jack251970 has most 🧠 knowledge in the files.

See details

Flow.Launcher.Test/Plugins/UrlPluginTest.cs

Activity based on git-commit:

Jack251970
DEC
NOV
OCT
SEP
AUG
JUL

Knowledge based on git-blame:
Jack251970: 100%

Plugins/Flow.Launcher.Plugin.Url/Main.cs

Activity based on git-commit:

Jack251970
DEC
NOV
OCT 4 additions & 4 deletions
SEP 59 additions & 37 deletions
AUG
JUL 1 additions & 1 deletions

Knowledge based on git-blame:
Jack251970: 92%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

@gitstream-cm
Copy link

gitstream-cm bot commented Dec 29, 2025

Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Refactors URL plugin tests using data-driven approach and updates URL validation logic to support IP-based URLs (IPv6, IPv4, localhost) with expanded scheme recognition. Changes validation from inclusive to opt-in, returning empty results for non-URL inputs.

Changes

Cohort / File(s) Summary
Test Refactoring
Flow.Launcher.Test/Plugins/UrlPluginTest.cs
Converts single test method to parameterized [TestCase] approach. Adds OneTimeSetUp using reflection to initialize plugin instance. Introduces WhenValidUrlThenIsUrlReturnsTrue and WhenInvalidUrlThenIsUrlReturnsFalse test methods with multiple URL test cases.
URL Validation Logic
Plugins/Flow.Launcher.Plugin.Url/Main.cs
Expands URL regex pattern to support IPv6 (bracketed/unbracketed), IPv4, and hostname matching. Introduces UrlSchemes array (http, https, ftp) for scheme detection. Changes Query flow to opt-in: returns empty when input is not a URL. Updates port pattern to allow five digits. Removes localhost-specific edge-case handling from IsURL.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fixing matching issues in URL plugin' clearly summarizes the main change - addressing URL matching problems in the plugin.
Description check ✅ Passed The description is related to the changeset, explaining what was fixed (private address support, test/matching logic refactoring, IPv6 support) and references the related issues.
Linked Issues check ✅ Passed The PR successfully addresses issue #4184 by supporting IP URLs as clickable targets. Changes include IPv6/IPv4 support, private address inclusion, and refactored matching logic.
Out of Scope Changes check ✅ Passed All changes in the PR are directly related to the URL plugin matching objectives. Test refactoring and pattern updates support the goal of IP URL support.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch just-fix-url-small-issues

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.

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
Flow.Launcher.Test/Plugins/UrlPluginTest.cs (2)

10-19: Reflection-based setup is acceptable but consider null check.

The reflection approach to set the private Settings field is reasonable for testing. However, settingsField could theoretically be null if the field is renamed or refactored.

🔎 Consider adding a guard or assertion
 [OneTimeSetUp]
 public void OneTimeSetup()
 {
     var settingsField = typeof(Main).GetField("Settings", BindingFlags.NonPublic | BindingFlags.Static);
+    Assert.That(settingsField, Is.Not.Null, "Settings field not found - check if field name changed");
     settingsField?.SetValue(null, new Settings());

     plugin = new Main();
 }

57-71: Invalid URL test cases look reasonable; consider adding a few edge cases.

Current coverage is good. You may want to add tests for these edge cases if they're relevant:

  • "http://256.256.256.256" (invalid IPv4 with scheme)
  • "[::1" (unclosed bracket)
  • "http://[::1" (unclosed bracket with scheme)

These would help ensure the regex properly rejects malformed inputs.

Plugins/Flow.Launcher.Plugin.Url/Main.cs (1)

118-125: IsURL method could be simplified.

The method works correctly but has a minor redundancy.

🔎 Simplify the return statement
 public bool IsURL(string raw)
 {
     raw = raw.ToLower();
-
-    if (UrlRegex.Match(raw).Value == raw) return true;
-
-    return false;
+    return UrlRegex.Match(raw).Value == raw;
 }

Additionally, since the regex uses RegexOptions.IgnoreCase, the ToLower() call may be redundant. Consider removing it for minor performance improvement:

 public bool IsURL(string raw)
 {
-    raw = raw.ToLower();
-    return UrlRegex.Match(raw).Value == raw;
+    return UrlRegex.Match(raw).Value.Equals(raw, StringComparison.OrdinalIgnoreCase);
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8092a44 and a8e0d65.

📒 Files selected for processing (2)
  • Flow.Launcher.Test/Plugins/UrlPluginTest.cs
  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-09-04T11:52:29.096Z
Learnt from: jjw24
Repo: Flow-Launcher/Flow.Launcher PR: 3932
File: Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs:48-55
Timestamp: 2025-09-04T11:52:29.096Z
Learning: In Flow Launcher's PluginsManifest.cs, when dealing with version parsing for the MinimumAppVersion feature, maintainer jjw24 prefers to keep the solution simple rather than implementing comprehensive helper methods for SemVer parsing normalization.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
  • Flow.Launcher.Test/Plugins/UrlPluginTest.cs
📚 Learning: 2025-07-06T12:21:37.947Z
Learnt from: Jack251970
Repo: Flow-Launcher/Flow.Launcher PR: 3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
📚 Learning: 2025-10-16T10:48:30.573Z
Learnt from: Jack251970
Repo: Flow-Launcher/Flow.Launcher PR: 3854
File: Flow.Launcher/App.xaml.cs:252-275
Timestamp: 2025-10-16T10:48:30.573Z
Learning: In Flow Launcher's App.xaml.cs, the plugin initialization block (lines 252-275) that includes PreStartPluginExecutablePathUpdate, PluginManager.LoadPlugins, PluginManager.InitializePluginsAsync, _mainVM.QueryResults(), and API.SaveAppAllSettings() does not require additional Task.Run wrappers or Dispatcher.InvokeAsync calls according to maintainer Jack251970, as the threading model is already safe as designed.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
  • Flow.Launcher.Test/Plugins/UrlPluginTest.cs
📚 Learning: 2025-07-21T09:19:49.684Z
Learnt from: Jack251970
Repo: Flow-Launcher/Flow.Launcher PR: 3854
File: Flow.Launcher/App.xaml.cs:246-262
Timestamp: 2025-07-21T09:19:49.684Z
Learning: In Flow Launcher's App.xaml.cs, the asynchronous plugin initialization task (containing AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate, PluginManager.LoadPlugins, PluginManager.InitializePluginsAsync, and AutoPluginUpdates) does not require additional try-catch error handling according to maintainer Jack251970, as these operations are designed to handle exceptions internally.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
📚 Learning: 2025-07-21T09:19:19.012Z
Learnt from: Jack251970
Repo: Flow-Launcher/Flow.Launcher PR: 3854
File: Flow.Launcher.Core/Plugin/PluginManager.cs:280-292
Timestamp: 2025-07-21T09:19:19.012Z
Learning: In Flow Launcher's PluginManager.cs, the post-initialization operations (RegisterResultsUpdatedEvent, UpdatePluginMetadataTranslation, RegisterPluginActionKeywords, DialogJump.InitializeDialogJumpPlugin, and AddPluginToLists) are designed to be exception-safe and do not require additional try-catch error handling according to the maintainer Jack251970.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
📚 Learning: 2024-11-03T07:40:11.014Z
Learnt from: Yusyuriv
Repo: Flow-Launcher/Flow.Launcher PR: 3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
📚 Learning: 2025-09-05T11:56:27.267Z
Learnt from: Jack251970
Repo: Flow-Launcher/Flow.Launcher PR: 3593
File: Flow.Launcher/HotkeyControlDialog.xaml:6-6
Timestamp: 2025-09-05T11:56:27.267Z
Learning: In Flow.Launcher's migration to iNKORE.UI.WPF.Modern UI framework, dialog resource keys like PopuBGColor, PopupButtonAreaBGColor, PopupButtonAreaBorderColor, and PopupTextColor are provided by the iNKORE.UI.WPF.Modern framework itself, not defined locally in the codebase theme files.

Applied to files:

  • Plugins/Flow.Launcher.Plugin.Url/Main.cs
📚 Learning: 2025-07-01T05:46:13.251Z
Learnt from: Jack251970
Repo: Flow-Launcher/Flow.Launcher PR: 3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.

Applied to files:

  • Flow.Launcher.Test/Plugins/UrlPluginTest.cs
🧬 Code graph analysis (1)
Flow.Launcher.Test/Plugins/UrlPluginTest.cs (1)
Plugins/Flow.Launcher.Plugin.Url/Main.cs (2)
  • Main (10-148)
  • IsURL (118-125)
🪛 GitHub Check: Check Spelling
Flow.Launcher.Test/Plugins/UrlPluginTest.cs

[warning] 26-26:
google is not a recognized word. (unrecognized-spelling)


[warning] 25-25:
google is not a recognized word. (unrecognized-spelling)


[warning] 26-26:
google is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Agent
  • GitHub Check: CodeQL analysis (csharp)
  • GitHub Check: gitStream.cm
  • GitHub Check: gitStream.cm
  • GitHub Check: gitStream.cm
  • GitHub Check: build
🔇 Additional comments (6)
Flow.Launcher.Test/Plugins/UrlPluginTest.cs (1)

21-55: Good coverage of valid URL formats including IPv6 and case variations.

The parameterized test cases comprehensively cover:

  • Standard HTTP/HTTPS/FTP schemes
  • Bare domains and hostnames
  • localhost with and without ports
  • IPv4 addresses including private ranges (192.168.x.x)
  • IPv6 in both bracketed and unbracketed forms
  • Case-insensitive matching
Plugins/Flow.Launcher.Plugin.Url/Main.cs (5)

19-34: IPv6 regex patterns are comprehensive but complex.

The IPv6 patterns cover many common forms. The (?!:[0-9]) negative lookahead on line 34 correctly prevents matching unbracketed IPv6 followed by a port (since ports require brackets for IPv6).

One observation: the patterns don't cover all valid IPv6 compressed forms (e.g., 2001::db8:1 with :: in arbitrary positions), but this aligns with the PR description noting that some IPv6 edge cases are not handled.

Consider adding a comment noting which IPv6 forms are intentionally unsupported for future maintainers:

+            // Note: Not all IPv6 compressed forms are supported (see issue #4185)
             // IPv6 address with optional brackets (brackets required if followed by port)

36-37: IPv4 pattern excludes 0.0.0.0 as intended.

The pattern correctly requires the first octet to be [1-9] (not starting with 0), which excludes 0.0.0.0. The remaining octets allow [1-9]?\d permitting values 0-99, and the 25[0-5]|2[0-4]\d|1\d\d covers 100-255.

However, I notice a potential issue: the pattern [1-9]?\d for octets 2-4 would match values like 00 or 09 (single digit with optional leading digit). This seems intentional but may produce unexpected matches.


58-58: Good extraction of URL schemes into a reusable array.

Using a static readonly array for schemes improves maintainability and ensures consistency between the regex pattern and the scheme check in Query.


63-66: Early return for non-URLs is a good refactor.

Returning an empty list early when the input is not a URL simplifies the control flow and makes the intent clear. This is a cleaner approach than the previous structure.


78-82: Scheme check using LINQ is correct.

The Any check with StringComparison.OrdinalIgnoreCase properly handles case-insensitive scheme matching, consistent with the test cases like "HTTP://EXAMPLE.COM".

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes URL matching issues in the URL plugin by refactoring the regex pattern to support IPv6 addresses, removing exclusions for private IP addresses, and consolidating localhost handling into the main regex pattern.

Key Changes:

  • Added comprehensive IPv6 support with both bracketed and unbracketed formats
  • Removed private IP address exclusions (10.x.x.x, 192.168.x.x, 172.16-31.x.x) to allow matching private network addresses
  • Refactored tests from a single test method with assertions to parameterized test cases using TestCase attributes

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
Plugins/Flow.Launcher.Plugin.Url/Main.cs Replaced IPv4-only address matching with comprehensive IPv6 support, removed private network exclusions, added UrlSchemes array, adjusted port pattern to allow single digits, and refactored Query method control flow
Flow.Launcher.Test/Plugins/UrlPluginTest.cs Converted from ClassicAssert to modern NUnit Assert.That syntax, added OneTimeSetUp for test initialization, and expanded test coverage with parameterized test cases for IPv6 addresses and edge cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +16
var settingsField = typeof(Main).GetField("Settings", BindingFlags.NonPublic | BindingFlags.Static);
settingsField?.SetValue(null, new Settings());
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

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

The OneTimeSetUp uses reflection to set the static Settings field directly, which is a workaround because the Main.Init() method is not called. This approach bypasses the normal initialization flow and creates a testing pattern that doesn't reflect how the plugin actually works. Consider refactoring the Main class to make it more testable, perhaps by injecting dependencies or providing a test-friendly initialization method, rather than using reflection to manipulate internal state.

Copilot uses AI. Check for mistakes.
{
if (!raw.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !raw.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
// not a recognized scheme, add preferred http scheme
if (!UrlSchemes.Any(scheme => raw.StartsWith(scheme, StringComparison.OrdinalIgnoreCase)))
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

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

The code uses UrlSchemes.Any(scheme => raw.StartsWith(scheme, StringComparison.OrdinalIgnoreCase)) which iterates through the array and performs string comparisons for each scheme. Given that this array only contains 3 elements, the performance impact is minimal. However, for consistency with the original approach and slightly better performance, you could use explicit OR conditions with StartsWith for each scheme, avoiding the LINQ overhead entirely.

Suggested change
if (!UrlSchemes.Any(scheme => raw.StartsWith(scheme, StringComparison.OrdinalIgnoreCase)))
if (!(raw.StartsWith(UrlSchemes[0], StringComparison.OrdinalIgnoreCase)
|| raw.StartsWith(UrlSchemes[1], StringComparison.OrdinalIgnoreCase)
|| raw.StartsWith(UrlSchemes[2], StringComparison.OrdinalIgnoreCase)))

Copilot uses AI. Check for mistakes.
")" +
// port number
"(?::\\d{2,5})?" +
"(?::\\d{1,5})?" +
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

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

The port pattern :\d{1,5} allows ports from 1 to 99999, but valid port numbers range from 1 to 65535. This will incorrectly match invalid ports like ":99999" or ":70000". While the original code had the same issue with high ports, changing from \d{2,5} to \d{1,5} now also allows single-digit ports (which are valid but uncommon). Consider using a more precise regex pattern that validates the actual port range (1-65535), such as :(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}).

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +34
"(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|" + // standard IPv6
"(?:[0-9a-fA-F]{1,4}:){1,7}:|" + // IPv6 with trailing ::
"(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" + // IPv6 compressed
"::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}|" + // IPv6 with leading ::
"(?:(?:[0-9a-fA-F]{1,4}:){1,6}|:):(?:[0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}|" + // IPv6 with :: in the middle
"::1)(?!:[0-9])" + // IPv6 loopback (not followed by port)
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

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

The negative lookahead (?!:[0-9]) only applies to the "::1" pattern on line 34, not to the other unbracketed IPv6 patterns (lines 29-33). This means addresses like "2001:db8::1" could be followed by ":8080" and the pattern would try to interpret ":8080" as part of the IPv6 address rather than as a port number, leading to ambiguous parsing. RFC 3986 requires brackets around IPv6 addresses in URLs specifically to avoid this ambiguity. The negative lookahead should apply to all unbracketed IPv6 alternatives, or unbracketed IPv6 support should be reconsidered given the inherent ambiguity.

Copilot uses AI. Check for mistakes.
@Jack251970 Jack251970 requested a review from jjw24 December 29, 2025 15:49
Comment on lines +21 to +27
"(?:\\[(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\]|" + // standard IPv6
"\\[(?:[0-9a-fA-F]{1,4}:){1,7}:\\]|" + // IPv6 with trailing ::
"\\[(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}\\]|" + // IPv6 compressed
"\\[::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}\\]|" + // IPv6 with leading ::
"\\[(?:(?:[0-9a-fA-F]{1,4}:){1,6}|:):(?:[0-9a-fA-F]{1,4}:){0,5}[0-9a-fA-F]{1,4}\\]|" + // IPv6 with :: in the middle
"\\[::1\\])" + // IPv6 loopback
"|" +
Copy link
Member

Choose a reason for hiding this comment

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

@VictoriousRaptor instead of a massive regex string, would Uri.TryCreate work for our scenarios? (Also IPAddress.TryParse for IPs)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@VictoriousRaptor instead of a massive regex string, would Uri.TryCreate work for our scenarios? (Also IPAddress.TryParse for IPs)

I did try but they made it more complicated. Uri.TryCreate can't deal with strings without a xxx:// scheme and IPAddress.TryParse can't parse strings with a scheme. I didnt figure out how to mix them, but we need both of them parsed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Code Refactor enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

support open ip url

3 participants