fix(mobile): detect and repair overriding Windows Firewall Block rules for pairing (#8846)

* fix(mobile): detect and repair overriding Windows Firewall Block rules for pairing

Firewall inspection now reports an overriding inbound Block rule as
blocked instead of false success, the UAC repair removes only
conflicting rules for the current Orca executable, TCP pairing port,
and Private profile before recreating the scoped allow rule, and the
notice re-inspects Windows policy after repair instead of optimistically
reporting success. Stale focus-triggered inspections can no longer
overwrite a newer result during UAC elevation.

Fixes #8371

* fix(mobile): inspect the ActiveStore so GPO firewall rules are visible

Without -PolicyStore ActiveStore the NetSecurity queries read only the
local persistent store, so a GPO-applied Block rule was invisible and
the post-repair re-inspection could report a false success on managed
hosts.
This commit is contained in:
OrcaWin 2026-07-15 01:40:41 -07:00 committed by GitHub
parent c13441123a
commit 9e2c63ec7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 271 additions and 50 deletions

View File

@ -25,6 +25,7 @@ describe('windows mobile firewall', () => {
const runPowerShell = vi.fn().mockResolvedValue(
JSON.stringify({
matchingRuleScopes: [{ remoteAddresses: ['192.168.0.0/24'] }],
blockingRuleDetected: false,
localAddress: '192.168.0.108',
localPrefixLength: 24,
privateFirewallEnabled: true,
@ -38,22 +39,55 @@ describe('windows mobile firewall', () => {
supported: true,
port: 6768,
ruleAllowed: true,
blockingRuleDetected: false,
privateFirewallEnabled: true,
networkCategory: 'private',
inspectionAvailable: true
})
const script = runPowerShell.mock.calls[0]![0] as string
// Why: without ActiveStore, GPO-applied Block rules are invisible and the
// post-repair re-inspection could report a false success on managed hosts.
expect(script).toContain(
"Get-NetFirewallApplicationFilter -PolicyStore ActiveStore -Program 'C:\\Users\\O''Brien\\Orca\\Orca.exe'"
)
expect(script).toContain('Get-NetFirewallProfile -PolicyStore ActiveStore -Name Private')
expect(script).toContain("LocalPort | Where-Object { [string]$_ -eq 'Any'")
expect(script).toContain("[string]$_ -eq '6768'")
expect(script).toContain("C:\\Users\\O''Brien\\Orca\\Orca.exe")
expect(script).toContain("$profile -match 'Private'")
expect(script).toContain("Get-NetIPAddress -IPAddress '192.168.0.108'")
expect(script).toContain('Get-NetFirewallAddressFilter')
expect(script).toContain("[string]$rule.Action -eq 'Block'")
expect(script).toContain('remoteAddresses = @($addressFilter.RemoteAddress')
expect(script).toContain('$localPrefixLength = [int]$ip.PrefixLength')
})
it('treats an overlapping inbound Block rule as overriding a matching Allow rule', async () => {
const runPowerShell = vi.fn().mockResolvedValue(
JSON.stringify({
matchingRuleScopes: [{ remoteAddresses: ['Any'] }],
blockingRuleDetected: true,
localAddress: '192.168.0.108',
localPrefixLength: 24,
privateFirewallEnabled: true,
networkCategory: 'Private'
})
)
await expect(
inspectWindowsMobileFirewall(6768, '192.168.0.108', environment(runPowerShell))
).resolves.toEqual({
supported: true,
port: 6768,
ruleAllowed: false,
blockingRuleDetected: true,
privateFirewallEnabled: true,
networkCategory: 'private',
inspectionAvailable: true
})
})
it('does not accept a qualifying rule whose remote-address scope excludes the phone subnet', async () => {
const runPowerShell = vi.fn().mockResolvedValue(
JSON.stringify({
@ -100,6 +134,7 @@ describe('windows mobile firewall', () => {
supported: true,
port: 6768,
ruleAllowed: false,
blockingRuleDetected: false,
privateFirewallEnabled: true,
networkCategory: 'unknown',
inspectionAvailable: false
@ -133,6 +168,10 @@ describe('windows mobile firewall', () => {
expect(encoded).toBeTruthy()
const repairScript = Buffer.from(encoded!, 'base64').toString('utf16le')
expect(repairScript).toContain("-Name 'Orca.MobilePairing'")
expect(repairScript).toContain(
"Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' -and $_.Action -eq 'Block' }"
)
expect(repairScript).toContain('$rule | Remove-NetFirewallRule')
expect(repairScript).toContain('-Profile Private')
expect(repairScript).toContain('-Protocol TCP')
expect(repairScript).toContain('-LocalPort 6769')

View File

@ -24,6 +24,7 @@ export type WindowsMobileFirewallEnvironment = {
type FirewallInspection = {
matchingRuleScopes?: unknown
blockingRuleDetected?: unknown
localAddress?: unknown
localPrefixLength?: unknown
privateFirewallEnabled: boolean
@ -51,14 +52,20 @@ export async function inspectWindowsMobileFirewall(
POWERSHELL_TIMEOUT_MS
)
const result = JSON.parse(stdout.trim()) as FirewallInspection
// Why: the phone address is unknown before pairing, so any matching Block
// rule must fail this advisory check closed instead of risking false success.
const blockingRuleDetected = result.blockingRuleDetected === true
return {
supported: true,
port,
ruleAllowed: hasSufficientWindowsFirewallRemoteScope(
result.matchingRuleScopes,
result.localAddress,
result.localPrefixLength
),
ruleAllowed:
!blockingRuleDetected &&
hasSufficientWindowsFirewallRemoteScope(
result.matchingRuleScopes,
result.localAddress,
result.localPrefixLength
),
blockingRuleDetected,
privateFirewallEnabled: result.privateFirewallEnabled !== false,
networkCategory: parseNetworkCategory(result.networkCategory),
inspectionAvailable: true
@ -131,6 +138,7 @@ function unavailableStatus(port: number): WindowsMobileFirewallStatus {
supported: true,
port,
ruleAllowed: false,
blockingRuleDetected: false,
privateFirewallEnabled: true,
networkCategory: 'unknown',
inspectionAvailable: false
@ -166,26 +174,34 @@ try {
} catch {}`
: ''
// Why: NetSecurity filter properties are stable across localized Windows
// display output and keep every rule's address scope independent.
// display output and keep every rule's address scope independent. ActiveStore
// includes GPO-applied rules the default persistent store hides, so managed
// Block rules cannot produce a false success after repair.
return `$ErrorActionPreference = 'Stop'
$matchingRuleScopes = @()
$rules = @(Get-NetFirewallApplicationFilter -Program ${quotePowerShell(executablePath)} -ErrorAction SilentlyContinue | Get-NetFirewallRule | Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' -and $_.Action -eq 'Allow' })
$blockingRuleDetected = $false
$rules = @(Get-NetFirewallApplicationFilter -PolicyStore ActiveStore -Program ${quotePowerShell(executablePath)} -ErrorAction SilentlyContinue | Get-NetFirewallRule | Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' })
foreach ($rule in $rules) {
$portFilter = $rule | Get-NetFirewallPortFilter
$protocol = [string]$portFilter.Protocol
$profile = [string]$rule.Profile
$portMatches = @($portFilter.LocalPort | Where-Object { [string]$_ -eq 'Any' -or [string]$_ -eq '${port}' }).Count -gt 0
if (($protocol -eq 'Any' -or $protocol -eq 'TCP' -or $protocol -eq '6') -and ($profile -eq 'Any' -or $profile -match 'Private') -and $portMatches) {
$addressFilter = $rule | Get-NetFirewallAddressFilter
$matchingRuleScopes += [pscustomobject]@{
remoteAddresses = @($addressFilter.RemoteAddress | ForEach-Object { [string]$_ })
if ([string]$rule.Action -eq 'Block') {
$blockingRuleDetected = $true
} elseif ([string]$rule.Action -eq 'Allow') {
$addressFilter = $rule | Get-NetFirewallAddressFilter
$matchingRuleScopes += [pscustomobject]@{
remoteAddresses = @($addressFilter.RemoteAddress | ForEach-Object { [string]$_ })
}
}
}
}
$privateFirewallEnabled = [bool](Get-NetFirewallProfile -Name Private).Enabled
$privateFirewallEnabled = [bool](Get-NetFirewallProfile -PolicyStore ActiveStore -Name Private).Enabled
$networkCategory = 'Unknown'${addressLookup}
[pscustomobject]@{
matchingRuleScopes = @($matchingRuleScopes)
blockingRuleDetected = $blockingRuleDetected
localAddress = $localAddress
localPrefixLength = $localPrefixLength
privateFirewallEnabled = $privateFirewallEnabled
@ -194,7 +210,21 @@ $networkCategory = 'Unknown'${addressLookup}
}
function buildRepairScript(port: number, executablePath: string): string {
// Why: Windows gives explicit Block rules precedence over narrower Allow
// rules, so the user's repair action must remove exact-app conflicts first.
// Removal deliberately ignores the Block rule's remote-address scope,
// mirroring the fail-closed inspection (the phone address is unknown).
return `$ErrorActionPreference = 'Stop'
$blockingRules = @(Get-NetFirewallApplicationFilter -Program ${quotePowerShell(executablePath)} -ErrorAction SilentlyContinue | Get-NetFirewallRule | Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' -and $_.Action -eq 'Block' })
foreach ($rule in $blockingRules) {
$portFilter = $rule | Get-NetFirewallPortFilter
$protocol = [string]$portFilter.Protocol
$profile = [string]$rule.Profile
$portMatches = @($portFilter.LocalPort | Where-Object { [string]$_ -eq 'Any' -or [string]$_ -eq '${port}' }).Count -gt 0
if (($protocol -eq 'Any' -or $protocol -eq 'TCP' -or $protocol -eq '6') -and ($profile -eq 'Any' -or $profile -match 'Private') -and $portMatches) {
$rule | Remove-NetFirewallRule
}
}
Get-NetFirewallRule -Name ${quotePowerShell(FIREWALL_RULE_NAME)} -ErrorAction SilentlyContinue | Remove-NetFirewallRule
New-NetFirewallRule -Name ${quotePowerShell(FIREWALL_RULE_NAME)} -DisplayName ${quotePowerShell(FIREWALL_RULE_DISPLAY_NAME)} -Description 'Allows Orca Mobile to connect to this Orca desktop on private networks.' -Direction Inbound -Action Allow -Enabled True -Profile Private -Protocol TCP -LocalPort ${port} -Program ${quotePowerShell(executablePath)} -EdgeTraversalPolicy Block | Out-Null`
}

View File

@ -3180,6 +3180,7 @@ export type PreloadApi = {
supported: true
port: number
ruleAllowed: boolean
blockingRuleDetected: boolean
privateFirewallEnabled: boolean
networkCategory: 'private' | 'public' | 'domain' | 'unknown'
inspectionAvailable: boolean

View File

@ -2,7 +2,7 @@
import '@testing-library/jest-dom/vitest'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen, waitFor } from '@testing-library/react'
import { act, cleanup, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { WindowsFirewallNotice } from './WindowsFirewallNotice'
@ -28,6 +28,7 @@ describe('WindowsFirewallNotice', () => {
supported: true,
port: 6768,
ruleAllowed: false,
blockingRuleDetected: false,
privateFirewallEnabled: true,
networkCategory: 'private',
inspectionAvailable: true
@ -45,15 +46,28 @@ describe('WindowsFirewallNotice', () => {
it('repairs only after explicit user action and hides after success', async () => {
const repairWindowsFirewall = vi.fn().mockResolvedValue({ ok: true })
setMobileApi({
getWindowsFirewallStatus: vi.fn().mockResolvedValue({
const getWindowsFirewallStatus = vi
.fn()
.mockResolvedValueOnce({
supported: true,
port: 6768,
ruleAllowed: false,
blockingRuleDetected: false,
privateFirewallEnabled: true,
networkCategory: 'private',
inspectionAvailable: true
}),
})
.mockResolvedValueOnce({
supported: true,
port: 6768,
ruleAllowed: true,
blockingRuleDetected: false,
privateFirewallEnabled: true,
networkCategory: 'private',
inspectionAvailable: true
})
setMobileApi({
getWindowsFirewallStatus,
repairWindowsFirewall
})
const user = userEvent.setup()
@ -61,11 +75,77 @@ describe('WindowsFirewallNotice', () => {
await user.click(await screen.findByRole('button', { name: /allow phone connections/i }))
expect(repairWindowsFirewall).toHaveBeenCalledTimes(1)
await waitFor(() => expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(2))
await waitFor(() =>
expect(screen.queryByText(/allow phone connections through/i)).not.toBeInTheDocument()
)
})
it('reports an overriding Block rule and stays actionable if repair cannot clear it', async () => {
const blockedStatus = {
supported: true,
port: 6768,
ruleAllowed: false,
blockingRuleDetected: true,
privateFirewallEnabled: true,
networkCategory: 'private',
inspectionAvailable: true
}
const getWindowsFirewallStatus = vi.fn().mockResolvedValue(blockedStatus)
const repairWindowsFirewall = vi.fn().mockResolvedValue({ ok: true })
setMobileApi({ getWindowsFirewallStatus, repairWindowsFirewall })
const user = userEvent.setup()
render(<WindowsFirewallNotice pairingReady address="192.168.0.108" />)
expect(await screen.findByText(/Windows may be blocking Orca Mobile/i)).toBeInTheDocument()
expect(screen.getByText(/Block rule can override/i)).toBeInTheDocument()
await user.click(screen.getByRole('button', { name: /repair firewall access/i }))
await waitFor(() => expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(2))
expect(screen.getByText(/Windows may be blocking Orca Mobile/i)).toBeInTheDocument()
})
it('ignores a stale inspection that resolves after a newer result', async () => {
const blockedStatus = {
supported: true,
port: 6768,
ruleAllowed: false,
blockingRuleDetected: true,
privateFirewallEnabled: true,
networkCategory: 'private',
inspectionAvailable: true
}
const clearedStatus = { ...blockedStatus, ruleAllowed: true, blockingRuleDetected: false }
let resolveStale: (status: typeof blockedStatus) => void = () => {}
const getWindowsFirewallStatus = vi
.fn()
.mockResolvedValueOnce(blockedStatus)
.mockImplementationOnce(
() =>
new Promise((resolve) => {
resolveStale = resolve
})
)
.mockResolvedValueOnce(clearedStatus)
setMobileApi({ getWindowsFirewallStatus })
render(<WindowsFirewallNotice pairingReady address="192.168.0.108" />)
expect(await screen.findByText(/Windows may be blocking Orca Mobile/i)).toBeInTheDocument()
// Why: UAC elevation bounces window focus, so an older in-flight
// inspection can resolve after a newer one and must not win.
window.dispatchEvent(new Event('focus'))
window.dispatchEvent(new Event('focus'))
await waitFor(() => expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(3))
await waitFor(() =>
expect(screen.queryByText(/Windows may be blocking Orca Mobile/i)).not.toBeInTheDocument()
)
await act(async () => {
resolveStale(blockedStatus)
})
expect(screen.queryByText(/Windows may be blocking Orca Mobile/i)).not.toBeInTheDocument()
})
it('does not offer a firewall rule while the selected network is public', async () => {
const openWindowsNetworkSettings = vi.fn().mockResolvedValue(true)
setMobileApi({
@ -73,6 +153,7 @@ describe('WindowsFirewallNotice', () => {
supported: true,
port: 6768,
ruleAllowed: true,
blockingRuleDetected: false,
privateFirewallEnabled: false,
networkCategory: 'public',
inspectionAvailable: true
@ -95,6 +176,7 @@ describe('WindowsFirewallNotice', () => {
supported: true,
port: 6768,
ruleAllowed: false,
blockingRuleDetected: false,
privateFirewallEnabled: true,
networkCategory: 'domain',
inspectionAvailable: true

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { CircleAlert, Loader2, ShieldCheck } from 'lucide-react'
import { toast } from 'sonner'
import type { WindowsMobileFirewallStatus } from '../../../../shared/windows-mobile-firewall'
@ -21,23 +21,29 @@ export function WindowsFirewallNotice({
const [status, setStatus] = useState<WindowsMobileFirewallStatus | null>(null)
const [repairing, setRepairing] = useState(false)
const mountedRef = useMountedRef()
const inspectIdRef = useRef(0)
const inspect = useCallback(async () => {
const inspect = useCallback(async (): Promise<WindowsMobileFirewallStatus | null> => {
// Why: UAC elevation steals and returns window focus, so a focus-triggered
// inspection can race the post-repair one; only the latest result may win.
const inspectId = ++inspectIdRef.current
if (!pairingReady) {
setStatus(null)
return
return null
}
try {
const next = await window.api.mobile.getWindowsFirewallStatus(
address ? { address } : undefined
)
if (mountedRef.current) {
if (mountedRef.current && inspectIdRef.current === inspectId) {
setStatus(next)
}
return next
} catch {
if (mountedRef.current) {
if (mountedRef.current && inspectIdRef.current === inspectId) {
setStatus(null)
}
return null
}
}, [address, mountedRef, pairingReady])
@ -52,11 +58,16 @@ export function WindowsFirewallNotice({
}
const firewallStatus = status
const networkIsPublic = firewallStatus.networkCategory === 'public'
const blockingRuleDetected = firewallStatus.blockingRuleDetected
// Why: a Private-profile allow rule cannot help on managed domain networks.
if (!pairingReady || firewallStatus.networkCategory === 'domain') {
return null
}
if (!networkIsPublic && (firewallStatus.ruleAllowed || !firewallStatus.privateFirewallEnabled)) {
if (
!networkIsPublic &&
(!firewallStatus.privateFirewallEnabled ||
(firewallStatus.ruleAllowed && !blockingRuleDetected))
) {
return null
}
@ -68,11 +79,32 @@ export function WindowsFirewallNotice({
return
}
if (result.ok) {
setStatus({ ...firewallStatus, ruleAllowed: true })
toast.success(
// Why: elevation success only confirms the script ran; managed policy
// can still leave an overriding Block rule in effect.
const next = await inspect()
if (!mountedRef.current) {
return
}
if (
next?.supported &&
(!next.privateFirewallEnabled ||
(next.ruleAllowed && !next.blockingRuleDetected && next.inspectionAvailable))
) {
toast.success(
translate(
'auto.components.mobile.WindowsFirewallNotice.repair-success',
'Windows Firewall now allows Orca Mobile on private networks'
)
)
return
}
if (!next) {
setStatus(firewallStatus)
}
toast.error(
translate(
'auto.components.mobile.WindowsFirewallNotice.repair-success',
'Windows Firewall now allows Orca Mobile on private networks'
'auto.components.mobile.WindowsFirewallNotice.repair-unverified',
'Windows Firewall access could not be verified'
)
)
return
@ -81,7 +113,7 @@ export function WindowsFirewallNotice({
toast.error(
translate(
'auto.components.mobile.WindowsFirewallNotice.repair-failed',
'Could not add the Windows Firewall rule'
'Could not update the Windows Firewall rules'
)
)
}
@ -90,7 +122,7 @@ export function WindowsFirewallNotice({
toast.error(
translate(
'auto.components.mobile.WindowsFirewallNotice.repair-failed',
'Could not add the Windows Firewall rule'
'Could not update the Windows Firewall rules'
)
)
}
@ -113,10 +145,15 @@ export function WindowsFirewallNotice({
'auto.components.mobile.WindowsFirewallNotice.public-title',
'Windows marks this network as public'
)
: translate(
'auto.components.mobile.WindowsFirewallNotice.missing-title',
'Allow phone connections through Windows Firewall'
)}
: blockingRuleDetected
? translate(
'auto.components.mobile.WindowsFirewallNotice.blocked-title',
'Windows may be blocking Orca Mobile'
)
: translate(
'auto.components.mobile.WindowsFirewallNotice.missing-title',
'Allow phone connections through Windows Firewall'
)}
</p>
<p className="text-xs text-muted-foreground">
{networkIsPublic
@ -124,11 +161,17 @@ export function WindowsFirewallNotice({
'auto.components.mobile.WindowsFirewallNotice.public-description',
'Change this trusted Wi-Fi network to Private before allowing Orca Mobile connections.'
)
: translate(
'auto.components.mobile.WindowsFirewallNotice.missing-description',
'Windows may block the pairing server. Add a rule for this Orca app and TCP port {{port}} on Private networks.',
{ port: firewallStatus.port }
)}
: blockingRuleDetected
? translate(
'auto.components.mobile.WindowsFirewallNotice.blocked-description',
'An existing inbound Block rule can override the pairing exception. Repair removes conflicting TCP rules for this Orca app, then allows port {{port}} on Private networks.',
{ port: firewallStatus.port }
)
: translate(
'auto.components.mobile.WindowsFirewallNotice.missing-description',
'Windows may block the pairing server. Add a rule for this Orca app and TCP port {{port}} on Private networks.',
{ port: firewallStatus.port }
)}
</p>
</div>
{networkIsPublic ? (
@ -155,10 +198,15 @@ export function WindowsFirewallNotice({
'auto.components.mobile.WindowsFirewallNotice.waiting',
'Waiting for Windows…'
)
: translate(
'auto.components.mobile.WindowsFirewallNotice.allow',
'Allow phone connections'
)}
: blockingRuleDetected
? translate(
'auto.components.mobile.WindowsFirewallNotice.repair',
'Repair firewall access'
)
: translate(
'auto.components.mobile.WindowsFirewallNotice.allow',
'Allow phone connections'
)}
</Button>
)}
</div>

View File

@ -10703,14 +10703,18 @@
},
"WindowsFirewallNotice": {
"repair-success": "Windows Firewall now allows Orca Mobile on private networks",
"repair-failed": "Could not add the Windows Firewall rule",
"repair-failed": "Could not update the Windows Firewall rules",
"public-title": "Windows marks this network as public",
"missing-title": "Allow phone connections through Windows Firewall",
"public-description": "Change this trusted Wi-Fi network to Private before allowing Orca Mobile connections.",
"missing-description": "Windows may block the pairing server. Add a rule for this Orca app and TCP port {{port}} on Private networks.",
"open-settings": "Open network settings",
"waiting": "Waiting for Windows…",
"allow": "Allow phone connections"
"allow": "Allow phone connections",
"repair-unverified": "Windows Firewall access could not be verified",
"blocked-title": "Windows may be blocking Orca Mobile",
"blocked-description": "An existing inbound Block rule can override the pairing exception. Repair removes conflicting TCP rules for this Orca app, then allows port {{port}} on Private networks.",
"repair": "Repair firewall access"
}
},
"gitlab": {

View File

@ -10703,14 +10703,18 @@
},
"WindowsFirewallNotice": {
"repair-success": "Windows Firewall now allows Orca Mobile on private networks",
"repair-failed": "Could not add the Windows Firewall rule",
"repair-failed": "Could not update the Windows Firewall rules",
"public-title": "Windows marks this network as public",
"missing-title": "Allow phone connections through Windows Firewall",
"public-description": "Change this trusted Wi-Fi network to Private before allowing Orca Mobile connections.",
"missing-description": "Windows may block the pairing server. Add a rule for this Orca app and TCP port {{port}} on Private networks.",
"open-settings": "Open network settings",
"waiting": "Waiting for Windows…",
"allow": "Allow phone connections"
"allow": "Allow phone connections",
"repair-unverified": "Windows Firewall access could not be verified",
"blocked-title": "Windows may be blocking Orca Mobile",
"blocked-description": "An existing inbound Block rule can override the pairing exception. Repair removes conflicting TCP rules for this Orca app, then allows port {{port}} on Private networks.",
"repair": "Repair firewall access"
}
},
"gitlab": {

View File

@ -10703,14 +10703,18 @@
},
"WindowsFirewallNotice": {
"repair-success": "Windows Firewall now allows Orca Mobile on private networks",
"repair-failed": "Could not add the Windows Firewall rule",
"repair-failed": "Could not update the Windows Firewall rules",
"public-title": "Windows marks this network as public",
"missing-title": "Allow phone connections through Windows Firewall",
"public-description": "Change this trusted Wi-Fi network to Private before allowing Orca Mobile connections.",
"missing-description": "Windows may block the pairing server. Add a rule for this Orca app and TCP port {{port}} on Private networks.",
"open-settings": "Open network settings",
"waiting": "Waiting for Windows…",
"allow": "Allow phone connections"
"allow": "Allow phone connections",
"repair-unverified": "Windows Firewall access could not be verified",
"blocked-title": "Windows may be blocking Orca Mobile",
"blocked-description": "An existing inbound Block rule can override the pairing exception. Repair removes conflicting TCP rules for this Orca app, then allows port {{port}} on Private networks.",
"repair": "Repair firewall access"
}
},
"gitlab": {

View File

@ -10703,14 +10703,18 @@
},
"WindowsFirewallNotice": {
"repair-success": "Windows Firewall now allows Orca Mobile on private networks",
"repair-failed": "Could not add the Windows Firewall rule",
"repair-failed": "Could not update the Windows Firewall rules",
"public-title": "Windows marks this network as public",
"missing-title": "Allow phone connections through Windows Firewall",
"public-description": "Change this trusted Wi-Fi network to Private before allowing Orca Mobile connections.",
"missing-description": "Windows may block the pairing server. Add a rule for this Orca app and TCP port {{port}} on Private networks.",
"open-settings": "Open network settings",
"waiting": "Waiting for Windows…",
"allow": "Allow phone connections"
"allow": "Allow phone connections",
"repair-unverified": "Windows Firewall access could not be verified",
"blocked-title": "Windows may be blocking Orca Mobile",
"blocked-description": "An existing inbound Block rule can override the pairing exception. Repair removes conflicting TCP rules for this Orca app, then allows port {{port}} on Private networks.",
"repair": "Repair firewall access"
}
},
"gitlab": {

View File

@ -10703,14 +10703,18 @@
},
"WindowsFirewallNotice": {
"repair-success": "Windows Firewall now allows Orca Mobile on private networks",
"repair-failed": "Could not add the Windows Firewall rule",
"repair-failed": "Could not update the Windows Firewall rules",
"public-title": "Windows marks this network as public",
"missing-title": "Allow phone connections through Windows Firewall",
"public-description": "Change this trusted Wi-Fi network to Private before allowing Orca Mobile connections.",
"missing-description": "Windows may block the pairing server. Add a rule for this Orca app and TCP port {{port}} on Private networks.",
"open-settings": "Open network settings",
"waiting": "Waiting for Windows…",
"allow": "Allow phone connections"
"allow": "Allow phone connections",
"repair-unverified": "Windows Firewall access could not be verified",
"blocked-title": "Windows may be blocking Orca Mobile",
"blocked-description": "An existing inbound Block rule can override the pairing exception. Repair removes conflicting TCP rules for this Orca app, then allows port {{port}} on Private networks.",
"repair": "Repair firewall access"
}
},
"gitlab": {

View File

@ -6,6 +6,7 @@ export type WindowsMobileFirewallStatus =
supported: true
port: number
ruleAllowed: boolean
blockingRuleDetected: boolean
privateFirewallEnabled: boolean
networkCategory: WindowsNetworkCategory
inspectionAvailable: boolean