diff --git a/connect-vpn/action.yml b/connect-vpn/action.yml index 25b56a4..f66c244 100644 --- a/connect-vpn/action.yml +++ b/connect-vpn/action.yml @@ -191,6 +191,71 @@ runs: curl -k -sS --http1.1 -A "$UA" "$@" } + build_otp_post_body() { + python3 - "$1" "$2" <<'PY' + from html.parser import HTMLParser + import sys + import urllib.parse + + html_file, otp_code = sys.argv[1], sys.argv[2] + + class FormParser(HTMLParser): + def __init__(self): + super().__init__() + self.forms = [] + self.current = None + + def handle_starttag(self, tag, attrs): + attrs = {k.lower(): (v if v is not None else "") for k, v in attrs} + if tag.lower() == "form": + self.current = {"attrs": attrs, "inputs": []} + self.forms.append(self.current) + elif tag.lower() == "input" and self.current is not None: + self.current["inputs"].append(attrs) + + def handle_endtag(self, tag): + if tag.lower() == "form": + self.current = None + + parser = FormParser() + with open(html_file, encoding="utf-8", errors="ignore") as f: + parser.feed(f.read()) + + form = None + for candidate in parser.forms: + names = {field.get("name", "") for field in candidate["inputs"]} + if "ga_code_attempt" in names: + form = candidate + break + if form is None: + raise SystemExit("OTP form not found") + + data = [] + seen = set() + for field in form["inputs"]: + name = field.get("name", "") + if not name: + continue + field_type = field.get("type", "text").lower() + if field_type in {"button", "image", "file"}: + continue + if field_type in {"checkbox", "radio"} and "checked" not in field: + continue + value = field.get("value", "") + if name == "ga_code_attempt": + value = otp_code + data.append((name, value)) + seen.add(name) + + if "ga_code_attempt" not in seen: + data.append(("ga_code_attempt", otp_code)) + if "vhost" not in seen: + data.append(("vhost", "standard")) + + print(urllib.parse.urlencode(data)) + PY + } + debug_print_otps() { echo "DEBUG OTP 확인용 코드 (unix-time=$(date +%s))" for otp_offset in 0 -1 1; do @@ -255,12 +320,14 @@ runs: if [ "$DEBUG_OTP" = "true" ]; then echo "DEBUG generated OTP offset=${otp_offset}: ${OTP_CODE} (unix-time=$(date +%s))" fi + OTP_POST_BODY="$WORKDIR/03-otp-post-${otp_offset}.body" + build_otp_post_body "$WORKDIR/02-otp.html" "$OTP_CODE" > "$OTP_POST_BODY" rm -f "$TOKEN_HEADERS" "$WORKDIR/04-token.body" curl_common -L -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ -X POST "$VPN_HOST/my.policy" \ - --data-urlencode "ga_code_attempt=$OTP_CODE" \ - --data-urlencode "vhost=standard" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + --data-binary "@$OTP_POST_BODY" \ -o "$WORKDIR/03-webtop-${otp_offset}.html" curl_common -b "$COOKIE_JAR" -c "$COOKIE_JAR" \