Skip to content

2.0 - #9

Merged
Mzdyl merged 19 commits into
mainfrom
2.0
Feb 25, 2026
Merged

2.0#9
Mzdyl merged 19 commits into
mainfrom
2.0

Conversation

@Mzdyl

@Mzdyl Mzdyl commented Feb 24, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

将 Magisk 模块重构为单一统一的 CSC 工具流水线,并集成 WebUI,用以取代旧版原生工具,同时相应更新构建与安装流程。

New Features:

  • 引入统一的 csc_tool 可执行文件,用于处理 CSC/浮动特性(floating-feature)的解码、编码,以及 JSON/XML 补丁应用,并支持可选的调试日志。
  • 新增基于 React 的 KernelSU WebUI,用于浏览、编辑和切换存储在 JSON 配置中的 CSC、运营商(carrier)和浮动特性条目。
  • 提供 CSC、运营商和浮动特性设置的默认 JSON 配置文件,在安装时初始化到共享数据目录中。

Enhancements:

  • 重写 post-fs-data.sh,采用结构化、带日志的工作流,动态定位 CSC 文件,并通过新的 csc_tool 应用补丁,同时实现更安全的绑定挂载(bind mount)和 SELinux 上下文恢复。
  • 通过在 /data/adb/csc_config 下管理配置文件并设置合适的权限来简化模块自定义流程,并提供更清晰的面向用户的安装提示信息。
  • 使用基于 NDK 的核心模块统一原生构建脚本,并调整 WebUI 的构建输出路径以匹配新的项目结构。

Build:

  • 更新 GitHub Actions 中的 Magisk 工作流,从 src/core 构建 WebUI 和新的核心 csc_tool 可执行文件,并相应调整分支与制品(artifact)内容。
Original summary in English

Summary by Sourcery

Refactor the Magisk module into a single unified CSC tooling pipeline with an integrated WebUI, replacing the legacy native utilities and updating build and installation workflows accordingly.

New Features:

  • Introduce a unified csc_tool binary that handles CSC/floating-feature decoding, encoding, and JSON/XML patching with optional debug logging.
  • Add a React-based KernelSU WebUI for browsing, editing, and toggling CSC, carrier, and floating-feature entries stored in JSON configs.
  • Provide default JSON configuration files for CSC, carrier, and floating-feature settings that are initialized into a shared data directory on install.

Enhancements:

  • Rewrite post-fs-data.sh to use a structured, logged workflow that dynamically locates CSC files and applies patches via the new csc_tool, with safer bind mounts and SELinux context restoration.
  • Simplify module customization by managing configuration files under /data/adb/csc_config and setting appropriate permissions, with clearer user-facing install messages.
  • Unify native build scripts using an NDK-based core module and adjust WebUI build output paths to match the new project layout.

Build:

  • Update the GitHub Actions Magisk workflow to build the WebUI and the new core csc_tool binary from src/core, adjusting branches and artifact contents.
Original summary in English

Summary by Sourcery

将 Magisk 模块重构为单一统一的 CSC 工具流水线,并集成 WebUI,用以取代旧版原生工具,同时相应更新构建与安装流程。

New Features:

  • 引入统一的 csc_tool 可执行文件,用于处理 CSC/浮动特性(floating-feature)的解码、编码,以及 JSON/XML 补丁应用,并支持可选的调试日志。
  • 新增基于 React 的 KernelSU WebUI,用于浏览、编辑和切换存储在 JSON 配置中的 CSC、运营商(carrier)和浮动特性条目。
  • 提供 CSC、运营商和浮动特性设置的默认 JSON 配置文件,在安装时初始化到共享数据目录中。

Enhancements:

  • 重写 post-fs-data.sh,采用结构化、带日志的工作流,动态定位 CSC 文件,并通过新的 csc_tool 应用补丁,同时实现更安全的绑定挂载(bind mount)和 SELinux 上下文恢复。
  • 通过在 /data/adb/csc_config 下管理配置文件并设置合适的权限来简化模块自定义流程,并提供更清晰的面向用户的安装提示信息。
  • 使用基于 NDK 的核心模块统一原生构建脚本,并调整 WebUI 的构建输出路径以匹配新的项目结构。

Build:

  • 更新 GitHub Actions 中的 Magisk 工作流,从 src/core 构建 WebUI 和新的核心 csc_tool 可执行文件,并相应调整分支与制品(artifact)内容。
Original summary in English

Summary by Sourcery

Refactor the Magisk module into a single unified CSC tooling pipeline with an integrated WebUI, replacing the legacy native utilities and updating build and installation workflows accordingly.

New Features:

  • Introduce a unified csc_tool binary that handles CSC/floating-feature decoding, encoding, and JSON/XML patching with optional debug logging.
  • Add a React-based KernelSU WebUI for browsing, editing, and toggling CSC, carrier, and floating-feature entries stored in JSON configs.
  • Provide default JSON configuration files for CSC, carrier, and floating-feature settings that are initialized into a shared data directory on install.

Enhancements:

  • Rewrite post-fs-data.sh to use a structured, logged workflow that dynamically locates CSC files and applies patches via the new csc_tool, with safer bind mounts and SELinux context restoration.
  • Simplify module customization by managing configuration files under /data/adb/csc_config and setting appropriate permissions, with clearer user-facing install messages.
  • Unify native build scripts using an NDK-based core module and adjust WebUI build output paths to match the new project layout.

Build:

  • Update the GitHub Actions Magisk workflow to build the WebUI and the new core csc_tool binary from src/core, adjusting branches and artifact contents.

@sourcery-ai

sourcery-ai Bot commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

审阅者指南

将 Magisk 模块重构为使用单一原生 csc_tool 和基于 WebUI 的 JSON 配置流程,替换之前多个 NDK 二进制文件和基于文本脚本的方案,并更新 post-fs-data/customize 逻辑以及 CI/构建流水线,以生成新的核心组件和 React WebUI。

新版 post-fs-data CSC 处理流水线的时序图

sequenceDiagram
    participant AndroidInit as AndroidInit
    participant post_fs_data_sh as post_fs_data_sh
    participant csc_tool as csc_tool
    participant FileSystem as FileSystem

    AndroidInit->>post_fs_data_sh: invoke post-fs-data.sh
    activate post_fs_data_sh

    post_fs_data_sh->>post_fs_data_sh: prepare_log()
    post_fs_data_sh->>post_fs_data_sh: read_props(CSC, ARCH)
    post_fs_data_sh->>post_fs_data_sh: compute TOOL path

    post_fs_data_sh->>post_fs_data_sh: process_feature_file(csc, cscfeature.xml, csc.json)
    activate post_fs_data_sh
    post_fs_data_sh->>FileSystem: find_file(cscfeature.xml)
    FileSystem-->>post_fs_data_sh: origin_path
    post_fs_data_sh->>csc_tool: --decode origin_path decoded_csc
    csc_tool-->>post_fs_data_sh: decoded_csc
    post_fs_data_sh->>FileSystem: read CONFIG_PATH/csc.json
    alt user_config exists
        post_fs_data_sh->>csc_tool: --patch decoded_csc user_config patched_csc
        csc_tool-->>post_fs_data_sh: patched_csc
    else user_config missing
        post_fs_data_sh->>post_fs_data_sh: copy decoded_csc to patched_csc
    end
    post_fs_data_sh->>csc_tool: --encode patched_csc final_csc
    csc_tool-->>post_fs_data_sh: final_csc
    post_fs_data_sh->>FileSystem: safe_mount(final_csc, origin_path)
    post_fs_data_sh->>FileSystem: restorecon(origin_path)
    deactivate post_fs_data_sh

    post_fs_data_sh->>post_fs_data_sh: process_feature_file(carrier, customer_carrier_feature.json, carrier.json)
    activate post_fs_data_sh
    post_fs_data_sh->>FileSystem: find_file(customer_carrier_feature.json)
    FileSystem-->>post_fs_data_sh: origin_path
    post_fs_data_sh->>csc_tool: --decode origin_path decoded_carrier
    csc_tool-->>post_fs_data_sh: decoded_carrier
    post_fs_data_sh->>FileSystem: read CONFIG_PATH/carrier.json
    alt user_config exists
        post_fs_data_sh->>csc_tool: --patch decoded_carrier user_config patched_carrier
        csc_tool-->>post_fs_data_sh: patched_carrier
    else user_config missing
        post_fs_data_sh->>post_fs_data_sh: copy decoded_carrier to patched_carrier
    end
    post_fs_data_sh->>csc_tool: --encode patched_carrier final_carrier
    csc_tool-->>post_fs_data_sh: final_carrier
    post_fs_data_sh->>FileSystem: safe_mount(final_carrier, origin_path)
    post_fs_data_sh->>FileSystem: restorecon(origin_path)
    deactivate post_fs_data_sh

    post_fs_data_sh->>post_fs_data_sh: process_feature_file(ff, floating_feature.xml, ff.json)
    activate post_fs_data_sh
    post_fs_data_sh->>FileSystem: find_file(floating_feature.xml)
    alt file_not_found_in_optics
        post_fs_data_sh->>FileSystem: use /etc/floating_feature.xml as origin_path
    end
    post_fs_data_sh->>FileSystem: copy origin_path to decoded_ff
    post_fs_data_sh->>FileSystem: read CONFIG_PATH/ff.json
    alt user_config exists
        post_fs_data_sh->>csc_tool: --patch decoded_ff user_config patched_ff
        csc_tool-->>post_fs_data_sh: patched_ff
    else user_config missing
        post_fs_data_sh->>post_fs_data_sh: copy decoded_ff to patched_ff
    end
    post_fs_data_sh->>FileSystem: safe_mount(patched_ff, origin_path)
    post_fs_data_sh->>FileSystem: restorecon(origin_path)
    deactivate post_fs_data_sh

    deactivate post_fs_data_sh
    post_fs_data_sh-->>AndroidInit: CSC customization ready
