Skip to content

Commit 5692031

Browse files
committed
Release v1.8.85 — cross-subsystem hardening pass
Cross-subsystem audit + hardening across recently-shipped v1.8.75-84 slices and load-bearing privacy / build / CI infrastructure. See RELEASE_NOTES_v1.8.85.md for the full per-fix breakdown. P0 fixes: - verifyNoInternetPermission now scans merged manifests + honours tools:node="remove" (closes library-AAR escape hatch). - HardwareKeyboardRuntimeMapper layout map is now thread-safe; AltGr (Ctrl+Alt) no longer dropped. - Sticker palette BitmapFactory.decodeStream gains bounds gate + downsample (defends IME process against 100k x 100k OOM). - ZipUtils.unzip adds pre-canonical entry-name guard + 10k entry-count cap. - CI workflows (android.yml, crowdin-upload.yml, reproducible-build.yml) pinned to read-only GITHUB_TOKEN at file scope. - validate-strings-no-translations.yml stops interpolating untrusted PR data into shell run: blocks. P1 fixes: - Android 12+ data_extraction_rules.xml ships correct schema with explicit excludes for SQLCipher dictionary DB and Tink-wrapped passphrase prefs (closes D2D-transfer leak). - Sticker MIME-type spoof closed (SAF declared MIME is now source of truth). - Addon enumerator no longer rejects legitimate 64MB+ asset packs (was conflating APK file size with bundle size). - verify-reproducible-apk.sh entry-manifest pass criterion replaces cmp -s on signed APKs (matches F-Droid rebuilder methodology). This release intentionally deviates from AGENTS.md §6 (one logical change per PR) at the maintainer's request for the audit pass. Future per-feature work returns to the per-release file pattern. Unverified on this dev VM (no JDK / Android SDK). Maintainer must run the AGENTS.md §5 Definition-of-Done verification commands listed at the end of RELEASE_NOTES_v1.8.85.md before tagging and pushing.
1 parent 1920cb6 commit 5692031

15 files changed

Lines changed: 684 additions & 73 deletions

.github/workflows/android.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ on:
2525
# so it is implicitly re-checked by both lintDebug and assembleDebug below; we also
2626
# call it explicitly first so a contract violation fails fast before slower tasks.
2727

28+
# Lock the default GITHUB_TOKEN to read-only. Build jobs that only need to
29+
# upload artefacts and report status do not need write access to issues,
30+
# pull-requests, packages, or contents. A future supply-chain compromise of a
31+
# transitive action dependency cannot abuse the token to push commits or
32+
# comment on issues.
33+
permissions:
34+
contents: read
35+
2836
jobs:
2937
build:
3038
runs-on: ubuntu-latest

.github/workflows/crowdin-upload.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ on:
77
- "app/src/main/res/values/strings.xml"
88
- ".github/workflows/crowdin-upload.yml"
99

10+
# Default GITHUB_TOKEN limited to read-only. This workflow uses a separate
11+
# Crowdin personal token (env FSEC_CROWDIN_PERSONAL_TOKEN); restricting the
12+
# default token blocks a malicious / compromised third-party action from
13+
# pushing to the repo via the workflow's own token. The Crowdin token
14+
# remains potent — pin `crowdin/github-action` to a SHA in a follow-up.
15+
permissions:
16+
contents: read
17+
1018
jobs:
1119
upload-to-crowdin:
1220
runs-on: ubuntu-latest

.github/workflows/reproducible-build.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ concurrency:
3737
group: reproducible-apk-${{ github.ref }}
3838
cancel-in-progress: true
3939

40+
# Default GITHUB_TOKEN limited to read-only — this job only checks out
41+
# source, builds, and uploads artefacts. Restricting the token closes the
42+
# blast radius if a transitive action dependency is compromised.
43+
permissions:
44+
contents: read
45+
4046
jobs:
4147
verify:
4248
runs-on: ubuntu-latest
Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
name: Validate no translated strings.xml included
22

3+
# SECURITY: `pull_request_target` runs in the base-repo context with write
4+
# tokens. Every `${{ github.event.* }}` and step-output value derived from
5+
# untrusted PR data (login, filenames, PR body, branch name) MUST be passed
6+
# via `env:` and referenced as a quoted shell variable. NEVER interpolate
7+
# `${{ ... }}` directly into `run:` script bodies — that is GitHub Actions'
8+
# canonical script-injection sink.
9+
310
on:
411
pull_request_target:
512
branches: [ main ]
613

