T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/create.sh:69
- Finding
- Arbitrary Python Code Execution Through Unsafe Variable Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create.sh`, lines 69–73, 82–95, and 146–151 **Vulnerability Type**: Python source-code injection **Risk Level**: High ### Vulnerable Code #### Start-time interpolation at lines 69–73 ```bash START_TS=$(date -d "$START_TIME" +%s 2>/dev/null || python3 -c " from datetime import datetime print(int(datetime.strptime('$START_TIME', '%Y-%m-%d %H:%M').timestamp())) ") ``` #### Meeting topic and recurrence-rule interpolation at lines 82–95 ```bash EVENT_JSON=$(python3 -c " import json event = { 'summary': '''$MEETING_TOPIC''', 'start_time': {'timestamp': '$START_TS', 'timezone': 'Asia/Shanghai'}, 'end_time': {'timestamp': '$END_TS', 'timezone': 'Asia/Shanghai'}, 'vchat': {'vc_type': 'vc'}, 'attendee_ability': 'can_see_others' } rrule = '''$RRULE''' if rrule: r = rrule.replace('RRULE:', '') if 'COUNT' not in r and 'UNTIL' not in r: r += ';COUNT=52' event['recurrence'] = r print(json.dumps(event, ensure_ascii=False)) ") ``` #### Owner and resolved identifier interpolation at lines 146–151 ```bash ATTENDEES_JSON=$(python3 -c " import json owner = '$DEFAULT_OWNER_OPEN_ID' extras = [x for x in '$EXTRA_OPEN_IDS'.split(',') if x] all_ids = list(dict.fromkeys([owner] + extras)) print(json.dumps({'attendees': [{'type':'user','user_id':uid} for uid in all_ids]})) ") ``` ### Technical Analysis The script constructs Python programs using `python3 -c` and directly inserts shell-variable contents into the Python source. These values are not encoded as Python string literals and are not passed through a structured interface such as command-line arguments or standard input. The following values reach generated Python source: - `START_TIME`, supplied through `--start` - `MEETING_TOPIC`, supplied as the first positional argument - `RRULE`, supplied through `--rrule` - `DEFAULT_OWNER_OPEN_ID`, obtained from configuration - `EXTRA_OPEN_IDS`, derived from the Feishu API res ...[truncated 2486 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Never interpolate variable contents into Python source code.** Pass every dynamic value as a positional argument: ```bash START_TS=$(python3 - "$START_TIME" <<'PY' from datetime import datetime import sys value = sys.argv[1] print(int(datetime.strptime(value, "%Y-%m-%d %H:%M").timestamp())) PY ) ``` 2. **Construct the event payload from command-line arguments or standard input:** ```bash EVENT_JSON=$(python3 - "$MEETING_TOPIC" "$START_TS" "$END_TS" "$RRULE" <<'PY' import json import sys topic, start_ts, end_ts, rrule = sys.argv[1:] event = { "summary": topic, "start_time": { "timestamp": start_ts, "timezone": "Asia/Shanghai", }, "end_time": { "timestamp": end_ts, "timezone": "Asia/Shanghai", }, "vchat": {"vc_type": "vc"}, "attendee_ability": "can_see_others", } if rrule: recurrence = rrule.removeprefix("RRULE:") if "COUNT" not in recurrence and "UNTIL" not in recurrence: recurrence += ";COUNT=52" event["recurrence"] = recurrence print(json.dumps(event, ensure_ascii=False)) PY ) ``` 3. **Construct attendee data using positional arguments rather than generated source:** ```bash ATTENDEES_JSON=$(python3 - "$DEFAULT_OWNER_OPEN_ID" "$EXTRA_OPEN_IDS" <<'PY' import json import sys owner = sys.argv[1] extras = [value for value in sys.argv[2].split(",") if value] all_ids = list(dict.fromkeys([owner, *extras])) print(json.dumps({ "attendees": [ {"type": "user", "user_id": user_id} for user_id in all_ids ] })) PY ) ``` 4. Validate all inputs before use: - Require `DURATION` to be a bounded positive integer. - Parse start times using one strict, documented format. - Validate RRULE fields against an allowlist of supported RFC 5545 components. - Apply reasonable maximum lengths to topics and invitee lists. - Validate Feishu Open IDs and calendar IDs against their expected formats. 5. Run the Skill as an unp ...[truncated 459 chars]
