|
| 1 | +#!/bin/bash |
| 2 | +# Real-life proxy/TLS test for codacy-cli-v2 (OD-30). |
| 3 | +# |
| 4 | +# Runs the ACTUAL cli-v2 binary through a REAL mitmproxy MITM proxy against the |
| 5 | +# real app.codacy.com, simulating a corporate TLS-intercepting proxy. Asserts: |
| 6 | +# |
| 7 | +# A. proxy + custom CA (SSL_CERT_FILE) -> success, traffic seen by proxy |
| 8 | +# B. proxy, no CA -> TLS verification failure, traffic seen |
| 9 | +# C. proxy + CODACY_CLI_INSECURE -> success, traffic seen |
| 10 | +# D. NO_PROXY for app.codacy.com -> success, proxy NOT traversed |
| 11 | +# |
| 12 | +# Cases A and C require the OD-30 feature (custom CA + insecure toggle). Before |
| 13 | +# that is implemented they FAIL with "certificate is not trusted" — that failure |
| 14 | +# is the baseline that proves the feature is needed. After implementation, green. |
| 15 | +# |
| 16 | +# Loopable: PROXY_TLS_LOOP=5 ./run.sh |
| 17 | +# Requires: mitmproxy (mitmdump). brew install mitmproxy |
| 18 | +set -uo pipefail |
| 19 | + |
| 20 | +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" |
| 21 | +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 22 | +CLI="$REPO_ROOT/cli-v2" |
| 23 | +PROXY_PORT="${PROXY_PORT:-8899}" |
| 24 | +CA="$HOME/.mitmproxy/mitmproxy-ca-cert.pem" |
| 25 | +WORK="$(mktemp -d)" |
| 26 | +export PROXY_CONNECT_LOG="$WORK/connects.txt" |
| 27 | +MITM_PID="" |
| 28 | + |
| 29 | +red() { printf '\033[31m%s\033[0m\n' "$*"; } |
| 30 | +green() { printf '\033[32m%s\033[0m\n' "$*"; } |
| 31 | + |
| 32 | +cleanup() { |
| 33 | + [ -n "$MITM_PID" ] && kill "$MITM_PID" 2>/dev/null |
| 34 | + rm -rf "$WORK" |
| 35 | +} |
| 36 | +trap cleanup EXIT |
| 37 | + |
| 38 | +command -v mitmdump >/dev/null 2>&1 || { red "mitmdump not found. Install: brew install mitmproxy"; exit 2; } |
| 39 | +[ -x "$CLI" ] || { echo "Building cli-v2..."; (cd "$REPO_ROOT" && make build) || exit 2; } |
| 40 | + |
| 41 | +# Start proxy with the connect-logging addon. |
| 42 | +mitmdump -p "$PROXY_PORT" -q -s "$HERE/connect_logger.py" >"$WORK/mitm.log" 2>&1 & |
| 43 | +MITM_PID=$! |
| 44 | + |
| 45 | +# Wait for proxy to bind and generate its CA. |
| 46 | +for _ in $(seq 1 40); do |
| 47 | + [ -f "$CA" ] && nc -z localhost "$PROXY_PORT" 2>/dev/null && break |
| 48 | + sleep 0.3 |
| 49 | +done |
| 50 | +[ -f "$CA" ] || { red "mitmproxy CA not generated at $CA"; cat "$WORK/mitm.log"; exit 2; } |
| 51 | + |
| 52 | +# Fresh, network-touching, tokenless CLI command. init hits app.codacy.com/api/v3. |
| 53 | +# Args are VAR=val pairs prepended to the cli invocation via env. |
| 54 | +run_init() { |
| 55 | + local dir="$WORK/proj.$RANDOM" |
| 56 | + mkdir -p "$dir" |
| 57 | + ( cd "$dir" && env "$@" "$CLI" init >"$WORK/last.log" 2>&1 ) |
| 58 | + local rc=$? |
| 59 | + rm -rf "$dir" |
| 60 | + return $rc |
| 61 | +} |
| 62 | + |
| 63 | +proxy_saw_codacy() { grep -q "codacy.com" "$PROXY_CONNECT_LOG" 2>/dev/null; } |
| 64 | + |
| 65 | +FAILURES=0 |
| 66 | +# check NAME EXPECT_RC(0|fail) EXPECT_PROXY(yes|no) -- VAR=val ... |
| 67 | +check() { |
| 68 | + local name="$1" want_rc="$2" want_proxy="$3"; shift 3; [ "$1" = "--" ] && shift |
| 69 | + : >"$PROXY_CONNECT_LOG" |
| 70 | + run_init "$@"; local rc=$? |
| 71 | + sleep 0.3 # let addon flush |
| 72 | + local saw="no"; proxy_saw_codacy && saw="yes" |
| 73 | + local ok=1 |
| 74 | + [ "$want_rc" = "0" ] && [ "$rc" -ne 0 ] && ok=0 |
| 75 | + [ "$want_rc" = "fail" ] && [ "$rc" -eq 0 ] && ok=0 |
| 76 | + [ "$want_proxy" != "$saw" ] && ok=0 |
| 77 | + if [ "$ok" -eq 1 ]; then |
| 78 | + green "PASS $name (rc=$rc, proxy_saw=$saw)" |
| 79 | + else |
| 80 | + red "FAIL $name (rc=$rc want=$want_rc, proxy_saw=$saw want=$want_proxy)" |
| 81 | + echo "----- cli output (tail) -----"; tail -3 "$WORK/last.log" 2>/dev/null; echo "-----------------------------" |
| 82 | + FAILURES=$((FAILURES+1)) |
| 83 | + fi |
| 84 | +} |
| 85 | + |
| 86 | +run_suite() { |
| 87 | + local P="http://localhost:$PROXY_PORT" |
| 88 | + echo "== A: proxy + custom CA (needs OD-30) ==" |
| 89 | + check "A custom-CA" 0 yes -- HTTPS_PROXY="$P" HTTP_PROXY="$P" SSL_CERT_FILE="$CA" |
| 90 | + echo "== B: proxy, no CA (expect TLS failure) ==" |
| 91 | + check "B no-CA-fails" fail yes -- HTTPS_PROXY="$P" HTTP_PROXY="$P" |
| 92 | + echo "== C: proxy + insecure (needs OD-30) ==" |
| 93 | + check "C insecure" 0 yes -- HTTPS_PROXY="$P" HTTP_PROXY="$P" CODACY_CLI_INSECURE=1 |
| 94 | + echo "== D: NO_PROXY bypass ==" |
| 95 | + check "D no_proxy-bypass" 0 no -- HTTPS_PROXY="$P" NO_PROXY="app.codacy.com,api.codacy.com" SSL_CERT_FILE="$CA" |
| 96 | +} |
| 97 | + |
| 98 | +LOOP="${PROXY_TLS_LOOP:-1}" |
| 99 | +for i in $(seq 1 "$LOOP"); do |
| 100 | + [ "$LOOP" -gt 1 ] && echo "### iteration $i/$LOOP ###" |
| 101 | + run_suite |
| 102 | +done |
| 103 | + |
| 104 | +echo |
| 105 | +if [ "$FAILURES" -eq 0 ]; then green "ALL PROXY/TLS CHECKS PASSED"; else red "$FAILURES check(s) FAILED"; fi |
| 106 | +exit "$FAILURES" |
0 commit comments