Loading

新版 WebUI CSC 编辑器的类型与关系类图

classDiagram
    class App {
      +FilterMode filterMode
      +CSCEntry[] entries
      +Record~string_string~ originEntries
      +void loadData()
      +void syncToDisk(newEntries)
      +void toggleEnable(key)
      +void removeEntry(key)
      +void openModal(key, modified, originValue)
      +void confirmModal()
    }

    class CSCEntry {
      +string command
      +string key
      +string value
      +string desc
      +boolean enabled
    }

    class FilterModeEnum {
      <<enumeration>>
      ALL
      ENABLED
      DISABLED
      UNSET
    }

    class KernelSUExec {
      +Promise~ExecResult~ exec(command)
      +void toast(message)
    }

    class FileConfig {
      +string label
      +string name
      +string decoded
    }

    App --> CSCEntry : manages
    App --> FilterModeEnum : uses
    App --> KernelSUExec : calls_exec_and_toast
    App --> FileConfig : selects_currentFile

    class Paths {
      +string CONFIG_PATH
      +string MODULE_PATH
    }

    App --> Paths : builds_IO_paths
Loading

文件级变更

Change Details Files
重构 post-fs-data 流水线,以统一使用 csc_tool 和 JSON 配置,并支持动态文件发现与安全绑定挂载。
  • 使用结构化辅助函数和按文件的处理例程(通过 csc_tool --decode/--patch/--encode)替换传统的 decode/auto_modify/encode shell 流水线。
  • 增加健壮的日志记录、可选调试模式,以及在 /optics/configs/carriers/$CSC 下的动态 CSC 文件发现,并对 floating_feature.xml 提供回退方案。
  • 引入带预检查与懒卸载的 safe_mount,并在挂载补丁文件后执行 SELinux restorecon
  • 使用 /data/adb/csc_config 下的 JSON 文件(csc.json, carrier.json, ff.json)作为用户补丁输入,通过 csc_tool 处理 XML 与 JSON 格式,对 floating_feature.xml 采用特殊的纯文本处理逻辑。
module_files/post-fs-data.sh
引入统一的原生 csc_tool,使用 C 实现,对 CSC XML 和 carrier JSON 提供自定义编码/解码与补丁功能。
  • csc_tool 新增 Android NDK 工程,使用 csc_tool.c 与捆绑的 cJSON 库,并链接 zlib
  • 实现基于盐值/位移表参数化的自定义混淆 + gzip 编码/解码逻辑。
  • 实现 XML 补丁器,将 FeatureSet/SecFloatingFeatureSet 区块解析为键值对,按 JSON 驱动的 MODIFY/DELETE 操作进行修改,对特性进行排序,并写出新的 XML 文件。
  • 实现 JSON 补丁器,遍历 carrier JSON 中 customer/specific[].feature 对象,应用配置 JSON 中启用的规则,并保持键排序以保证结果确定性。
  • 提供统一 CLI:--decode <in> <out>--encode <in> <out>--patch <target> <config> <out>,自动检测 XML/JSON,并支持可选的 --debug 日志。
src/core/jni/csc_tool.c
src/core/jni/Android.mk
src/core/jni/Application.mk
src/core/jni/lib/cJSON.c
src/core/jni/lib/cJSON.h
通过 customize.sh 引入基于 JSON 的配置初始化流程,替换旧的 NDK 二进制与文本配置工作流。
  • 移除旧的 auto_modify_*decode_csc NDK 源码及其构建脚本,以及示例 ml_*.txt 文件。
  • 修改 customize.sh,为 $MODPATH 与库文件设置合理权限,创建 /data/adb/csc_config,并在目标缺失时从 module_files/configs 迁移初始 csc.json/ff.json/carrier.json
  • 引导用户通过 WebUI 配置特性,而不是编辑示例文本文件,并将配置目录权限放宽为 0777 以便在设备上编辑。
module_files/customize.sh
module_files/configs/csc.json
module_files/configs/ff.json
module_files/configs/carrier.json
scr/auto_modify_cscfeature/jni/Android.mk
scr/auto_modify_cscfeature/jni/Application.mk
scr/auto_modify_cscfeature/jni/auto_modify_cscfeature.c
scr/auto_modify_json_csc/jni/Android.mk
scr/auto_modify_json_csc/jni/Application.mk
scr/auto_modify_json_csc/jni/auto_modify_jsoncsc.c
scr/cscdecode/jni/Android.mk
scr/cscdecode/jni/Application.mk
scr/cscdecode/jni/decode_csc.c
scr/CSC_SORT.py
module_files/ml_carrier.txt.example
module_files/ml_csc.txt.example
module_files/ml_ff.txt.example
新增基于 React 的 WebUI,用于在设备上管理 CSC 配置 JSON,并将其接入构建输出路径。
  • 实现单页 React 应用(App.tsx),可浏览/搜索 CSC 键、显示原始值与修改值,并通过模态框编辑条目;使用 KernelSU 的 exec/toast API 读取/写入 /data/adb/csc_config 下的 JSON 以及模块目录中的解码参考文件。
  • 提供启用/禁用条目、删除覆盖(恢复为原始值)、按状态过滤、在 carrier/csc/ff 配置间切换等 UI 功能。
  • 替换原有 WebUI App 实现,并调整 Parcel 构建输出路径到 ../../module_files/webroot,以便将资源打包进 Magisk 模块。
