T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/entur.py:87
- Finding
- GraphQL Injection Through Unescaped CLI and Geocoder Inputs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/entur.py:87-100`, `scripts/entur.py:166-181`, `scripts/entur.py:219-234`, and `scripts/entur.py:259-267` **Vulnerability Type**: GraphQL injection caused by unsafe string interpolation **Risk Level**: Medium ### Vulnerable Code ```python # scripts/entur.py:87-100 if args.modes: mode_list = [m.strip() for m in args.modes.split(",")] transport_modes = ", ".join( f'{{transportMode: {m}}}' for m in mode_list ) modes_filter = f"modes: {{transportModes: [{transport_modes}]}}" time_clause = "" if args.time: time_clause = f'dateTime: "{args.time}"' arrive_clause = "" if args.arrive: arrive_clause = "arriveBy: true" ``` ```python # scripts/entur.py:166-181 query = f""" {{ stopPlace(id: "{args.stop_id}") {{ id name estimatedCalls(timeRange: 3600, numberOfDepartures: {args.limit or 10}) {{ realtime aimedDepartureTime expectedDepartureTime destinationDisplay {{ frontText }} serviceJourney {{ line {{ publicCode name transportMode authority {{ name }} }} }} ``` ```python # scripts/entur.py:219-234 query = f""" {{ stopPlace(id: "{args.stop_id}") {{ id name transportMode description latitude longitude quays {{ id name publicCode description }} }} }} """ ``` ```python # scripts/entur.py:259-267 def _place_arg(place: dict) -> str: if "id" in place: s = f'place: "{place["id"]}"' else: c = place["coords"] s = f'coordinates: {{latitude: {c["lat"]}, longitude: {c["lon"]}}}' if "name" in place: s += f', name: "{place["name"]}"' return s ``` ### Technical Analysis The script constructs GraphQL documents by directly interpolating values originating from command-line arguments or geocoder responses. The affected values include: - `--time` - `--modes` - Stop IDs used by the ` ...[truncated 1999 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Define static GraphQL documents and pass every dynamic value through GraphQL variables. This should include stop IDs, timestamps, place names, coordinates, mode filters, result counts, and departure limits. 2. Validate `--modes` against an explicit allowlist: - `bus` - `rail` - `tram` - `metro` - `water` - `air` - `coach` 3. Validate direct stop and place IDs with a strict format appropriate for Entur identifiers. Reject unexpected quotes, whitespace, braces, comments, or control characters. 4. Parse `--time` with a strict ISO-8601 parser and transmit the normalized value through a GraphQL variable. 5. Treat geocoder output as untrusted remote data. Place names and IDs returned by the service must also be passed through GraphQL variables rather than inserted into query source. 6. Check GraphQL `errors` in API responses and return a controlled error rather than silently treating malformed or rejected operations as empty results. 7. Add tests containing quotes, braces, GraphQL comments, Unicode control characters, and invalid mode names to verify that input cannot modify query structure. ]]>
