Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ runs:
PP_POST_COMMENT: ${{ inputs.post-comment }}
PP_REPOSITORY: ${{ github.repository }}
PP_PR_NUMBER: ${{ github.event.pull_request.number }}
# Through env, never interpolated into the script: a PR title is attacker-controlled text.
PP_PR_TITLE: ${{ github.event.pull_request.title }}
PP_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PP_PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PP_MODE: ${{ inputs.mode }}
Expand Down Expand Up @@ -404,6 +406,7 @@ runs:
--argjson failOnMissing "$FAIL_ON_MISSING_JSON" \
--argjson protectedPathsChanged "$PROTECTED_PATHS_CHANGED" \
--argjson changedFiles "$CHANGED_FILES_JSON" \
--arg prTitle "${PP_PR_TITLE:-}" \
'{
scope: {
repo: $repo,
Expand All @@ -416,7 +419,15 @@ runs:
failOnMissing: $failOnMissing,
protectedPathsChanged: $protectedPathsChanged,
changedFiles: $changedFiles
}')
}
# The PR title, so the approval page has one even when PP cannot
# read the pull request itself. PP rejects null and more than 300
# UTF-16 units, so the key is left out or cut: 300 code points when
# every one is a single unit, else 150 (at most two units each). An
# over-long title must not fail the gate closed.
+ (if ($prTitle | length) == 0 then {}
elif ([$prTitle | explode[] | select(. > 65535)] | length) == 0 then { prTitle: $prTitle[0:300] }
else { prTitle: $prTitle[0:150] } end)')

echo ""
echo "═══════════════════════════════════════════════════════════"
Expand Down
21 changes: 20 additions & 1 deletion test/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ run_case() {
export PP_REDEEM= PP_FAIL_ON_MISSING=true PP_PROTECTED_PATHS='^(deploy/|\.github/workflows/)'
export PP_FAIL_OPEN_TIMEOUT=30 PP_FAIL_MODE=closed PP_PRODUCTION_ENVIRONMENTS=production,prod,live
export PP_POST_COMMENT=false PP_REPOSITORY=Acme/Web
export PP_PR_NUMBER= PP_PR_HEAD_SHA= PP_PR_BASE_SHA=
export PP_PR_NUMBER= PP_PR_HEAD_SHA= PP_PR_BASE_SHA= PP_PR_TITLE=
export PP_MODE=auto PP_EVENT_NAME=push PP_SHA=mergesha0000000000 PP_REF=refs/heads/main
export PP_RUN_ID=4242 GH_TOKEN=ghs_test
for assignment in "$@"; do export "$assignment"; done
Expand Down Expand Up @@ -130,6 +130,7 @@ check "scope" "$(request .scope)" '{"repo":"acme/web","prNumber":12,"headSha":"a
check "redeem" "$(request .redeem)" true
check "failOnMissing" "$(request .failOnMissing)" true
check "no draft lookup" "$(grep -c isDraft "$CASE_DIR/gh.log")" 0
check "no title outside a PR event" "$(request 'has("prTitle")')" false

PULLS="$MERGED" run_case "workflow_dispatch deploys merged PR" PP_EVENT_NAME=workflow_dispatch <<<"$VALID"
check "exit" "$EXIT_CODE" 0
Expand Down Expand Up @@ -245,6 +246,24 @@ check "no PP call" "$(no_request)" none
run_case "pr gate missing receipt, fail-on-missing false" "${PR_CTX[@]}" PP_FAIL_ON_MISSING=false <<<'{"valid": false, "errorCode": "RECEIPT_NOT_FOUND"}'
check "exit" "$EXIT_CODE" 0

# --- the PR title rides along, so PP can show it without reading GitHub ------
utf16() { jq -r '[.prTitle | explode[] | if . > 65535 then 2 else 1 end] | add' "$CASE_DIR/request.json"; }

run_case "PR title is sent as JSON, quotes and newlines intact" "${PR_CTX[@]}" "PP_PR_TITLE=$(printf 'Fix "auth" redirect\nloop $(id)')" <<<"$VALID"
check "exit" "$EXIT_CODE" 0
check "title" "$(request .prTitle)" '"Fix \"auth\" redirect\nloop $(id)"'
check "scope unchanged" "$(request .scope)" '{"repo":"acme/web","prNumber":12,"headSha":"approvedhead12","env":"production","capability":"deploy:production"}'

run_case "empty PR title is left out" "${PR_CTX[@]}" PP_PR_TITLE= <<<"$VALID"
check "no key" "$(request 'has("prTitle")')" false

run_case "long PR title is cut to 300" "${PR_CTX[@]}" "PP_PR_TITLE=$(printf 'x%.0s' $(seq 1 400))" <<<"$VALID"
check "length" "$(request '.prTitle | length')" 300

run_case "emoji PR title stays within 300 UTF-16 units" "${PR_CTX[@]}" "PP_PR_TITLE=$(for i in $(seq 1 200); do printf '\360\237\232\200'; done)" <<<"$VALID"
check "code points" "$(request '.prTitle | length')" 150
check "utf-16 units" "$(utf16)" 300

echo ""
echo "${PASS} passed, ${FAIL} failed"
[ "$FAIL" -eq 0 ]
Loading