src/webui/src/App.tsx
src/webui/src/App.css
src/webui/package.json
webui/src/App.tsx
webui/src/App.css
更新 GitHub Actions 工作流以构建 WebUI 和新的 csc_tool,并调整打包触发条件。
  • 修改工作流触发条件,在推送到 main/master/2.0 分支时运行。
  • 新增 Node.js 18 的设置步骤,以及在 src/webui 中执行 npm install && npm run build 的 WebUI 构建步骤,将文件输出到 module_files/webroot
  • src/core 中的单次 ndk-build 构建 csc_tool 并将 libs/* 复制到 module_files/libs,替换之前三个旧的 ndk-build 调用。
  • 在压缩 Magisk 模块前确保移除 .gitkeep 文件,并保持 upload-artifact 步骤仍指向 auto_modify_cscfeature.zip
  • 移除未使用的 apk.yml 工作流。
.github/workflows/magisk.yml
.github/workflows/apk.yml

提示与命令

与 Sourcery 交互

  • 触发新评审: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub Issue: 在评审评论下回复请求 Sourcery 创建 Issue。也可以直接回复 @sourcery-ai issue 来从该评论创建 Issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题中任意位置写入 @sourcery-ai 以在任意时间生成标题。也可以在 Pull Request 中评论 @sourcery-ai title 以(重新)生成标题。
  • 生成 Pull Request 总结: 在 Pull Request 正文中任意位置写入 @sourcery-ai summary,即可在对应位置生成 PR 总结。也可以在 Pull Request 中评论 @sourcery-ai summary 以在任意时间(重新)生成总结。
  • 生成审阅者指南: 在 Pull Request 中评论 @sourcery-ai guide 以在任意时间(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve 以标记所有 Sourcery 评论为已解决。如果你已经处理完所有评论并不希望再看到它们,这会很有用。
  • 撤销所有 Sourcery 评审: 在 Pull Request 中评论 @sourcery-ai dismiss 以撤销所有现有的 Sourcery 评审。如果你想从头开始新的评审,这尤其有用——别忘了再评论 @sourcery-ai review 来触发新评审!

自定义你的体验

访问你的控制台 以:

  • 启用或禁用评审功能,比如 Sourcery 自动生成的 Pull Request 总结、审阅者指南等。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审指令。
  • 调整其他评审设置。

获取帮助

Original review guide in English

Reviewer's Guide

Refactors the Magisk module to use a single native csc_tool plus a WebUI-based JSON configuration flow, replacing the previous multiple NDK binaries and text-based scripts, with updated post-fs-data/customize logic and CI/build pipelines to produce the new core and React WebUI.

Sequence diagram for the new post-fs-data CSC processing pipeline

sequenceDiagram
    participant AndroidInit as AndroidInit
    participant post_fs_data_sh as post_fs_data_sh
    participant csc_tool as csc_tool
    participant FileSystem as FileSystem

    AndroidInit->>post_fs_data_sh: invoke post-fs-data.sh
    activate post_fs_data_sh

    post_fs_data_sh->>post_fs_data_sh: prepare_log()
    post_fs_data_sh->>post_fs_data_sh: read_props(CSC, ARCH)
    post_fs_data_sh->>post_fs_data_sh: compute TOOL path

    post_fs_data_sh->>post_fs_data_sh: process_feature_file(csc, cscfeature.xml, csc.json)
    activate post_fs_data_sh
    post_fs_data_sh->>FileSystem: find_file(cscfeature.xml)
    FileSystem-->>post_fs_data_sh: origin_path
    post_fs_data_sh->>csc_tool: --decode origin_path decoded_csc
    csc_tool-->>post_fs_data_sh: decoded_csc
    post_fs_data_sh->>FileSystem: read CONFIG_PATH/csc.json
    alt user_config exists
        post_fs_data_sh->>csc_tool: --patch decoded_csc user_config patched_csc
        csc_tool-->>post_fs_data_sh: patched_csc
    else user_config missing
        post_fs_data_sh->>post_fs_data_sh: copy decoded_csc to patched_csc
    end
    post_fs_data_sh->>csc_tool: --encode patched_csc final_csc
    csc_tool-->>post_fs_data_sh: final_csc
    post_fs_data_sh->>FileSystem: safe_mount(final_csc, origin_path)
    post_fs_data_sh->>FileSystem: restorecon(origin_path)
    deactivate post_fs_data_sh

    post_fs_data_sh->>post_fs_data_sh: process_feature_file(carrier, customer_carrier_feature.json, carrier.json)
    activate post_fs_data_sh
    post_fs_data_sh->>FileSystem: find_file(customer_carrier_feature.json)
    FileSystem-->>post_fs_data_sh: origin_path
    post_fs_data_sh->>csc_tool: --decode origin_path decoded_carrier
    csc_tool-->>post_fs_data_sh: decoded_carrier
    post_fs_data_sh->>FileSystem: read CONFIG_PATH/carrier.json
    alt user_config exists
        post_fs_data_sh->>csc_tool: --patch decoded_carrier user_config patched_carrier
        csc_tool-->>post_fs_data_sh: patched_carrier
    else user_config missing
        post_fs_data_sh->>post_fs_data_sh: copy decoded_carrier to patched_carrier
    end
    post_fs_data_sh->>csc_tool: --encode patched_carrier final_carrier
    csc_tool-->>post_fs_data_sh: final_carrier
    post_fs_data_sh->>FileSystem: safe_mount(final_carrier, origin_path)
    post_fs_data_sh->>FileSystem: restorecon(origin_path)
    deactivate post_fs_data_sh

    post_fs_data_sh->>post_fs_data_sh: process_feature_file(ff, floating_feature.xml, ff.json)
    activate post_fs_data_sh
    post_fs_data_sh->>FileSystem: find_file(floating_feature.xml)
    alt file_not_found_in_optics
        post_fs_data_sh->>FileSystem: use /etc/floating_feature.xml as origin_path
    end
    post_fs_data_sh->>FileSystem: copy origin_path to decoded_ff
    post_fs_data_sh->>FileSystem: read CONFIG_PATH/ff.json
    alt user_config exists
        post_fs_data_sh->>csc_tool: --patch decoded_ff user_config patched_ff
        csc_tool-->>post_fs_data_sh: patched_ff
    else user_config missing
        post_fs_data_sh->>post_fs_data_sh: copy decoded_ff to patched_ff
    end
    post_fs_data_sh->>FileSystem: safe_mount(patched_ff, origin_path)
    post_fs_data_sh->>FileSystem: restorecon(origin_path)
    deactivate post_fs_data_sh

    deactivate post_fs_data_sh
    post_fs_data_sh-->>AndroidInit: CSC customization ready
Loading

Class diagram for new WebUI CSC editor types and relationships

classDiagram
    class App {
      +FilterMode filterMode
      +CSCEntry[] entries
      +Record~string_string~ originEntries
      +void loadData()
      +void syncToDisk(newEntries)
      +void toggleEnable(key)
      +void removeEntry(key)
      +void openModal(key, modified, originValue)
      +void confirmModal()
    }

    class CSCEntry {
      +string command
      +string key
      +string value
      +string desc
      +boolean enabled
    }

    class FilterModeEnum {
      <<enumeration>>
      ALL
      ENABLED
      DISABLED
      UNSET
    }

    class KernelSUExec {
      +Promise~ExecResult~ exec(command)
      +void toast(message)
    }

    class FileConfig {
      +string label
      +string name
      +string decoded
    }

    App --> CSCEntry : manages
    App --> FilterModeEnum : uses
    App --> KernelSUExec : calls_exec_and_toast
    App --> FileConfig : selects_currentFile

    class Paths {
      +string CONFIG_PATH
      +string MODULE_PATH
    }

    App --> Paths : builds_IO_paths
Loading

File-Level Changes

Change Details Files
Refactor post-fs-data pipeline to use unified csc_tool and JSON configs with dynamic file discovery and safe bind mounts.
  • Replace legacy decode/auto_modify/encode shell pipeline with structured helper functions and a per-file processing routine using csc_tool --decode/--patch/--encode.
  • Add robust logging, optional debug mode, and dynamic CSC file discovery under /optics/configs/carriers/$CSC with a floating_feature.xml fallback.
  • Introduce safe_mount with pre-checks and lazy umount, and apply SELinux restorecon after mounting patched files.
  • Use /data/adb/csc_config JSON files (csc.json, carrier.json, ff.json) as user patch inputs and handle XML vs JSON formats via csc_tool, with special-cased plaintext handling for floating_feature.xml.
module_files/post-fs-data.sh
Introduce unified native csc_tool implemented in C with custom encode/decode and patching for both CSC XML and carrier JSON.
  • Add Android NDK project for csc_tool using csc_tool.c and bundled cJSON library, linking against zlib.
  • Implement custom obfuscation + gzip-based encode/decode logic parameterized by salts/shifts tables.
  • Implement XML patcher that parses the FeatureSet/SecFloatingFeatureSet block into key/value pairs, applies JSON-driven MODIFY/DELETE operations, sorts features, and writes a new XML file.
  • Implement JSON patcher that walks carrier JSON customer/specific[].feature objects, applies enabled rules from a config JSON, and keeps keys sorted for determinism.
  • Provide unified CLI: --decode , --encode , and --patch with automatic XML/JSON detection and optional --debug logging.
src/core/jni/csc_tool.c
src/core/jni/Android.mk
src/core/jni/Application.mk
src/core/jni/lib/cJSON.c
src/core/jni/lib/cJSON.h
Replace old NDK binaries and text config workflow with a JSON-based configuration initialization in customize.sh.
  • Remove legacy auto_modify_* and decode_csc NDK sources and their build scripts plus example ml_*.txt files.
  • Change customize.sh to set sane permissions on $MODPATH and libs, create /data/adb/csc_config, and move initial csc.json/ff.json/carrier.json from module_files/configs if missing.
  • Guide users to configure features via the WebUI instead of editing example text files, and relax config directory permissions to 0777 for on-device editing.
module_files/customize.sh
module_files/configs/csc.json
module_files/configs/ff.json
module_files/configs/carrier.json
scr/auto_modify_cscfeature/jni/Android.mk
scr/auto_modify_cscfeature/jni/Application.mk
scr/auto_modify_cscfeature/jni/auto_modify_cscfeature.c
scr/auto_modify_json_csc/jni/Android.mk
scr/auto_modify_json_csc/jni/Application.mk
scr/auto_modify_json_csc/jni/auto_modify_jsoncsc.c
scr/cscdecode/jni/Android.mk
scr/cscdecode/jni/Application.mk
scr/cscdecode/jni/decode_csc.c
scr/CSC_SORT.py
module_files/ml_carrier.txt.example
module_files/ml_csc.txt.example
module_files/ml_ff.txt.example
Add a React-based WebUI for managing CSC configuration JSON on-device and wire it into the build output paths.
  • Implement a single-page React app (App.tsx) that can browse/search CSC keys, show original vs modified values, and edit entries via a modal, using KernelSU exec/toast APIs to read/write JSON under /data/adb/csc_config and decoded reference files in the module directory.
  • Provide UI affordances for enabling/disabling entries, deleting overrides (restoring to origin), filtering by state, and switching between carrier/csc/ff configs.
  • Replace the previous webui App implementation and adjust Parcel build output path to ../../module_files/webroot so assets are packed into the Magisk module.
src/webui/src/App.tsx
src/webui/src/App.css
src/webui/package.json
webui/src/App.tsx
webui/src/App.css
Update GitHub Actions workflow to build WebUI and new csc_tool and adjust packaging triggers.
  • Change workflow trigger to run on pushes to main/master/2.0 branches.
  • Add a Node.js 18 setup step and a WebUI build step that runs npm install && npm run build in src/webui, emitting files into module_files/webroot.
  • Replace three legacy ndk-build invocations with a single ndk-build in src/core that builds csc_tool and copies libs/* into module_files/libs.
  • Ensure .gitkeep files are removed before zipping the Magisk module, and keep the upload-artifact step pointing at auto_modify_cscfeature.zip.
  • Remove unused apk.yml workflow.
.github/workflows/magisk.yml
.github/workflows/apk.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 5 个问题,并留下了一些整体性反馈:

  • post-fs-data.sh 中,当缺少 $TOOL 时,你只记录了一个致命错误日志但仍继续执行;建议要么尽早退出(或实现真正的回退逻辑),以避免后续 decode/patch 步骤默默失败。
  • csc_tool.c 中有多处内存分配在所有错误路径上都没有被释放(例如在 patch_xml / patch_json 中解析失败的分支),并且有些 read_file_to_memory 的结果在提前返回时也没有释放;收紧这些清理逻辑可以提升多次调用时的健壮性。
  • csc_tool 中的 XML/JSON 检测依赖 strstr(argv[2], ".json"),这可能会错误识别路径中其他位置包含 .json 的文件名;可以考虑使用更严格的后缀检查,或使用显式的模式标志来区分 JSON 和 XML 输入。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `post-fs-data.sh`, when `$TOOL` is missing you only log a fatal error but continue execution; consider exiting early (or implementing the actual fallback) to avoid subsequent decode/patch steps silently failing.
- `csc_tool.c` has several allocations that are not freed on all error paths (e.g., in `patch_xml`/`patch_json` when parsing fails) and some `read_file_to_memory` results are used without freeing on early returns; tightening up cleanup logic would improve robustness for repeated invocations.
- The XML/JSON detection in `csc_tool` relies on `strstr(argv[2], ".json")`, which may misclassify paths with `.json` elsewhere in the name; consider using a stricter suffix check or explicit mode flag to distinguish JSON vs XML inputs.

## Individual Comments

### Comment 1
<location path="src/core/jni/csc_tool.c" line_range="231-233" />
<code_context>
+        }
+        FILE *f = fopen(argv[3], "wb"); fwrite(enc, 1, cs, f); fclose(f);
+        free(d); free(comp); free(enc);
+    } else if (strcmp(argv[1], "--patch") == 0 && argc == 5) {
+        if (strstr(argv[2], ".json")) patch_json(argv[2], argv[3], argv[4]);
+        else patch_xml(argv[2], argv[3], argv[4]);
+    }
+    return 0;
</code_context>
<issue_to_address>
**issue (bug_risk):** 使用 `strstr(argv[2], ".json")` 来区分 JSON 和 XML 比较脆弱,而且当前和调用端的路径约定不一致。

在 `--patch` 分支中,仅根据 `argv[2]` 是否包含 `.json` 来决定路由。然而,`post-fs-data.sh` 传入的是 `decoded_$label`(无扩展名),因此诸如运营商配置这样的 JSON 输入会被错误地走到 `patch_xml`。即使未来更新了调用端路径,仅依赖文件名扩展名也依旧不够可靠。

可以考虑:
- 根据首个非空白字符检测格式(`{` / `[` → JSON,`<` → XML),或者
- 添加显式的 `--json` / `--xml` 标志,而不是从路径中推断。

这样可以避免在路径或命名约定改变时发生错误路由。
</issue_to_address>

### Comment 2
<location path="src/core/jni/csc_tool.c" line_range="130-134" />
<code_context>
+    }
+
+    cJSON* item;
+    cJSON_ArrayForEach(item, configs) {
+        const char* k = cJSON_GetObjectItem(item, "key")->valuestring;
+        const char* v = cJSON_GetObjectItem(item, "value") ? cJSON_GetObjectItem(item, "value")->valuestring : "";
+        const char* cmd = cJSON_GetObjectItem(item, "command")->valuestring;
+        if (!cJSON_IsTrue(cJSON_GetObjectItem(item, "enabled"))) continue;
+
+        int found = -1;
</code_context>
<issue_to_address>
**issue (bug_risk):** 当前补丁逻辑假设所有预期的 JSON 键都存在;缺失或格式错误的条目会导致崩溃。

在 `patch_xml``patch_json` 中,你通过直接解引用 `cJSON_GetObjectItem(...)` 的返回值来访问 `key``value``command``enabled` 等字段。对于格式错误或用户修改过的配置(字段缺失、类型错误、拼写错误等),`cJSON_GetObjectItem` 可能返回 `NULL`,在此基础上解引用或对其调用 `cJSON_IsTrue` 会导致未定义行为甚至崩溃。

请先将 `cJSON_GetObjectItem` 的结果保存到局部变量中,检查其合法性(`!= NULL``cJSON_IsString``cJSON_IsBool` 等),然后对非法条目进行跳过或记录日志,而不是假定所有键都存在且类型正确。
</issue_to_address>

### Comment 3
<location path="module_files/post-fs-data.sh" line_range="121-123" />
<code_context>
+prepare_log
+
+# 检查工具是否存在
+if [ ! -f "$TOOL" ]; then
+    log "致命错误: 未在 $TOOL 找到核心工具"
+    # 回退到尝试使用老的二进制(如果存在)或退出
 fi

</code_context>
<issue_to_address>
**suggestion (bug_risk):** 当找不到 csc_tool 时缺少回退/退出行为,使得错误处理不完整。

目前脚本在 `$TOOL` 缺失时会记录一个致命错误,但之后仍继续执行,并在后面调用 `process_feature_file`,这会尝试运行一个不存在的二进制并产生让人困惑的错误。请实现实际的回退逻辑(如果有旧版本二进制可用则使用它),或立即终止(例如 `exit 1`),这样失败是确定的,日志与行为也更加一致。

建议实现:

```
prepare_log

# 检查工具是否存在
if [ ! -f "$TOOL" ]; then
    log "致命错误: 未在 $TOOL 找到核心工具,终止执行"
    exit 1
fi

MODDIR=${0%/*}
LOG_FILE="$MODDIR/log.txt"

```

- 确保在这个检查之前已经定义好 `TOOL` 变量(例如指向期望的 csc_tool 二进制)。如果目前是在脚本后面才设置,请把赋值移到 `prepare_log` 和存在性检查之前。
- 如果以后决定对旧版二进制实现真实的回退逻辑,可以将 `exit 1` 分支替换为检查旧版路径的逻辑(例如 `[ -x "$LEGACY_TOOL" ] && TOOL="$LEGACY_TOOL" || exit 1`),同时保持日志输出的一致性。
</issue_to_address>

### Comment 4
<location path="src/core/jni/csc_tool.c" line_range="74-78" />
<code_context>
+    return strcmp(((Feature*)a)->key, ((Feature*)b)->key);
+}
+
+void patch_xml(const char* xml_path, const char* config_json, const char* dest_path) {
+    size_t xml_size, json_size;
+    char* xml_content = read_file_to_memory(xml_path, &xml_size);
+    char* json_content = read_file_to_memory(config_json, &json_size);
+    if (!xml_content || !json_content) return;
+
+    cJSON* configs = cJSON_Parse(json_content);
</code_context>
<issue_to_address>
**nitpick (bug_risk):** `patch_xml` / `patch_json` 中的提前返回会泄漏已分配的缓冲区;即便是短生命周期工具,也可以很容易整理好这部分逻辑。

`patch_xml``patch_json` 通过 `read_file_to_memory` 分配了 `xml_content` / `json_content`,但某些错误路径在返回前没有释放它们(例如 `if (!xml_content || !json_content) return;`,以及 `patch_json` 中类似的代码)。请确保所有提前返回的路径都会释放已经成功分配的缓冲区,或者通过统一的 `cleanup:` 标签集中处理清理逻辑再返回。
</issue_to_address>

### Comment 5
<location path="src/core/jni/csc_tool.c" line_range="207" />
<code_context>
+
+// --- Main ---
+
+int main(int argc, char **argv) {
+    if (argc < 4) return 1;
+    if (strcmp(argv[1], "--decode") == 0) {
</code_context>
<issue_to_address>
**issue (complexity):** 建议将编码/解码逻辑以及 XML/JSON 补丁流程拆分为一些可复用的小工具函数,并使用 `qsort` 对 JSON 排序,这样 `main` 和补丁函数只需要编排高层步骤。

在不改变行为的前提下,你可以通过以下方式减少复杂度和重复:

### 1. 将编码/解码核心逻辑抽取到辅助函数中

目前编码/解码位运算逻辑直接写在 `main` 里。可以将其移动到专用函数中;`main` 只负责 I/O 和模式选择。

```c
static void decode_buffer(const unsigned char *in, size_t len, unsigned char *out) {
    for (size_t i = 0; i < len; i++) {
        unsigned int b = in[i];
        int s = shifts[i % num_shifts];
        out[i] = (((b << s) | (b >> (8 - s))) & 0xFF) ^ salts[i % num_salts];
    }
}

static void encode_buffer(const unsigned char *in, size_t len, unsigned char *out) {
    for (size_t i = 0; i < len; i++) {
        unsigned int x = (in[i] ^ salts[i % num_salts]) & 0xFF;
        int s = shifts[i % num_shifts];
        out[i] = ((x >> s) | (x << (8 - s))) & 0xFF;
    }
}
```

然后 `main` 可以简化为:

```c
} else if (strcmp(argv[1], "--decode") == 0) {
    size_t sz; unsigned char *d = (unsigned char*)read_file_to_memory(argv[2], &sz);
    if (!d) return 1;
    unsigned char *dec = malloc(sz);
    decode_buffer(d, sz, dec);
    decompress_gzip(dec, sz, argv[3]);
    free(d); free(dec);
} else if (strcmp(argv[1], "--encode") == 0) {
    size_t sz; unsigned char *d = (unsigned char*)read_file_to_memory(argv[2], &sz);
    if (!d) return 1;
    unsigned char *comp; size_t cs;
    compress_gzip(d, sz, &comp, &cs);
    unsigned char *enc = malloc(cs);
    encode_buffer(comp, cs, enc);
    FILE *f = fopen(argv[3], "wb"); fwrite(enc, 1, cs, f); fclose(f);
    free(d); free(comp); free(enc);
}
```

这样也能将 `salts` / `shifts` / 魔数隔离在编码层中。

---

### 2. 将 XML 补丁流程拆分为更小的步骤

`patch_xml` 目前在一个函数里完成:读取 XML、查找块、解析 feature、应用配置、排序、写回。可以拆分为多个小的辅助函数,使每一部分都更易于理解和测试。

示例结构:

```c
static int find_feature_block(char *xml, const char **active_tag,
                              char **block_start, char **block_end) {
    const char* tag_names[] = {"FeatureSet", "SecFloatingFeatureSet"};
    for (int i = 0; i < 2; i++) {
        char s_tag[128], e_tag[128];
        sprintf(s_tag, "<%s>", tag_names[i]);
        sprintf(e_tag, "</%s>", tag_names[i]);
        char *s = strstr(xml, s_tag);
        if (!s) continue;
        s += strlen(s_tag);
        char *e = strstr(s, e_tag);
        if (!e) continue;
        *active_tag = tag_names[i];
        *block_start = s;
        *block_end = e;
        return 0;
    }
    return -1;
}

static int parse_features(char *block_start, char *block_end,
                          Feature *features, int *count) {
    char *p = block_start;
    *count = 0;
    while (p < block_end && *count < MAX_FEATURES) {
        /* existing parsing logic moved here */
    }
    return 0;
}
```

这样 `patch_xml` 就主要是编排逻辑:

```c
void patch_xml(const char* xml_path, const char* config_json, const char* dest_path) {
    /* read xml/json ... */

    const char *active_tag = NULL;
    char *s_ptr = NULL, *e_ptr = NULL;
    if (find_feature_block(xml_content, &active_tag, &s_ptr, &e_ptr) != 0) {
        fprintf(stderr, "Error: No FeatureSet found\n");
        /* free + return */
    }

    Feature *features = malloc(sizeof(Feature) * MAX_FEATURES);
    int count = 0;
    parse_features(s_ptr, e_ptr, features, &count);
    apply_xml_config(configs, features, &count);   // extracted from existing loop
    qsort(features, count, sizeof(Feature), compare_features);
    write_patched_xml(dest_path, xml_content, xml_size, s_ptr, e_ptr, features, count);
}
```

你几乎可以直接把现有代码挪进这些辅助函数,改动很少;行为保持不变,但主函数会更短。

---

### 3. 让 JSON 补丁结构与 XML 补丁结构对齐

类似地,`patch_json` 目前在一个地方做了很多事。可以抽取“加载并解析”和“应用配置”两个步骤,使其结构与 XML 更加对称:

```c
static int load_json_pair(const char *src_path, const char *cfg_path,
                          cJSON **src_root, cJSON **cfg_root) {
    size_t s_sz, c_sz;
    char *s_d = read_file_to_memory(src_path, &s_sz);
    char *c_d = read_file_to_memory(cfg_path, &c_sz);
    if (!s_d || !c_d) { free(s_d); free(c_d); return -1; }

    *src_root = cJSON_Parse(s_d);
    *cfg_root = cJSON_Parse(c_d);
    free(s_d); free(c_d);
    return (*src_root && *cfg_root) ? 0 : -1;
}

static void apply_json_config(cJSON *feat, cJSON *cfg_root) {
    cJSON *it;
    cJSON_ArrayForEach(it, cfg_root) {
        /* move existing loop body here */
    }
}

void patch_json(const char* src, const char* cfg, const char* dest) {
    cJSON *s_r = NULL, *c_r = NULL;
    if (load_json_pair(src, cfg, &s_r, &c_r) != 0) return;
    cJSON *customer = cJSON_GetObjectItem(s_r, "customer");
    cJSON *feat = cJSON_GetObjectItem(cJSON_GetArrayItem(customer, 0), "feature");
    apply_json_config(feat, c_r);
    sort_json(feat);
    /* write + cleanup */
}
```

这样可以让 JSON/XML “补丁步骤”在概念上保持一致,从而在后续修改行为时减轻心智负担。

---

### 4. 用 `qsort` 替换 `sort_json` 中的冒泡排序

你已经在 feature 排序中使用了 `qsort`;可以复用它来对 JSON 排序,而不必维护一个自定义冒泡排序实现:

```c
static int compare_cjson_object(const void *a, const void *b) {
    const cJSON *ca = *(const cJSON**)a;
    const cJSON *cb = *(const cJSON**)b;
    return strcmp(ca->string, cb->string);
}

void sort_json(cJSON *obj) {
    int n = cJSON_GetArraySize(obj);
    if (n <= 1) return;
    cJSON **nodes = malloc(sizeof(cJSON*) * n);
    cJSON *c = obj->child;
    for (int i = 0; i < n; i++) { nodes[i] = c; c = c->next; }

    qsort(nodes, n, sizeof(cJSON*), compare_cjson_object);

    obj->child = nodes[0];
    nodes[0]->prev = NULL;
    for (int i = 0; i < n - 1; i++) {
        nodes[i]->next = nodes[i+1];
        nodes[i+1]->prev = nodes[i];
    }
    nodes[n-1]->next = NULL;
    free(nodes);
}
```

这样可以移除自定义排序逻辑,并与 feature 排序的方式保持一致。

---

这些改动在保持现有功能不变的前提下,将职责拆分为更加聚焦的单元,使这个单文件更容易浏览和修改,也更安全。
</issue_to_address>

Sourcery 对开源项目免费使用——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 5 issues, and left some high level feedback:

  • In post-fs-data.sh, when $TOOL is missing you only log a fatal error but continue execution; consider exiting early (or implementing the actual fallback) to avoid subsequent decode/patch steps silently failing.
  • csc_tool.c has several allocations that are not freed on all error paths (e.g., in patch_xml/patch_json when parsing fails) and some read_file_to_memory results are used without freeing on early returns; tightening up cleanup logic would improve robustness for repeated invocations.
  • The XML/JSON detection in csc_tool relies on strstr(argv[2], ".json"), which may misclassify paths with .json elsewhere in the name; consider using a stricter suffix check or explicit mode flag to distinguish JSON vs XML inputs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `post-fs-data.sh`, when `$TOOL` is missing you only log a fatal error but continue execution; consider exiting early (or implementing the actual fallback) to avoid subsequent decode/patch steps silently failing.
- `csc_tool.c` has several allocations that are not freed on all error paths (e.g., in `patch_xml`/`patch_json` when parsing fails) and some `read_file_to_memory` results are used without freeing on early returns; tightening up cleanup logic would improve robustness for repeated invocations.
- The XML/JSON detection in `csc_tool` relies on `strstr(argv[2], ".json")`, which may misclassify paths with `.json` elsewhere in the name; consider using a stricter suffix check or explicit mode flag to distinguish JSON vs XML inputs.

## Individual Comments

### Comment 1
<location path="src/core/jni/csc_tool.c" line_range="231-233" />
<code_context>
+        }
+        FILE *f = fopen(argv[3], "wb"); fwrite(enc, 1, cs, f); fclose(f);
+        free(d); free(comp); free(enc);
+    } else if (strcmp(argv[1], "--patch") == 0 && argc == 5) {
+        if (strstr(argv[2], ".json")) patch_json(argv[2], argv[3], argv[4]);
+        else patch_xml(argv[2], argv[3], argv[4]);
+    }
+    return 0;
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `strstr(argv[2], ".json")` to distinguish JSON from XML is brittle and currently inconsistent with caller paths.

In the `--patch` branch, routing is based solely on whether `argv[2]` contains `.json`. However, `post-fs-data.sh` passes `decoded_$label` (no extension), so JSON inputs like carrier config will incorrectly go through `patch_xml`. Even if caller paths are updated, relying on filename extensions is still fragile.

Consider either:
- detecting format from the first non‑whitespace character (`{`/`[` → JSON, `<` → XML), or
- adding an explicit `--json` / `--xml` flag instead of inferring from the path.

This avoids misrouting when paths or naming conventions change.
</issue_to_address>

### Comment 2
<location path="src/core/jni/csc_tool.c" line_range="130-134" />
<code_context>
+    }
+
+    cJSON* item;
+    cJSON_ArrayForEach(item, configs) {
+        const char* k = cJSON_GetObjectItem(item, "key")->valuestring;
+        const char* v = cJSON_GetObjectItem(item, "value") ? cJSON_GetObjectItem(item, "value")->valuestring : "";
+        const char* cmd = cJSON_GetObjectItem(item, "command")->valuestring;
+        if (!cJSON_IsTrue(cJSON_GetObjectItem(item, "enabled"))) continue;
+
+        int found = -1;
</code_context>
<issue_to_address>
**issue (bug_risk):** Patch logic assumes all expected JSON keys exist; missing or malformed entries can cause crashes.

In both `patch_xml` and `patch_json`, fields like `key`, `value`, `command`, and `enabled` are accessed via direct dereferences of `cJSON_GetObjectItem(...)` results. For malformed or user-edited configs (missing fields, wrong types, typos), `cJSON_GetObjectItem` can return `NULL`, leading to undefined behavior or crashes when you dereference or call `cJSON_IsTrue` on it.

Please first assign `cJSON_GetObjectItem` results to locals, validate them (`!= NULL`, `cJSON_IsString`, `cJSON_IsBool`, etc.), and then either skip or log invalid entries instead of assuming all keys are present and well-typed.
</issue_to_address>

### Comment 3
<location path="module_files/post-fs-data.sh" line_range="121-123" />
<code_context>
+prepare_log
+
+# 检查工具是否存在
+if [ ! -f "$TOOL" ]; then
+    log "致命错误: 未在 $TOOL 找到核心工具"
+    # 回退到尝试使用老的二进制(如果存在)或退出
 fi

</code_context>
<issue_to_address>
**suggestion (bug_risk):** Missing fallback/exit behavior if csc_tool is not found makes the error handling incomplete.

Right now the script logs a fatal error when `$TOOL` is missing but then continues and later calls `process_feature_file`, which will try to run a non‑existent binary and produce confusing errors. Please either implement the actual fallback (use the legacy binary if available) or abort immediately (e.g. `exit 1`) so the failure is deterministic and the logs match the behavior.

Suggested implementation:

```
prepare_log

# 检查工具是否存在
if [ ! -f "$TOOL" ]; then
    log "致命错误: 未在 $TOOL 找到核心工具,终止执行"
    exit 1
fi

MODDIR=${0%/*}
LOG_FILE="$MODDIR/log.txt"

```

- Ensure the `TOOL` variable is defined **before** this check (e.g. pointing to the expected csc_tool binary). If it's currently set later in the script, move its assignment above `prepare_log` and this existence check.
- If you later decide to implement a real fallback to a legacy binary, replace the `exit 1` branch with logic that checks for the legacy path (e.g. `[ -x "$LEGACY_TOOL" ] && TOOL="$LEGACY_TOOL" || exit 1`) while keeping the logging consistent.
</issue_to_address>

### Comment 4
<location path="src/core/jni/csc_tool.c" line_range="74-78" />
<code_context>
+    return strcmp(((Feature*)a)->key, ((Feature*)b)->key);
+}
+
+void patch_xml(const char* xml_path, const char* config_json, const char* dest_path) {
+    size_t xml_size, json_size;
+    char* xml_content = read_file_to_memory(xml_path, &xml_size);
+    char* json_content = read_file_to_memory(config_json, &json_size);
+    if (!xml_content || !json_content) return;
+
+    cJSON* configs = cJSON_Parse(json_content);
</code_context>
<issue_to_address>
**nitpick (bug_risk):** Early returns in patch_xml/patch_json leak allocated buffers; easy to tidy up even for a short-lived tool.

`patch_xml` and `patch_json` allocate `xml_content` / `json_content` via `read_file_to_memory`, but some error paths return without freeing them (e.g. `if (!xml_content || !json_content) return;`, and similar in `patch_json`). Please ensure all early returns free any successfully allocated buffers, or centralize cleanup via a common `cleanup:` label before returning.
</issue_to_address>

### Comment 5
<location path="src/core/jni/csc_tool.c" line_range="207" />
<code_context>
+
+// --- Main ---
+
+int main(int argc, char **argv) {
+    if (argc < 4) return 1;
+    if (strcmp(argv[1], "--decode") == 0) {
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the encoding/decoding logic and XML/JSON patch workflows into small reusable helpers and using qsort for JSON sorting so that main and patch functions only orchestrate high-level steps.

You can reduce complexity and duplication without changing behavior by:

### 1. Extract encode/decode core into helpers

Right now the encode/decode bit‑twiddling is inlined in `main`. Move it into dedicated functions; `main` then just orchestrates I/O and mode selection.

```c
static void decode_buffer(const unsigned char *in, size_t len, unsigned char *out) {
    for (size_t i = 0; i < len; i++) {
        unsigned int b = in[i];
        int s = shifts[i % num_shifts];
        out[i] = (((b << s) | (b >> (8 - s))) & 0xFF) ^ salts[i % num_salts];
    }
}

static void encode_buffer(const unsigned char *in, size_t len, unsigned char *out) {
    for (size_t i = 0; i < len; i++) {
        unsigned int x = (in[i] ^ salts[i % num_salts]) & 0xFF;
        int s = shifts[i % num_shifts];
        out[i] = ((x >> s) | (x << (8 - s))) & 0xFF;
    }
}
```

Then `main` becomes simpler:

```c
} else if (strcmp(argv[1], "--decode") == 0) {
    size_t sz; unsigned char *d = (unsigned char*)read_file_to_memory(argv[2], &sz);
    if (!d) return 1;
    unsigned char *dec = malloc(sz);
    decode_buffer(d, sz, dec);
    decompress_gzip(dec, sz, argv[3]);
    free(d); free(dec);
} else if (strcmp(argv[1], "--encode") == 0) {
    size_t sz; unsigned char *d = (unsigned char*)read_file_to_memory(argv[2], &sz);
    if (!d) return 1;
    unsigned char *comp; size_t cs;
    compress_gzip(d, sz, &comp, &cs);
    unsigned char *enc = malloc(cs);
    encode_buffer(comp, cs, enc);
    FILE *f = fopen(argv[3], "wb"); fwrite(enc, 1, cs, f); fclose(f);
    free(d); free(comp); free(enc);
}
```

This also isolates `salts`/`shifts`/magic into the encoding layer.

---

### 2. Decompose XML patching into smaller steps

`patch_xml` currently: read XML, find block, parse features, apply config, sort, write. Split into small helpers so each piece is easier to reason about and test.

Example structure:

```c
static int find_feature_block(char *xml, const char **active_tag,
                              char **block_start, char **block_end) {
    const char* tag_names[] = {"FeatureSet", "SecFloatingFeatureSet"};
    for (int i = 0; i < 2; i++) {
        char s_tag[128], e_tag[128];
        sprintf(s_tag, "<%s>", tag_names[i]);
        sprintf(e_tag, "</%s>", tag_names[i]);
        char *s = strstr(xml, s_tag);
        if (!s) continue;
        s += strlen(s_tag);
        char *e = strstr(s, e_tag);
        if (!e) continue;
        *active_tag = tag_names[i];
        *block_start = s;
        *block_end = e;
        return 0;
    }
    return -1;
}

static int parse_features(char *block_start, char *block_end,
                          Feature *features, int *count) {
    char *p = block_start;
    *count = 0;
    while (p < block_end && *count < MAX_FEATURES) {
        /* existing parsing logic moved here */
    }
    return 0;
}
```

Then `patch_xml` becomes mainly orchestration:

```c
void patch_xml(const char* xml_path, const char* config_json, const char* dest_path) {
    /* read xml/json ... */

    const char *active_tag = NULL;
    char *s_ptr = NULL, *e_ptr = NULL;
    if (find_feature_block(xml_content, &active_tag, &s_ptr, &e_ptr) != 0) {
        fprintf(stderr, "Error: No FeatureSet found\n");
        /* free + return */
    }

    Feature *features = malloc(sizeof(Feature) * MAX_FEATURES);
    int count = 0;
    parse_features(s_ptr, e_ptr, features, &count);
    apply_xml_config(configs, features, &count);   // extracted from existing loop
    qsort(features, count, sizeof(Feature), compare_features);
    write_patched_xml(dest_path, xml_content, xml_size, s_ptr, e_ptr, features, count);
}
```

You can literally move existing code into these helpers with minimal edits; behavior stays identical but the main function is short.

---

### 3. Mirror JSON patching structure with helpers

Similarly, `patch_json` does several things in one place. Extract “load and parse” and “apply config” so the structure parallels XML:

```c
static int load_json_pair(const char *src_path, const char *cfg_path,
                          cJSON **src_root, cJSON **cfg_root) {
    size_t s_sz, c_sz;
    char *s_d = read_file_to_memory(src_path, &s_sz);
    char *c_d = read_file_to_memory(cfg_path, &c_sz);
    if (!s_d || !c_d) { free(s_d); free(c_d); return -1; }

    *src_root = cJSON_Parse(s_d);
    *cfg_root = cJSON_Parse(c_d);
    free(s_d); free(c_d);
    return (*src_root && *cfg_root) ? 0 : -1;
}

static void apply_json_config(cJSON *feat, cJSON *cfg_root) {
    cJSON *it;
    cJSON_ArrayForEach(it, cfg_root) {
        /* move existing loop body here */
    }
}

void patch_json(const char* src, const char* cfg, const char* dest) {
    cJSON *s_r = NULL, *c_r = NULL;
    if (load_json_pair(src, cfg, &s_r, &c_r) != 0) return;
    cJSON *customer = cJSON_GetObjectItem(s_r, "customer");
    cJSON *feat = cJSON_GetObjectItem(cJSON_GetArrayItem(customer, 0), "feature");
    apply_json_config(feat, c_r);
    sort_json(feat);
    /* write + cleanup */
}
```

This keeps JSON/XML “patch steps” conceptually aligned, which reduces mental overhead when you change behavior later.

---

### 4. Replace bubble sort in `sort_json` with `qsort`

You already use `qsort` for features; you can reuse it for JSON, instead of maintaining a custom bubble sort:

```c
static int compare_cjson_object(const void *a, const void *b) {
    const cJSON *ca = *(const cJSON**)a;
    const cJSON *cb = *(const cJSON**)b;
    return strcmp(ca->string, cb->string);
}

void sort_json(cJSON *obj) {
    int n = cJSON_GetArraySize(obj);
    if (n <= 1) return;
    cJSON **nodes = malloc(sizeof(cJSON*) * n);
    cJSON *c = obj->child;
    for (int i = 0; i < n; i++) { nodes[i] = c; c = c->next; }

    qsort(nodes, n, sizeof(cJSON*), compare_cjson_object);

    obj->child = nodes[0];
    nodes[0]->prev = NULL;
    for (int i = 0; i < n - 1; i++) {
        nodes[i]->next = nodes[i+1];
        nodes[i+1]->prev = nodes[i];
    }
    nodes[n-1]->next = NULL;
    free(nodes);
}
```

This removes custom sorting logic and aligns with your feature sort pattern.

---

These changes keep functionality intact, but split responsibilities into focused units, making the single file easier to navigate and safer to modify.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/core/jni/csc_tool.c Outdated
Comment thread src/core/jni/csc_tool.c Outdated
Comment thread module_files/post-fs-data.sh Outdated
Comment thread src/core/jni/csc_tool.c Outdated
Comment thread src/core/jni/csc_tool.c
@Mzdyl
Mzdyl marked this pull request as draft February 25, 2026 02:02
@Mzdyl
Mzdyl marked this pull request as ready for review February 25, 2026 02:53

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 3 个问题,并给出了一些整体性的反馈:

  • post-fs-data.sh 中,当 $TOOL 缺失时你只记录日志但仍继续执行;建议在致命日志之后尽早以非零状态退出,这样可以避免后续流水线在半配置状态下继续运行。
  • csc_tool.c::patch_xml 中对 XML 的处理依赖于对 <FeatureSet>/<SecFloatingFeatureSet> 块进行临时性的 strstr/sprintf 字符串解析;如果未来的 CSC 变体引入不同的包装标签或嵌套标签,这种方式很可能会失效,因此可能值得统一并强化 XML 结构检测逻辑,而不是仅对两个标签名做字符串搜索。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- In `post-fs-data.sh` you only log when `$TOOL` is missing but continue execution; consider exiting early (non‑zero) after the fatal log so the rest of the pipeline doesn’t run in a half‑configured state.
- The XML handling in `csc_tool.c::patch_xml` relies on ad‑hoc `strstr`/`sprintf` parsing of `<FeatureSet>`/`<SecFloatingFeatureSet>` blocks; if future CSC variants introduce different wrappers or nested tags this will likely break, so it may be worth centralizing and hardening the XML structure detection instead of string searching against only two tag names.

## Individual Comments

### Comment 1
<location path="module_files/post-fs-data.sh" line_range="189-17" />
<code_context>
+debug "CONFIG_PATH=$CONFIG_PATH"
+debug "TOOL=$TOOL"
+
+if [ ! -f "$TOOL" ]; then
+    log "致命错误: 未在 $TOOL 找到核心工具"
 fi

-# 记录脚本结束
</code_context>
<issue_to_address>
**issue (bug_risk):** Missing early exit when core tool is unavailable may lead to confusing partial execution

Right now this guard only logs the fatal error but still lets the script continue, so `process_feature_file` will later try to invoke a missing binary and fail in a less obvious way. Add an `exit 1` (or `return 1` in a function) here so execution stops immediately when `$TOOL` is not found.
</issue_to_address>

### Comment 2
<location path="module_files/customize.sh" line_range="22" />
<code_context>
-REPLACE="
-"
+# 权限修正
+set_perm_recursive "$CONFIG_DIR" 0 0 0777 0777
+
+ui_print "------------------------"
</code_context>
<issue_to_address>
**🚨 suggestion (security):** World-writable config directory under /data/adb is risky and likely unnecessary

Using `0777` here makes all config files in `$CONFIG_DIR` world‑writable to anything that can access `/data/adb`, which is a larger trust surface than needed. Since only root/Magisk needs write access, consider restricting the directory to `0755` or `0700` and controlling write access via file‑level permissions instead.
</issue_to_address>

### Comment 3
<location path="src/core/jni/csc_tool.c" line_range="99-114" />
<code_context>
+    return (ret == Z_STREAM_END) ? 0 : -1;
+}
+
+int compress_gz(const unsigned char* src, size_t src_len, unsigned char** out, size_t* out_len) {
+    DEBUG_LOG("Starting zlib compression (input size: %zu)...", src_len);
+    z_stream strm = {0};
+    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) return -1;
+    size_t bound = deflateBound(&strm, src_len) + 100;
+    *out = malloc(bound);
+    if (!*out) { deflateEnd(&strm); return -1; }
+    
+    strm.next_in = (Bytef*)src; strm.avail_in = src_len;
+    strm.next_out = *out; strm.avail_out = bound;
+    deflate(&strm, Z_FINISH);
+    *out_len = bound - strm.avail_out;
+    deflateEnd(&strm); 
+    DEBUG_LOG("Compression successful (output size: %zu).", *out_len);
+    return 0;
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Compression path ignores deflate() return value and assumes single-call completion

In `compress_gz` you call `deflate(&strm, Z_FINISH)` once, ignore its return value, and assume completion. For larger inputs this can yield truncated output when `deflate` returns `Z_OK` or `Z_BUF_ERROR`. Consider looping on `deflate` until `Z_STREAM_END` (or an error) and handling unexpected statuses, similar to `inflate` in `decompress_gz`.

```suggestion
int compress_gz(const unsigned char* src, size_t src_len, unsigned char** out, size_t* out_len) {
    DEBUG_LOG("Starting zlib compression (input size: %zu)...", src_len);
    z_stream strm = {0};
    int ret;

    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
        DEBUG_LOG("deflateInit2 failed.");
        return -1;
    }

    size_t bound = deflateBound(&strm, src_len) + 100;
    *out = malloc(bound);
    if (!*out) {
        DEBUG_LOG("Failed to allocate %zu bytes for compression buffer.", bound);
        deflateEnd(&strm);
        return -1;
    }

    strm.next_in  = (Bytef*)src;
    strm.avail_in = (uInt)src_len;
    strm.next_out = *out;
    strm.avail_out = (uInt)bound;

    do {
        ret = deflate(&strm, Z_FINISH);
        if (ret == Z_STREAM_ERROR) {
            DEBUG_LOG("deflate encountered Z_STREAM_ERROR.");
            deflateEnd(&strm);
            free(*out);
            *out = NULL;
            return -1;
        }
        /* With deflateBound() the buffer should be large enough, so
         * repeatedly returning Z_OK with no progress would be unexpected. */
        if (ret == Z_OK && strm.avail_out == 0) {
            DEBUG_LOG("deflate buffer exhausted before reaching Z_STREAM_END.");
            deflateEnd(&strm);
            free(*out);
            *out = NULL;
            return -1;
        }
    } while (ret == Z_OK);

    if (ret != Z_STREAM_END) {
        DEBUG_LOG("deflate did not reach Z_STREAM_END (ret=%d).", ret);
        deflateEnd(&strm);
        free(*out);
        *out = NULL;
        return -1;
    }

    *out_len = bound - strm.avail_out;
    deflateEnd(&strm);
    DEBUG_LOG("Compression successful (output size: %zu).", *out_len);
    return 0;
}
```
</issue_to_address>

Sourcery 对开源项目是免费的 —— 如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据这些反馈改进后续的评审。
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • In post-fs-data.sh you only log when $TOOL is missing but continue execution; consider exiting early (non‑zero) after the fatal log so the rest of the pipeline doesn’t run in a half‑configured state.
  • The XML handling in csc_tool.c::patch_xml relies on ad‑hoc strstr/sprintf parsing of <FeatureSet>/<SecFloatingFeatureSet> blocks; if future CSC variants introduce different wrappers or nested tags this will likely break, so it may be worth centralizing and hardening the XML structure detection instead of string searching against only two tag names.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `post-fs-data.sh` you only log when `$TOOL` is missing but continue execution; consider exiting early (non‑zero) after the fatal log so the rest of the pipeline doesn’t run in a half‑configured state.
- The XML handling in `csc_tool.c::patch_xml` relies on ad‑hoc `strstr`/`sprintf` parsing of `<FeatureSet>`/`<SecFloatingFeatureSet>` blocks; if future CSC variants introduce different wrappers or nested tags this will likely break, so it may be worth centralizing and hardening the XML structure detection instead of string searching against only two tag names.

## Individual Comments

### Comment 1
<location path="module_files/post-fs-data.sh" line_range="189-17" />
<code_context>
+debug "CONFIG_PATH=$CONFIG_PATH"
+debug "TOOL=$TOOL"
+
+if [ ! -f "$TOOL" ]; then
+    log "致命错误: 未在 $TOOL 找到核心工具"
 fi

-# 记录脚本结束
</code_context>
<issue_to_address>
**issue (bug_risk):** Missing early exit when core tool is unavailable may lead to confusing partial execution

Right now this guard only logs the fatal error but still lets the script continue, so `process_feature_file` will later try to invoke a missing binary and fail in a less obvious way. Add an `exit 1` (or `return 1` in a function) here so execution stops immediately when `$TOOL` is not found.
</issue_to_address>

### Comment 2
<location path="module_files/customize.sh" line_range="22" />
<code_context>
-REPLACE="
-"
+# 权限修正
+set_perm_recursive "$CONFIG_DIR" 0 0 0777 0777
+
+ui_print "------------------------"
</code_context>
<issue_to_address>
**🚨 suggestion (security):** World-writable config directory under /data/adb is risky and likely unnecessary

Using `0777` here makes all config files in `$CONFIG_DIR` world‑writable to anything that can access `/data/adb`, which is a larger trust surface than needed. Since only root/Magisk needs write access, consider restricting the directory to `0755` or `0700` and controlling write access via file‑level permissions instead.
</issue_to_address>

### Comment 3
<location path="src/core/jni/csc_tool.c" line_range="99-114" />
<code_context>
+    return (ret == Z_STREAM_END) ? 0 : -1;
+}
+
+int compress_gz(const unsigned char* src, size_t src_len, unsigned char** out, size_t* out_len) {
+    DEBUG_LOG("Starting zlib compression (input size: %zu)...", src_len);
+    z_stream strm = {0};
+    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) return -1;
+    size_t bound = deflateBound(&strm, src_len) + 100;
+    *out = malloc(bound);
+    if (!*out) { deflateEnd(&strm); return -1; }
+    
+    strm.next_in = (Bytef*)src; strm.avail_in = src_len;
+    strm.next_out = *out; strm.avail_out = bound;
+    deflate(&strm, Z_FINISH);
+    *out_len = bound - strm.avail_out;
+    deflateEnd(&strm); 
+    DEBUG_LOG("Compression successful (output size: %zu).", *out_len);
+    return 0;
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Compression path ignores deflate() return value and assumes single-call completion

In `compress_gz` you call `deflate(&strm, Z_FINISH)` once, ignore its return value, and assume completion. For larger inputs this can yield truncated output when `deflate` returns `Z_OK` or `Z_BUF_ERROR`. Consider looping on `deflate` until `Z_STREAM_END` (or an error) and handling unexpected statuses, similar to `inflate` in `decompress_gz`.

```suggestion
int compress_gz(const unsigned char* src, size_t src_len, unsigned char** out, size_t* out_len) {
    DEBUG_LOG("Starting zlib compression (input size: %zu)...", src_len);
    z_stream strm = {0};
    int ret;

    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
        DEBUG_LOG("deflateInit2 failed.");
        return -1;
    }

    size_t bound = deflateBound(&strm, src_len) + 100;
    *out = malloc(bound);
    if (!*out) {
        DEBUG_LOG("Failed to allocate %zu bytes for compression buffer.", bound);
        deflateEnd(&strm);
        return -1;
    }

    strm.next_in  = (Bytef*)src;
    strm.avail_in = (uInt)src_len;
    strm.next_out = *out;
    strm.avail_out = (uInt)bound;

    do {
        ret = deflate(&strm, Z_FINISH);
        if (ret == Z_STREAM_ERROR) {
            DEBUG_LOG("deflate encountered Z_STREAM_ERROR.");
            deflateEnd(&strm);
            free(*out);
            *out = NULL;
            return -1;
        }
        /* With deflateBound() the buffer should be large enough, so
         * repeatedly returning Z_OK with no progress would be unexpected. */
        if (ret == Z_OK && strm.avail_out == 0) {
            DEBUG_LOG("deflate buffer exhausted before reaching Z_STREAM_END.");
            deflateEnd(&strm);
            free(*out);
            *out = NULL;
            return -1;
        }
    } while (ret == Z_OK);

    if (ret != Z_STREAM_END) {
        DEBUG_LOG("deflate did not reach Z_STREAM_END (ret=%d).", ret);
        deflateEnd(&strm);
        free(*out);
        *out = NULL;
        return -1;
    }

    *out_len = bound - strm.avail_out;
    deflateEnd(&strm);
    DEBUG_LOG("Compression successful (output size: %zu).", *out_len);
    return 0;
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread module_files/post-fs-data.sh
Comment thread module_files/customize.sh Outdated
Comment thread src/core/jni/csc_tool.c
Mzdyl and others added 3 commits February 25, 2026 10:59
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
@Mzdyl
Mzdyl merged commit f06efcf into main Feb 25, 2026
3 checks passed
@Mzdyl
Mzdyl deleted the 2.0 branch February 25, 2026 03:03
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.

1 participant