#!/usr/bin/env bash
# Scripts/check-no-network.sh: fails if a built Kept binary contains networking code.
#
#   Scripts/check-no-network.sh                         checks build/Kept.app
#   Scripts/check-no-network.sh path/to/Kept.app        checks that bundle
#   Scripts/check-no-network.sh path/to/binary [...]    checks those Mach-O files
#
# Kept runs without the App Sandbox (the sandbox forbids the Accessibility permission that
# pasting for you needs), so the "no network entitlement" guarantee of 1.0 is replaced by this
# check on every release build, plus KeptTests/PrivacyTests.swift on the sources.
#
# What it checks, for every executable in the bundle (Contents/MacOS/* and Contents/Helpers/*):
#   1. `otool -L`: no direct link to a networking library. The baseline for Kept 1.1 is
#      Foundation, AppKit, SwiftUI, Combine, Carbon, ServiceManagement, ApplicationServices,
#      CoreFoundation, CoreGraphics, CoreServices, CryptoKit, ImageIO, QuartzCore, QuickLookUI,
#      libsqlite3, libobjc, libSystem, libc++ and the Swift runtime overlays. Foundation itself
#      loads CFNetwork at run time (every Cocoa app has it in memory); that transitive load is
#      not a link of Kept's and does nothing unless code calls it, which (2) rules out.
#   2. `nm -u`: no undefined symbol from a networking API: URLSession / NSURLSession,
#      NSURLConnection, URLRequest, Network.framework (nw_*, NWConnection), CFSocket, socket
#      streams, BSD socket()/connect()/getaddrinfo(), WebKit views.
set -euo pipefail

targets=()
if [[ $# -eq 0 ]]; then set -- build/Kept.app; fi
for arg in "$@"; do
  if [[ -d "$arg" ]]; then
    for f in "$arg"/Contents/MacOS/* "$arg"/Contents/Helpers/*; do
      [[ -f "$f" ]] && targets+=("$f")
    done
  elif [[ -f "$arg" ]]; then
    targets+=("$arg")
  else
    echo "check-no-network: $arg not found" >&2; exit 2
  fi
done
[[ ${#targets[@]} -gt 0 ]] || { echo "check-no-network: no binaries to check" >&2; exit 2; }

LIB_PATTERN='/Network\.framework|/CFNetwork\.framework|/WebKit\.framework|libnetwork|/NetworkExtension\.framework|/SystemConfiguration\.framework'
SYM_PATTERN='URLSession|NSURLConnection|NSURLDownload|URLRequest|_nw_|NWConnection|NWListener|NWPathMonitor|CFSocket|SocketToHost|WKWebView|SCNetworkReachability|^_socket$|^_connect$|^_getaddrinfo$|^_gethostbyname'

status=0
for bin in "${targets[@]}"; do
  if ! file "$bin" | grep -q "Mach-O"; then continue; fi
  ok=1
  libs=$(otool -L "$bin" | tail -n +2)
  if bad=$(grep -E "$LIB_PATTERN" <<<"$libs"); then
    echo "✗ $bin links a networking library:" >&2
    echo "$bad" >&2
    ok=0
  fi
  syms=$(nm -u "$bin" 2>/dev/null | awk '{print $NF}' | sort -u)
  if bad=$(grep -E "$SYM_PATTERN" <<<"$syms"); then
    echo "✗ $bin references networking symbols:" >&2
    echo "$bad" | head -20 >&2
    ok=0
  fi
  if [[ $ok -eq 1 ]]; then echo "✓ no networking code in $bin"; else status=1; fi
done
exit $status
