Skip to content

feat(core): extend FrameworkDetector to support split APKs#14

Open
luca-regne wants to merge 11 commits intomainfrom
feat/split-apk-framework-detection
Open

feat(core): extend FrameworkDetector to support split APKs#14
luca-regne wants to merge 11 commits intomainfrom
feat/split-apk-framework-detection

Conversation

@luca-regne
Copy link
Copy Markdown
Owner

Summary

  • FrameworkDetector now accepts list[Path] and aggregates file listings across all APK parts before signature matching — frameworks whose libs/assets live in split config APKs (e.g. split_config.arm64_v8a.apk) are now correctly detected without requiring a full decompile
  • batuta analyze framework accepts a file or a split APK directory; when given a directory it globs *.apk and scans all parts together
  • batuta apk pull now runs a lightweight framework scan (ZIP listing only, non-fatal) immediately after pulling and reports detected frameworks in both Rich and JSON output

Motivation

Split APKs distribute their content across multiple parts — native .so libraries typically land in ABI-specific splits rather than base.apk. The old single-path detector would miss these signatures entirely. This fix lets you identify the framework before committing to a full decompile.

$ batuta apk pull com.example.flutterapp
✓ Pulled to ./com.example.flutterapp/
  3 APK files: base.apk, split_config.arm64_v8a.apk, split_config.en.apk
  Framework: Flutter        ← new

$ batuta analyze framework ./com.example.flutterapp/
Scanned 3 APK parts
Frameworks Detected:
  Flutter
    - lib/arm64-v8a/libflutter.so
    - assets/flutter_assets/

Test plan

  • batuta analyze framework ./single.apk — single file path still works
  • batuta analyze framework ./split-dir/ — scans all .apk files in dir
  • batuta analyze framework ./empty-dir/ — prints error, exits 1
  • batuta apk pull <flutter-app> — prints Framework: Flutter after pull
  • batuta apk pull <native-app> --json — output includes "frameworks": [...] when detected
  • uv run ruff check src/batuta/ passes
  • uv run mypy src/batuta/ passes

🤖 Generated with Claude Code

Comment on lines +9 to +19
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Validate commit messages
uses: wagoid/commitlint-github-action@v5
with:
configFile: .commitlintrc.json

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 4 days ago

In general, this problem is fixed by explicitly setting a permissions block in the workflow or job to restrict the GITHUB_TOKEN to the least privileges required (typically contents: read for read‑only workflows). For this specific commit‑lint workflow, the job only needs to read commits and configuration; it does not need to write to the repo, create issues, or modify PRs. Therefore we can add a job‑level permissions block under lint-commits: specifying contents: read. This keeps existing behavior (actions/checkout and commitlint can still read the repository) while preventing any unintended write operations via the token.

Concretely, edit .github/workflows/commit-lint.yml in the lint-commits job definition. Immediately under lint-commits: (before runs-on:) add:

    permissions:
      contents: read

No imports or additional methods are needed, as this is pure workflow configuration.

Suggested changeset 1
.github/workflows/commit-lint.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/commit-lint.yml b/.github/workflows/commit-lint.yml
--- a/.github/workflows/commit-lint.yml
+++ b/.github/workflows/commit-lint.yml
@@ -6,6 +6,8 @@
 
 jobs:
   lint-commits:
+    permissions:
+      contents: read
     runs-on: ubuntu-latest
     steps:
       - name: Checkout
EOF
@@ -6,6 +6,8 @@

jobs:
lint-commits:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- name: Checkout
Copilot is powered by AI and may make mistakes. Always verify output.
fetch-depth: 0

- name: Validate commit messages
uses: wagoid/commitlint-github-action@v5

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Commit Lint' step
Uses Step
uses 'wagoid/commitlint-github-action' with ref 'v5', not a pinned commit hash
Comment on lines +10 to +48
name: Pre-release Validation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Install Python 3.13
run: uv python install 3.13

- name: Install dependencies
run: uv sync --group dev

- name: Lint with ruff
run: uv run ruff check src/batuta/

- name: Type check with mypy
run: uv run mypy src/batuta/

- name: Build package
run: uv build

- name: Verify installation
run: |
uv venv .test-venv
source .test-venv/bin/activate
uv pip install dist/*.whl
batuta --help
deactivate

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 4 days ago

In general, the fix is to add an explicit permissions block that grants only the minimal required scopes to the GITHUB_TOKEN. For the validate job, the steps only need to read the repository contents (for checkout and analysis) and do not push commits, create releases, or interact with issues/PRs, so contents: read is sufficient.

The best minimal fix, without changing functionality, is to add a permissions block under the validate job (since the publish job already has its own permissions). This block should be placed at the same indentation level as runs-on and steps. A suitable configuration is:

permissions:
  contents: read

No new imports, methods, or definitions are needed; this is purely a YAML configuration change in .github/workflows/publish.yml, specifically adding the permissions section after runs-on: ubuntu-latest (line 11) and before steps: for the validate job.

Suggested changeset 1
.github/workflows/publish.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -9,6 +9,8 @@
   validate:
     name: Pre-release Validation
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     steps:
       - name: Checkout
         uses: actions/checkout@v6
EOF
@@ -9,6 +9,8 @@
validate:
name: Pre-release Validation
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
Copilot is powered by AI and may make mistakes. Always verify output.
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Publish' step
Uses Step
uses 'astral-sh/setup-uv' with ref 'v7', not a pinned commit hash
path: dist/

- name: Install uv
uses: astral-sh/setup-uv@v7

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Publish' step
Uses Step
uses 'astral-sh/setup-uv' with ref 'v7', not a pinned commit hash
git cliff --tag "$VERSION" --unreleased --strip header > release-notes.md

- name: Create GitHub Release
uses: softprops/action-gh-release@v2

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Publish' step
Uses Step
uses 'softprops/action-gh-release' with ref 'v2', not a pinned commit hash
FrameworkDetector now accepts a list of APK paths and aggregates file
listings across all parts before signature matching, so frameworks whose
libs or assets live in split config APKs (e.g. split_config.arm64_v8a.apk)
are correctly detected without requiring a full decompile.

- models/analyze.py: apk_path → apk_paths (list[Path])
- core/analyzer.py: constructor takes list[Path]; detect() iterates all
  paths and extends a combined namelist before running _detect_frameworks
- cli/analyze.py: 'analyze framework' now accepts a file or directory;
  when given a directory it globs *.apk and passes all parts to the detector
- cli/apk.py: 'apk pull' runs a lightweight framework scan after pulling
  (ZIP listing only, non-fatal) and reports detected frameworks in both
  Rich and JSON output

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@luca-regne luca-regne force-pushed the feat/split-apk-framework-detection branch from e027ae3 to f027e8f Compare April 1, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants