From 3db19d90ac7eebe4db4861d9092cc9cbec90adf4 Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Sat, 21 Feb 2026 08:20:48 -0800 Subject: [PATCH] fix: accept comma in Fly.io macaroon tokens & handle flat org dict (#1593) Real `fly auth token` returns comma-separated multi-segment macaroon tokens (fm2_...,fm2_...,fo1_...). The token validation regex rejected commas, forcing re-auth on every run. Add comma to the allowed charset. `fly orgs list --json` returns a flat dict ({"slug": "Name"}) on some flyctl versions, not the list/nodes format the parser expected. Detect and handle both formats so the org picker works correctly. Co-authored-by: lab <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- fly/lib/common.sh | 27 +++++++++++++++++---------- shared/common.sh | 7 ++++--- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/fly/lib/common.sh b/fly/lib/common.sh index 21d0b91f..4ead108d 100644 --- a/fly/lib/common.sh +++ b/fly/lib/common.sh @@ -190,16 +190,23 @@ _fly_list_orgs() { import json, sys try: data = json.loads(sys.stdin.read()) - orgs = data if isinstance(data, list) else data.get('nodes', data.get('organizations', [])) - if not orgs: - sys.exit(1) - for o in orgs: - slug = o.get('slug') or o.get('name') or '' - name = o.get('name') or slug - otype = o.get('type') or '' - suffix = ' (' + otype + ')' if otype else '' - if slug: - print(slug + '|' + name + suffix) + if isinstance(data, dict) and not any(k in data for k in ('nodes', 'organizations')): + # Flat dict format: {'slug': 'Display Name', ...} + if not data: + sys.exit(1) + for slug, name in data.items(): + print(slug + '|' + str(name)) + else: + orgs = data if isinstance(data, list) else data.get('nodes', data.get('organizations', [])) + if not orgs: + sys.exit(1) + for o in orgs: + slug = o.get('slug') or o.get('name') or '' + name = o.get('name') or slug + otype = o.get('type') or '' + suffix = ' (' + otype + ')' if otype else '' + if slug: + print(slug + '|' + name + suffix) except Exception: sys.exit(1) " 2>/dev/null diff --git a/shared/common.sh b/shared/common.sh index a5d002b6..3124b163 100644 --- a/shared/common.sh +++ b/shared/common.sh @@ -2766,10 +2766,11 @@ _load_token_from_config() { # SECURITY: Validate token characters to prevent curl config injection via -K - # Similar to key-request.sh _try_load_env_var (^[a-zA-Z0-9._/@-]+$) but also # allows colon (:) for Fly.io FlyV1 tokens and URL-style formats, - # plus (+) / equals (=) for base64-encoded token segments, and + # plus (+) / equals (=) for base64-encoded token segments, + # comma (,) for multi-segment macaroon tokens (fm2_...,fm2_...,fo1_...), and # space ( ) for Fly.io "FlyV1 " prefixed tokens. - # Space is safe inside curl -K double-quoted values: header = "Authorization: FlyV1 fm2_..." - if [[ ! "${saved_token}" =~ ^[a-zA-Z0-9._/@:+=\ -]+$ ]]; then + # Space and comma are safe inside curl -K double-quoted values. + if [[ ! "${saved_token}" =~ ^[a-zA-Z0-9._/@:+=,\ -]+$ ]]; then log_warn "Saved ${provider_name} token is malformed — clearing cached credentials." rm -f "${config_file}" 2>/dev/null || true return 1