T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/upnote.sh:59
- Finding
- Custom URL Query Parameter Injection Through Unencoded User Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upnote.sh:59`, `scripts/upnote.sh:74`, `scripts/upnote.sh:100`, and `scripts/upnote.sh:109-118` **Vulnerability Type**: Custom URL query parameter injection **Risk Level**: Medium ### Vulnerable Code ```bash open) shift NOTE_ID="$1" NEW_WINDOW="${2:-false}" if [[ -z "$NOTE_ID" ]]; then echo "Usage: upnote open <noteId> [true|false]" exit 1 fi open "upnote://x-callback-url/openNote?noteId=$NOTE_ID&new_window=$NEW_WINDOW" ;; ``` ```bash open) shift NOTEBOOK_ID="$1" if [[ -z "$NOTEBOOK_ID" ]]; then echo "Usage: upnote notebook open <notebookId>" exit 1 fi open "upnote://x-callback-url/openNotebook?notebookId=$NOTEBOOK_ID" ;; ``` ```bash filter) shift FILTER_ID="$1" if [[ -z "$FILTER_ID" ]]; then echo "Usage: upnote filter <filterId>" exit 1 fi open "upnote://x-callback-url/openFilter?filterId=$FILTER_ID" ;; ``` ```bash view) shift MODE="$1" URL="upnote://x-callback-url/view?mode=$MODE" shift while [[ $# -gt 0 ]]; do case $1 in --note-id) URL+="¬eId=$2"; shift 2 ;; --notebook-id) URL+="¬ebookId=$2"; shift 2 ;; --tag-id) URL+="&tagId=$2"; shift 2 ;; --filter-id) URL+="&filterId=$2"; shift 2 ;; --space-id) URL+="&spaceId=$2"; shift 2 ;; --query) URL+="&action=search&query=$(urlencode "$2")"; shift 2 ;; *) echo "Unknown option: $1"; exit 1 ;; esac done open "$URL" ;; ``` ### Technical Analysis The script constructs `upnote://x-callback-url` URLs by directly concatenating several user-controlled values into the query string. The affected values include: - Note IDs - Notebook IDs - Filter IDs - Tag IDs - Space IDs - View modes - The `new_window` argument Unlike note titles, note content, notebook names, tag names, and search queries, the ...[truncated 2776 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. URL-encode every dynamic query parameter before adding it to a callback URL: ```bash open "upnote://x-callback-url/openNote?noteId=$(urlencode "$NOTE_ID")&new_window=$(urlencode "$NEW_WINDOW")" open "upnote://x-callback-url/openNotebook?notebookId=$(urlencode "$NOTEBOOK_ID")" open "upnote://x-callback-url/openFilter?filterId=$(urlencode "$FILTER_ID")" ``` 2. Apply the same protection to all view parameters: ```bash URL="upnote://x-callback-url/view?mode=$(urlencode "$MODE")" case $1 in --note-id) [[ $# -ge 2 ]] || { echo "Missing value for --note-id"; exit 1; } URL+="¬eId=$(urlencode "$2")" shift 2 ;; --notebook-id) [[ $# -ge 2 ]] || { echo "Missing value for --notebook-id"; exit 1; } URL+="¬ebookId=$(urlencode "$2")" shift 2 ;; --tag-id) [[ $# -ge 2 ]] || { echo "Missing value for --tag-id"; exit 1; } URL+="&tagId=$(urlencode "$2")" shift 2 ;; --filter-id) [[ $# -ge 2 ]] || { echo "Missing value for --filter-id"; exit 1; } URL+="&filterId=$(urlencode "$2")" shift 2 ;; --space-id) [[ $# -ge 2 ]] || { echo "Missing value for --space-id"; exit 1; } URL+="&spaceId=$(urlencode "$2")" shift 2 ;; esac ``` 3. Validate view modes against the documented allowlist: ```bash case "$MODE" in all_notes|quick_access|templates|trash|notebooks|tags|filters|all_notebooks|all_tags) ;; *) echo "Invalid view mode: $MODE" exit 1 ;; esac ``` 4. Restrict boolean arguments to accepted values: ```bash case "$NEW_WINDOW" in true|false) ;; *) echo "new_window must be true or false" exit 1 ;; esac ``` 5. Add regression tests using values containing `&`, `=`, `#`, spaces, percent signs, Unicode characters, and missing option arguments. Verify that each supplied value remains confined to exactl ...[truncated 24 chars]
