Skip to content

Support IP/CIDR rules conversion from SwitchyOmega#541

Closed
salarcode wants to merge 3 commits into
masterfrom
fixes/Support-SwitchyOmega-IP-CIDR
Closed

Support IP/CIDR rules conversion from SwitchyOmega#541
salarcode wants to merge 3 commits into
masterfrom
fixes/Support-SwitchyOmega-IP-CIDR

Conversation

@salarcode
Copy link
Copy Markdown
Owner

Summary

This change fixes SwitchyOmega Ip: rule imports so they are converted into SmartProxy internal regex host rules correctly.

Previously, SwitchyOmega IP conditions were not making it through the importer because the importer still depended on the old SwitchyOmega-style IP address classes, which are not available in this code path. As a result, IpCondition rules could not be compiled into the internal regex representation that SmartProxy expects.

This PR removes that dependency from the importer path and reuses the existing CIDR-to-regex utilities already implemented in the project.

What changed

  • Updated the SwitchyOmega importer so IpCondition compiles directly into internal RegexHost rules.
  • Replaced the importer’s reliance on the missing IP.v4.Address / IP.v6.Address path with the existing Utils.ipCidrNotationToRegExp logic.
  • Kept the conversion aligned with the existing SmartProxy rule pipeline instead of introducing a separate IP-matching implementation.
  • Fixed an IPv6 normalization issue where exact IPv6 literals like 2001:db8::1 could be misinterpreted as an address with a port and lose their final segment during normalization.
  • Added regression coverage for both IPv4 and IPv6 SwitchyOmega Ip: rules, including compressed and expanded IPv6 forms.

Why this approach

The project already has a CIDR-to-regex implementation and already treats internal IP/CIDR rules as regex host rules. Reusing that logic keeps behavior consistent across manual rules, imported rules, and runtime matching, while avoiding an extra dependency and avoiding duplicate subnet logic.

Behavior before

SwitchyOmega rules like the following were not imported correctly:

[SwitchyOmega Conditions]
Ip: 10.0.0.0/8
Ip: 172.16.0.0/12
Ip: 192.168.0.0/16
Ip: 2001:0db8:0000:0000:0000:0000:0000:0001/128
Ip: 2001:db8::1/128

Behavior after

These rules are now parsed and compiled into SmartProxy internal RegexHost rules and can participate in the normal subscription/import matching flow.

Test coverage

Added or updated tests to verify:

  • SwitchyOmega Ip: rules are converted into internal regex host rules
  • IPv4 CIDR import cases such as /8, /12, and /16
  • IPv6 exact match cases in both expanded and compressed notation
  • IPv6 normalization correctness for exact addresses and bracketed host forms

Validation

Verified with:

npm test -- --runTestsByPath src/tests/RuleImporter.test.ts src/tests/Utils.ipCidr.test.ts
npm run build-ch

Copy link
Copy Markdown

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 updates SwitchyOmega rule import handling so Ip: conditions are converted into SmartProxy regex host rules using existing CIDR utilities, with regression coverage for IPv4/IPv6 cases.

Changes:

  • Reworks SwitchyOmega IpCondition parsing/compilation to use Utils.ipCidrNotationToRegExp.
  • Adds IPv6 normalization regression coverage for exact and bracketed host forms.
  • Fixes a Chrome PAC path to use the matched subscription rule when forcing proxy selection.

Reviewed changes

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

Show a summary per file
File Description
src/lib/RuleImporterSwitchy.js Converts SwitchyOmega IP conditions to regex-backed host rules.
src/lib/Utils.ts Adjusts IPv6 host normalization for bracketed hosts with ports.
src/tests/RuleImporter.test.ts Adds SwitchyOmega Ip: import regression tests.
src/tests/Utils.ipCidr.test.ts Adds IPv6 normalization regression test coverage.
src/core/ProxyEngineChrome.ts Corrects subscription matched-rule usage in AlwaysEnabled bypass handling.

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

Comment thread src/lib/RuleImporterSwitchy.js
Comment thread src/lib/RuleImporterSwitchy.js
Copy link
Copy Markdown

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

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Comment thread src/lib/RuleImporter.ts
Comment on lines 988 to +990
if (type == 'host') {
newRule.importedRuleType = CompiledProxyRuleType.RegexHost;
newRule.normalizeIpHostMatch = compiled.normalizeIpHostMatch == true;
@@ -2437,15 +2437,6 @@ export class settingsPage {
username: username,
password: password,
backupFilename: backupFilename,
Comment on lines +473 to +483
if (ipv6.indexOf('.') >= 0)
return null;

const parts = ipv6.split('::');
if (parts.length > 2)
return null;

const left = parts[0] ? parts[0].split(':').filter(Boolean) : [];
const right = parts[1] ? parts[1].split(':').filter(Boolean) : [];
const missing = 8 - (left.length + right.length);
if (missing < 0)
Comment on lines +884 to +888
regex = Utils.ipCidrNotationToRegExp(cache.addr.addressMinusSuffix, cache.addr.subnetMask.toString());
if (regex == null) {
throw new Error("Invalid IP address " + addr);
}
cache.regex = regex;
Comment on lines +429 to 442
prefixMatch = address.match(/\/(\d+)$/);
if (prefixMatch) {
subnetMask = parseInt(prefixMatch[1], 10);
address = address.substring(0, address.length - prefixMatch[0].length);
}
addressIsBracketed = address.charCodeAt(0) === '['.charCodeAt(0);
if (addressIsBracketed) {
if (!/^\[[^\]]+\]$/.test(address)) {
return null;
}
address = address.substring(1, address.length - 1);
} else if (address.indexOf('[') >= 0 || address.indexOf(']') >= 0) {
return null;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If an imported ip is 'bracketed', like [172.16.0.0/12], the prefixMatch regex //(\d+)$/ will return null because the string ends with a bracket ], not a digit.

As a result, the code skips stripping the subnet suffix here, later strips the brackets, and eventually passes "172.16.0.0/12" with a wrong default subnetMask to Utils.ipCidrNotationToRegExp.

To fix this, the bracket-stripping logic should execute before the prefixMatch slicing.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

yeah thanks I got this PR is kinda messed up, i'll try another one

@salarcode salarcode closed this May 19, 2026
@salarcode salarcode deleted the fixes/Support-SwitchyOmega-IP-CIDR branch May 19, 2026 01:52
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.

3 participants