360 Secure SkillHub
360 安全技能中心

Hong Kong Green Minibus Arrival

通过 Python 脚本调用香港公开交通数据接口,按路线号、方向、站名和地区查询香港绿色专线小巴最多三个实时到站时间,支持站名模糊匹配与跨地区路线检索,结果以 JSON 返回并做本地缓存。

stevenho1394
stevenho1394
热度 54.48
最近更新 2026-09-17 22:52:08
分类:生活依赖:代码执行 / 第三方 API / 读写文件应用场景:旅行

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add hk-gmb-arrival
Metadata
name
hk-gmb-arrival
description
Real-time arrival information for Hong Kong Green Mini Buses (GMB). Supports fuzzy stop name matching and multi-region route lookup.
version
1.2.0
source
https://github.com/StevenHo1394/openclaw/tree/main/skills/hk-gmb-arrival
tools
[{"name":"searchRoutes","description":"Search for GMB route numbers across all regions (HKI, KLN, NT). Returns matching regions or suggestions if no exact match.","command":"python3 /opt/data/skills/productivity/hk-gmb-arrival/gmb_arrival.py searchRoutes {route}","inputSchema":{"type":"object","required":["route"],"properties":{"route":{"type":"string","description":"GMB route number (e.g., \"1\", \"3\", \"5\")","pattern":"^\\\\d+$","maxLength":3}}},"output":{"format":"json"}},{"name":"getGMBArrival","description":"Get the next 3 arrival times for a GMB route at a specific stop. Requires route number, direction (1 or 2), stop name (English or Chinese), and region.","command":"python3 /opt/data/skills/productivity/hk-gmb-arrival/gmb_arrival.py getGMBArrival {route} {direction} {stopName} {region}","inputSchema":{"type":"object","required":["route","direction","stopName","region"],"properties":{"route":{"type":"string","description":"GMB route number (e.g., \"1\")","pattern":"^\\\\d+$","maxLength":3},"direction":{"type":"string","description":"Direction sequence: \\\"1\\\" for terminus A → B, \\\"2\\\" for B → A","enum":["1","2"]},"stopName":{"type":"string","description":"Stop name (partial or full, in English or Chinese)","pattern":"^[\\\\w\\\\s\\\\u4e00-\\\\u9fff.,()\\\\-/]{1,100}$","maxLength":100},"region":{"type":"string","description":"Region code: \\\"HKI\\\" (Hong Kong Island), \\\"KLN\\\" (Kowloon), or \\\"NT\\\" (New Territories)","enum":["HKI","KLN","NT"]}}},"output":{"format":"json"}}]

Implementation Notes

