Skip to content

[Duplicate Code] Host-access port validation still repeats across firewall layers #6015

Description

@github-actions

Duplicate Code Opportunity

Summary

  • Pattern: Host-access port parsing and validation is implemented separately in TypeScript host firewall rules and the agent iptables shell script.
  • Locations: src/host-iptables-validation.ts lines 7-17 and 38-57; containers/agent/setup-iptables.sh lines 26-41, 45-63, 269-281, and 374-399.
  • Impact: Consolidates security-critical port-spec semantics and reduces the chance that host-level DOCKER-USER rules and in-container NAT/filter rules drift.

Related prior issue: #5500 was marked completed, but the duplication still reproduces in the current tree.

Evidence

src/host-iptables-validation.ts validates single ports/ranges, trims comma-delimited lists, warns, and skips invalid specs:

function isValidPortSpec(spec: string): boolean {
  const rangeMatch = spec.match(/^(\d+)-(\d+)$/);
  if (rangeMatch) {
    const start = parseInt(rangeMatch[1], 10);
    const end = parseInt(rangeMatch[2], 10);
    if (String(start) !== rangeMatch[1] || String(end) !== rangeMatch[2]) return false;
    return start >= 1 && start <= 65535 && end >= 1 && end <= 65535 && start <= end;
  }
  const port = parseInt(spec, 10);
  return !isNaN(port) && String(port) === spec && port >= 1 && port <= 65535;
}

export function parseValidPortSpecs(input: string | undefined, label: string): string[] {
  if (!input) {
    return [];
  }

  const validSpecs: string[] = [];
  for (const entry of input.split(',')) {
    const trimmed = entry.trim();
    if (!trimmed) {
      continue;
    }
    if (!isValidPortSpec(trimmed)) {
      logger.warn(`Skipping invalid ${label}: ${trimmed}`);
      continue;
    }
    validSpecs.push(trimmed);
  }

  return validSpecs;
}

containers/agent/setup-iptables.sh repeats the validation grammar and skip-on-invalid behavior for both host-access bypasses and DNAT redirects:

is_valid_port_spec() {
  local spec="$1"
  if echo "$spec" | grep -qE '^[1-9][0-9]{0,4}-[1-9][0-9]{0,4}$'; then
    local start=$(echo "$spec" | cut -d- -f1)
    local end=$(echo "$spec" | cut -d- -f2)
    [ "$start" -ge 1 ] && [ "$start" -le 65535 ] && [ "$end" -ge 1 ] && [ "$end" -le 65535 ] && [ "$start" -le "$end" ]
  elif echo "$spec" | grep -qE '^[1-9][0-9]{0,4}$'; then
    [ "$spec" -ge 1 ] && [ "$spec" -le 65535 ]
  else
    return 1
  fi
}

# ...
IFS=',' read -ra gw_ports <<< "$AWF_ALLOW_HOST_PORTS"
for port_spec in "${gw_ports[@]}"; do
  port_spec=$(echo "$port_spec" | xargs)
  if ! is_valid_port_spec "$port_spec"; then
    echo "[iptables] WARNING: Skipping invalid port spec: $port_spec"
    continue
  fi
  echo "[iptables]   Allow ${label} port $port_spec"
  iptables -A OUTPUT -p tcp -d "$gw_ip" --dport "$port_spec" -j ACCEPT
done

# ...
IFS=',' read -ra PORTS <<< "$AWF_ALLOW_HOST_PORTS"
for port_spec in "${PORTS[@]}"; do
  port_spec=$(echo "$port_spec" | xargs)

  if ! is_valid_port_spec "$port_spec"; then
    echo "[iptables] WARNING: Skipping invalid port spec: $port_spec"
    continue
  fi

  if [[ $port_spec == *"-"* ]]; then
    iptables -t nat -A OUTPUT -p tcp --dport "$port_spec" -j DNAT --to-destination "${SQUID_IP}:${SQUID_PORT}"
  else
    iptables -t nat -A OUTPUT -p tcp --dport "$port_spec" -j DNAT --to-destination "${SQUID_IP}:${SQUID_PORT}"
  fi
done

Suggested Refactoring

Generate the shell validator and parser from a single shared source of truth, or add a small build-time checked fixture that exports both:

  • TypeScript isValidPortSpec / parseValidPortSpecs for host firewall rules.
  • Shell is_valid_port_spec / parse_port_specs emitted into setup-iptables.sh or sourced from a generated helper.

Then use the shared shell parser for AWF_ALLOW_HOST_PORTS and AWF_HOST_SERVICE_PORTS so all agent-side port consumers receive the same normalized list.

Affected Files

  • src/host-iptables-validation.ts — lines 7-17, 38-57
  • src/host-iptables-rules.ts — lines 200-205
  • containers/agent/setup-iptables.sh — lines 26-41, 45-63, 269-281, 374-399

Effort Estimate

Medium


Detected by Duplicate Code Detector workflow. Run date: 2026-07-08

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by Duplicate Code Detector · 60.1 AIC · ⊞ 20.1K ·

  • expires on Aug 7, 2026, 6:35 AM UTC

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions