security: 修复 CI/Release workflow 签名凭据风险
- 所有 action 引用改为 commit SHA pin(checkout/setup-java/setup-android/action-gh-release/upload-artifact) - build.gradle.kts 移除硬编码签名密码 fallback,改用 takeIf 防御 null 和空字符串 - release.yml 构建完成后清理 release.jks(if: always()) - ci.yml 补充最小权限声明 permissions: contents: read
This commit is contained in:
parent
172d6b61aa
commit
f203149f4f
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
@ -5,17 +5,20 @@ on:
|
|||||||
branches: [main]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Unit Tests & Lint
|
name: Unit Tests & Lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
- uses: android-actions/setup-android@v3
|
- uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
|
||||||
with:
|
with:
|
||||||
packages: 'platforms;android-35 build-tools;35.0.0'
|
packages: 'platforms;android-35 build-tools;35.0.0'
|
||||||
|
|
||||||
@ -27,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: unit-test-results
|
name: unit-test-results
|
||||||
path: app/build/test-results/testDebugUnitTest/*.xml
|
path: app/build/test-results/testDebugUnitTest/*.xml
|
||||||
@ -38,7 +41,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload lint results
|
- name: Upload lint results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: lint-results
|
name: lint-results
|
||||||
path: app/build/reports/lint-results-debug.*
|
path: app/build/reports/lint-results-debug.*
|
||||||
@ -49,12 +52,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
- uses: android-actions/setup-android@v3
|
- uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
|
||||||
with:
|
with:
|
||||||
packages: 'platforms;android-35 build-tools;35.0.0'
|
packages: 'platforms;android-35 build-tools;35.0.0'
|
||||||
|
|
||||||
@ -65,7 +68,7 @@ jobs:
|
|||||||
run: ./gradlew assembleDebug
|
run: ./gradlew assembleDebug
|
||||||
|
|
||||||
- name: Upload Debug APK
|
- name: Upload Debug APK
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: debug-apk
|
name: debug-apk
|
||||||
path: app/build/outputs/apk/debug/app-debug.apk
|
path: app/build/outputs/apk/debug/app-debug.apk
|
||||||
@ -76,12 +79,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
- uses: android-actions/setup-android@v3
|
- uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
|
||||||
with:
|
with:
|
||||||
packages: 'platforms;android-35 build-tools;35.0.0'
|
packages: 'platforms;android-35 build-tools;35.0.0'
|
||||||
|
|
||||||
@ -119,7 +122,7 @@ jobs:
|
|||||||
echo "✅ APK integrity verified: manifest + arsc + res/ all present"
|
echo "✅ APK integrity verified: manifest + arsc + res/ all present"
|
||||||
|
|
||||||
- name: Upload Release APK
|
- name: Upload Release APK
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
name: release-apk
|
name: release-apk
|
||||||
path: app/build/outputs/apk/release/app-release.apk
|
path: app/build/outputs/apk/release/app-release.apk
|
||||||
|
|||||||
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@ -10,12 +10,12 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
- uses: android-actions/setup-android@v3
|
- uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
|
||||||
with:
|
with:
|
||||||
packages: 'platforms;android-35 build-tools;35.0.0'
|
packages: 'platforms;android-35 build-tools;35.0.0'
|
||||||
- name: Decode Keystore
|
- name: Decode Keystore
|
||||||
@ -28,10 +28,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
chmod +x gradlew
|
chmod +x gradlew
|
||||||
./gradlew assembleRelease
|
./gradlew assembleRelease
|
||||||
|
- name: Cleanup keystore (always run for security)
|
||||||
|
run: rm -f release.jks
|
||||||
|
if: always()
|
||||||
- name: Rename & Release
|
- name: Rename & Release
|
||||||
run: |
|
run: |
|
||||||
cp app/build/outputs/apk/release/app-release.apk "RainyToken-${GITHUB_REF#refs/tags/}.apk"
|
cp app/build/outputs/apk/release/app-release.apk "RainyToken-${GITHUB_REF#refs/tags/}.apk"
|
||||||
- uses: softprops/action-gh-release@v2
|
- uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
||||||
with:
|
with:
|
||||||
name: "🎉 RainyToken ${{ github.ref_name }}"
|
name: "🎉 RainyToken ${{ github.ref_name }}"
|
||||||
files: RainyToken-${{ github.ref_name }}.apk
|
files: RainyToken-${{ github.ref_name }}.apk
|
||||||
|
|||||||
@ -32,9 +32,18 @@ android {
|
|||||||
val keystoreFile = rootProject.file("release.jks")
|
val keystoreFile = rootProject.file("release.jks")
|
||||||
if (keystoreFile.exists()) {
|
if (keystoreFile.exists()) {
|
||||||
storeFile = keystoreFile
|
storeFile = keystoreFile
|
||||||
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: "RainyToken2026!"
|
// 凭据必须通过环境变量注入;缺失或为空时直接报错,不提供任何隐式 fallback
|
||||||
keyAlias = System.getenv("KEYSTORE_ALIAS") ?: "rainy"
|
// 注意:GitHub Actions 中未设置的 secret 会被替换为空字符串(非 null),
|
||||||
keyPassword = System.getenv("KEY_PASSWORD") ?: "RainyToken2026!"
|
// 因此用 takeIf { isNotBlank() } 同时防御 null 和空字符串
|
||||||
|
storePassword = System.getenv("KEYSTORE_PASSWORD")
|
||||||
|
?.takeIf { it.isNotBlank() }
|
||||||
|
?: throw GradleException("KEYSTORE_PASSWORD env var not set or empty — cannot sign release")
|
||||||
|
keyAlias = System.getenv("KEYSTORE_ALIAS")
|
||||||
|
?.takeIf { it.isNotBlank() }
|
||||||
|
?: throw GradleException("KEYSTORE_ALIAS env var not set or empty — cannot sign release")
|
||||||
|
keyPassword = System.getenv("KEY_PASSWORD")
|
||||||
|
?.takeIf { it.isNotBlank() }
|
||||||
|
?: throw GradleException("KEY_PASSWORD env var not set or empty — cannot sign release")
|
||||||
} else {
|
} else {
|
||||||
// 仅在 CI 环境中 fallback 到 debug keystore(用于编译/资源完整性验证)
|
// 仅在 CI 环境中 fallback 到 debug keystore(用于编译/资源完整性验证)
|
||||||
// 本地构建缺少 release.jks 时直接报错,避免静默生成 debug 签名的 Release APK
|
// 本地构建缺少 release.jks 时直接报错,避免静默生成 debug 签名的 Release APK
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user