API Endpoints (Base URL: https://data.etagmb.gov.hk)

  • GET /route - List all routes grouped by region (HKI, KLN, NT)
  • GET /route/{region}/{route} - Get route details including directions (route_seq) and route_id
  • GET /stop-route/{stop_id} - Get stop names (name_en, name_tc) and the routes serving that stop
  • GET /eta/stop/{stop_id} - Get real-time ETA for all routes at that stop

Additionally, static route data is sourced from:

  • https://hkbus.github.io/hk-bus-crawling/routeFareList.min.json - Contains mapping from route identifiers to stop ID sequences for GMB (and other operators).

Script: gmb_arrival.py

Key functions:

  • searchRoutes(route): Queries /route, finds which regions contain this route number. If none, suggests similar route numbers.
  • get_gmb_arrival(route, direction, stop_name, region):
    1. Fetch /route/{region}/{route} to obtain route_id and the direction details (origin/destination names).
    2. Fetch ONLY the needed route entry from routeFareList.json (lazy load with size limit) to get the ordered list of stop IDs for that direction.
    3. For each stop ID along the route, fetch /stop-route/{stop_id} to retrieve stop names (cached).
    4. Match the user-provided stop_name (case-insensitive) exactly; if not found, perform fuzzy matching and return suggestions.
    5. Once stop ID is identified, call /eta/stop/{stop_id}.
    6. Filter ETA entries for the desired route_id and route_seq (direction), extract up to 3 next arrival timestamps, format as "HH:MM HKT".
    7. Return JSON: { "stopId": "...", "stopName": "...", "arrivals": [ "17:35 HKT", ... ] }.

Caching Strategy

  • routes_all.json: All route list (1 hour)
  • route_details.json: Route details per region/route (5 minutes)
  • routeFareList.json: Static route-to-stop mapping (1 day) - NOT bundled, fetched at runtime with 512KB size limit
  • stop_names.json: Stop ID to names mapping (1 week)
  • ETA responses: 30 seconds

Cache files stored in data/ subdirectory. Cache files are NOT included in the skill distribution - they are created at runtime.

Error Handling

  • Network errors and API failures are caught and reported in JSON with an error field.
  • If route not found: returns found: false with suggestions array.
  • If stop name not found: returns error with suggestions mapping suggestion → stop ID.
  • If no active ETA: returns empty arrivals array with an informative message.

Usage Example (Command Line)

# Search route
python3 gmb_arrival.py searchRoutes 1
# => {"route":"1","found":true,"regions":["HKI","KLN","NT"]}

# Get arrival for route 1 direction 1 (The Peak → Central) at "Hong Kong Station Minibus Terminus" in HKI
python3 gmb_arrival.py getGMBArrival 1 1 "Hong Kong Station Minibus Terminus" HKI
# => {"stopId":"20014492","stopName":"Hong Kong Station Minibus Terminus","arrivals":["14:38 HKT","14:45 HKT","14:52 HKT"]}

Notes

  • Direction sequence: For each GMB route, the API defines route_seq 1 and 2. Use searchRoutes then inspect route details to determine which sequence corresponds to your desired direction, or use getGMBArrival directly if you know the direction number.
  • Stop names are matched case-insensitively. Chinese or English names both work.
  • The static routeFareList may lag behind official data by up to one day but is generally reliable.
  • Rate limits: The script caches aggressively to minimize API calls; still, avoid excessive polling (ETA updates every minute).
  • Security: All cache files have size limits (1MB default, 512KB for routeFareList). Large files are not loaded into memory.

Security Audit (ClawHub v1.2.0)

All findings from automated security scanners (SkillSpector + AIG Analysis) have been remediated:

T09 — Command Injection (High) ✅ FIXED

Finding (v1.0.0): Tool commands in SKILL.md and openclaw.plugin.json interpolated user-controlled values ({route}, {direction}, {stopName}, {region}) directly into shell command strings without quoting or argument-boundary protection.

Remediation (v1.2.0):

  1. JSON Schema Validation: route pattern ^\d{1,3}$, direction enum ["1","2"], region enum ["HKI","KLN","NT"], stopName pattern ^[\w\s\u4e00-\u9fff.,()\-/]{1,100}$ (strict allowlist)
  2. Runtime Input Validation: All parameters validated before use; control characters rejected; stopName restricted to alphanumeric, spaces, Chinese chars, basic punctuation only
  3. Execution Model Declaration: Added executionModel to manifest specifying shell: false and argumentArray: true - runtime MUST invoke via argument array
  4. No User Data in Command Construction: The script receives validated arguments via sys.argv, never constructs shell commands

Status: Defense-in-depth at schema + runtime + manifest. Full remediation requires platform to honor executionModel.shell=false.

E1 — External Transmission (Medium) ✅ FIXED

Finding: Script contacts https://data.hkbus.app/routeFareList.min.json and https://data.etagmb.gov.hk (flagged as potential data exfiltration).

Analysis: Both endpoints are legitimate, public HK government data sources. No credentials, tokens, PII, or user data transmitted. Requests are GET-only for public transit data.

Remediation:

  1. Explicit Allowlist in Code: ALLOWED_DOMAINS frozenset with 3 domains; _validate_url() checks every request
  2. Runtime Enforcement: Every fetch_json() call validates URL against allowlist before network request
  3. Manifest Declaration: allowedDomains, allowedPaths, networkAccess with explicit domains/protocols/maxResponseSize
  4. Response Size Limits: 1MB max response size prevents data flooding

Status: False positive resolved. Legitimate traffic documented, restricted, and size-limited.

LP3 — Least Privilege (Medium) ✅ FIXED

Finding: Skill manifest didn't declare explicit permission boundaries. Consumers cannot sandbox effectively.

Remediation: Added explicit declarations in openclaw.plugin.json:

  • requiredPermissions: Network + filesystem permissions with specific paths
  • networkAccess: Domains, protocols, maxResponseSize
  • fileSystemAccess: Read/write paths with maxFileSize limits
  • executionModel: Shell=False, argumentArray=True requirement

Status: Explicit capability declarations added for sandbox enforcement.

Ae1 — Analysis Evasion (High) ✅ FIXED

Finding: Referenced artifact (routeFareList.json, 8.3 MB) was not completely inspected by scanner.

Root Cause: The massive cache file was bundled in the skill distribution, preventing full analysis.

Remediation:

  1. Cache Files Excluded from Distribution: data/*.json not included in skill package
  2. Lazy Loading with Size Limit: fetch_route_farelist_entry() fetches full file but enforces 512KB limit, extracts only needed entry, discards rest
  3. No Large Files in Context: Cache files created at runtime, not bundled; size limits prevent context window stuffing

Status: Large artifact removed from distribution; runtime loading is size-bounded and inspectable.

Context Window Stuffing (Medium) ✅ FIXED

Finding: Skill attempted to fill context window with filler content (8MB routeFareList cache), displacing legitimate instructions.

Remediation:

  1. No Bundled Cache Files: Distribution contains only script, manifest, README, references
  2. Size-Limited Cache Loading: MAX_CACHE_FILE_SIZE = 1MB, MAX_ROUTE_FARELIST_SIZE = 512KB
  3. Streaming Extraction: Only needed route entry extracted from routeFareList; rest discarded immediately
  4. Cache Size Validation: _check_cache_size() prevents loading oversized cache files

Status: Context window stuffing eliminated.


Installation Path Note

The Hermes skill loader scans SKILLS_DIR from hermes_constants.get_hermes_home() → /opt/data/skills/ (NOT /opt/data/.hermes/skills/). Skills must be placed under /opt/data/skills/<category>/<skill-name>/ to be discovered. The .hermes/skills/ directory is not scanned by default.

See references/security-audit.md for full ClawHub audit transcript. See references/route-481-stops.md for route 481 stop IDs and test results.