14+
# Restrict the default token to the absolute minimum this workflow needs.
15+
permissions:
16+
contents: read
17+
pull-requests: write
18+
719
jobs:
820
validate:
921
runs-on: ubuntu-latest
10-
permissions:
11-
contents: read
12-
pull-requests: write
1322
steps:
1423
- name: Precheck if validation is required
1524
id: precheck
25+
env:
26+
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
1627
run: |
17-
pr_author="${{ github.event.pull_request.user.login }}"
18-
if [[ "$pr_author" == "florisboard-bot" ]]; then
28+
set -euo pipefail
29+
if [[ "$PR_AUTHOR" == "florisboard-bot" ]]; then
1930
echo "PR is by florisboard-bot, skipping validation!"
2031
echo "require_validation=false" >> "$GITHUB_OUTPUT"
2132
else
@@ -26,33 +37,40 @@ jobs:
2637
- name: Fetch PR changed files manually
2738
id: fetch_changed_files
2839
if: steps.precheck.outputs.require_validation == 'true'
40+
env:
41+
REPO: ${{ github.repository }}
42+
PR_NUMBER: ${{ github.event.pull_request.number }}
43+
GH_TOKEN: ${{ github.token }}
2944
run: |
30-
pr_info="$(curl -sSf https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }})" || exit 11
31-
num_changed_files="$(jq -r '.changed_files' <<< "$pr_info")" || exit 12
32-
num_pages=$(((num_changed_files + 99) / 100))
33-
changed_files=""
34-
echo "Num changed files: $num_changed_files ($num_pages page(s))"
35-
for ((page=1; page<=num_pages; page++)); do
36-
pr_files="$(curl -sSf https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files?per_page=100\&page=$page)" || exit 11
37-
changed_files_iter="$(jq -r '.[].filename' <<< "$pr_files")" || exit 12
38-
changed_files+="$changed_files_iter"
39-
done
40-
echo "Changed files:\n$changed_files"
41-
illegal_changes_list="$(grep -E '^app/src/main/res/values-.+/strings.xml$' <<< "$changed_files")" || true
45+
set -euo pipefail
46+
# Use `gh api` which handles auth + pagination cleanly and never
47+
# interpolates the response into the shell command line.
48+
changed_files="$(gh api --paginate "repos/${REPO}/pulls/${PR_NUMBER}/files" --jq '.[].filename')"
49+
echo "Changed files:"
50+
echo "$changed_files"
51+
illegal_changes_list="$(printf '%s\n' "$changed_files" | grep -E '^app/src/main/res/values-.+/strings\.xml$' || true)"
4252
if [ -n "$illegal_changes_list" ]; then
43-
echo -e "Illegal changes detected:\n$illegal_changes_list"
53+
printf 'Illegal changes detected:\n%s\n' "$illegal_changes_list"
4454
else
4555
echo "No illegal changes detected"
4656
fi
47-
echo "illegal_changes_list<<EOF" >> "$GITHUB_OUTPUT"
48-
echo "$illegal_changes_list" >> "$GITHUB_OUTPUT"
49-
echo "EOF" >> "$GITHUB_OUTPUT"
57+
{
58+
echo "illegal_changes_list<<EOF"
59+
echo "$illegal_changes_list"
60+
echo "EOF"
61+
} >> "$GITHUB_OUTPUT"
5062
5163
- name: Create comment if illegal files detected
64+
# TODO supply-chain: pin to a SHA before next release; the SHA above
65+
# was not independently verified at edit time, so the floating tag
66+
# is kept for now. `gh api repos/peter-evans/create-or-update-comment/git/refs/tags/v4`
67+
# will return the canonical commit SHA to substitute here.
5268
uses: peter-evans/create-or-update-comment@v4
5369
if: steps.precheck.outputs.require_validation == 'true' && steps.fetch_changed_files.outputs.illegal_changes_list != ''
5470
with:
5571
issue-number: ${{ github.event.pull_request.number }}
72+
# Markdown-fenced — even if filenames contain backticks they cannot
73+
# escape the fence into surrounding markdown commands.
5674
body: |
5775
⚠️ Illegal changes detected
5876
@@ -67,4 +85,9 @@ jobs:
6785
6886
- name: Fail workflow if illegal files detected
6987
if: steps.precheck.outputs.require_validation == 'true' && steps.fetch_changed_files.outputs.illegal_changes_list != ''
70-
run: echo -e "Illegal changes detected:\n${{ steps.fetch_changed_files.outputs.illegal_changes_list }}" && exit 1
88+
env:
89+
ILLEGAL_CHANGES: ${{ steps.fetch_changed_files.outputs.illegal_changes_list }}
90+
run: |
91+
set -euo pipefail
92+
printf 'Illegal changes detected:\n%s\n' "$ILLEGAL_CHANGES" >&2
93+
exit 1

0 commit comments

Comments
 (0)