Skip to content

Commit 9747acd

Browse files
tools: Make 4byte.directory selector lookup opt-in in decode_apdu
decode_function_selector() automatically queried 4byte.directory for any selector that was not present in the local cache. Operators running the decoder on captured APDU traces had no way to keep the analysis offline, and the function selectors of internal contracts or private integrations could leak to a third-party host without an explicit prompt. Add an --online-selectors flag (default off). When set, main() flips the module-level ALLOW_ONLINE_SELECTOR_LOOKUP gate and emits a clear warning before any external request. Without the flag the decoder returns "Unknown (0x...)" for cache misses and never reaches the network (CWE-201).
1 parent 74e05a4 commit 9747acd

1 file changed

Lines changed: 30 additions & 1 deletion

File tree

tools/decode_apdu.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626

2727
# Local selector cache at module level
2828
LOCAL_SELECTORS = {}
29+
# Whether to fall back to 4byte.directory for selectors absent from the local
30+
# cache. Opt-in only — set from --online-selectors in main() so that running
31+
# the decoder over a captured APDU trace does not silently disclose its
32+
# function selectors to a third-party host (CWE-201).
33+
ALLOW_ONLINE_SELECTOR_LOOKUP = False
2934
CACHE_FILE = Path(__file__).parent / "function_selectors.json"
3035

3136
logger = logging.getLogger(__name__)
@@ -49,6 +54,15 @@ def init_parser() -> argparse.ArgumentParser:
4954
parser = argparse.ArgumentParser(description="Decode APDU replay file to extract transaction details.")
5055
parser.add_argument("--input", "-i", required=True, help="Input apdu replay file.")
5156
parser.add_argument("--verbose", "-v", action='store_true', help="Verbose mode")
57+
parser.add_argument(
58+
"--online-selectors",
59+
action="store_true",
60+
help=(
61+
"Allow unknown function selectors to be looked up online against "
62+
"4byte.directory. By default the decoder works offline and leaks "
63+
"no replay data over the network (CWE-201)."
64+
),
65+
)
5266
return parser
5367

5468

@@ -123,7 +137,12 @@ def decode_function_selector(selector: str) -> str:
123137
logger.debug(f"Found selector {selector} in local cache")
124138
return LOCAL_SELECTORS[selector]
125139

126-
# 2. Try online API as fallback
140+
# 2. Try online API as fallback — only when the user explicitly asked for
141+
# it. Without the opt-in we never leak the selector to 4byte.directory.
142+
if not ALLOW_ONLINE_SELECTOR_LOOKUP:
143+
logger.debug(f"Selector {selector} not in cache; online lookup disabled")
144+
return f"Unknown (0x{selector})"
145+
127146
logger.debug(f"Selector {selector} not in cache, querying API...")
128147
try:
129148
url = f"https://www.4byte.directory/api/v1/signatures/?hex_signature=0x{selector}"
@@ -1024,11 +1043,21 @@ def parse_apdu_line(line: str) -> Optional[bytes]:
10241043
# Main entry
10251044
# ===============================================================================
10261045
def main() -> None:
1046+
global ALLOW_ONLINE_SELECTOR_LOOKUP
1047+
10271048
parser = init_parser()
10281049
args = parser.parse_args()
10291050

10301051
set_logging(args.verbose)
10311052

1053+
if args.online_selectors:
1054+
ALLOW_ONLINE_SELECTOR_LOOKUP = True
1055+
logger.warning(
1056+
"Online selector lookup enabled: unknown function selectors from "
1057+
"the replay will be sent to https://www.4byte.directory/. Do not "
1058+
"use this flag on sensitive traces."
1059+
)
1060+
10321061
# Load selector cache at startup
10331062
load_selector_cache()
10341063
logger.debug(f"Loaded {len(LOCAL_SELECTORS)} function selectors from cache")

0 commit comments

Comments
 (0)