mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
feat(permissions): grant dangerous permissions via aapt + adb
Inspector parses uses-permission entries from `aapt dump permissions` and the granter shells out to `adb shell pm grant`. Both are pluggable so tests can drive logic without aapt or a device. Granter failures become warnings rather than errors — Android refuses non-runtime permissions and we'd rather keep going than abort the run.
This commit is contained in:
1 parent
b6b18c3681
commit
d856c40d7c
2 files changed
+252
No files matched your search
@@ -0,0 +1,125 @@
|
||||
package permissions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Inspector returns the list of uses-permission entries declared by the APK.
|
||||
type Inspector func(ctx context.Context, apkPath string) ([]string, error)
|
||||
|
||||
// Granter grants a single permission to the given package on the connected
|
||||
// device. Implementations typically wrap `adb shell pm grant`.
|
||||
type Granter func(ctx context.Context, packageName, permission string) error
|
||||
|
||||
// GrantDangerous filters declared permissions to the dangerous set and grants
|
||||
// each one via the supplied granter. Errors from individual grants are
|
||||
// collected as warnings rather than aborting — Android refuses to grant
|
||||
// non-runtime permissions and we prefer to soldier on.
|
||||
func GrantDangerous(
|
||||
ctx context.Context,
|
||||
apkPath, packageName string,
|
||||
inspector Inspector,
|
||||
granter Granter,
|
||||
) (granted []string, warnings []string, err error) {
|
||||
declared, err := inspector(ctx, apkPath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("inspect permissions: %w", err)
|
||||
}
|
||||
for _, permission := range declared {
|
||||
if !IsDangerous(permission) {
|
||||
continue
|
||||
}
|
||||
if err := granter(ctx, packageName, permission); err != nil {
|
||||
warnings = append(warnings, fmt.Sprintf("%s: %v", permission, err))
|
||||
continue
|
||||
}
|
||||
granted = append(granted, permission)
|
||||
}
|
||||
return granted, warnings, nil
|
||||
}
|
||||
|
||||
var aaptPermissionPattern = regexp.MustCompile(`uses-permission:\s+name='([^']+)'`)
|
||||
|
||||
// AaptInspector shells out to `aapt dump permissions` to list permissions.
|
||||
func AaptInspector(ctx context.Context, apkPath string) ([]string, error) {
|
||||
output, err := exec.CommandContext(ctx, "aapt", "dump", "permissions", apkPath).Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("aapt dump permissions: %w", err)
|
||||
}
|
||||
var permissions []string
|
||||
for _, match := range aaptPermissionPattern.FindAllStringSubmatch(string(output), -1) {
|
||||
permissions = append(permissions, match[1])
|
||||
}
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// AdbGranter returns a Granter that runs `adb shell pm grant` against the
|
||||
// supplied device serial (empty = default device).
|
||||
func AdbGranter(adbPath, deviceSerial string) Granter {
|
||||
return func(ctx context.Context, packageName, permission string) error {
|
||||
arguments := []string{"shell", "pm", "grant", packageName, permission}
|
||||
if deviceSerial != "" {
|
||||
arguments = append([]string{"-s", deviceSerial}, arguments...)
|
||||
}
|
||||
command := exec.CommandContext(ctx, adbPath, arguments...)
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("adb pm grant %s: %w (%s)", permission, err, strings.TrimSpace(string(output)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// dangerousPermissions captures Android's PROTECTION_DANGEROUS set as of
|
||||
// API 34. New runtime permissions added in later releases should be appended
|
||||
// here when needed.
|
||||
var dangerousPermissions = map[string]bool{
|
||||
"android.permission.READ_CALENDAR": true,
|
||||
"android.permission.WRITE_CALENDAR": true,
|
||||
"android.permission.CAMERA": true,
|
||||
"android.permission.READ_CONTACTS": true,
|
||||
"android.permission.WRITE_CONTACTS": true,
|
||||
"android.permission.GET_ACCOUNTS": true,
|
||||
"android.permission.ACCESS_FINE_LOCATION": true,
|
||||
"android.permission.ACCESS_COARSE_LOCATION": true,
|
||||
"android.permission.ACCESS_BACKGROUND_LOCATION": true,
|
||||
"android.permission.RECORD_AUDIO": true,
|
||||
"android.permission.READ_PHONE_STATE": true,
|
||||
"android.permission.READ_PHONE_NUMBERS": true,
|
||||
"android.permission.CALL_PHONE": true,
|
||||
"android.permission.ANSWER_PHONE_CALLS": true,
|
||||
"android.permission.READ_CALL_LOG": true,
|
||||
"android.permission.WRITE_CALL_LOG": true,
|
||||
"android.permission.ADD_VOICEMAIL": true,
|
||||
"android.permission.USE_SIP": true,
|
||||
"android.permission.PROCESS_OUTGOING_CALLS": true,
|
||||
"android.permission.BODY_SENSORS": true,
|
||||
"android.permission.SEND_SMS": true,
|
||||
"android.permission.RECEIVE_SMS": true,
|
||||
"android.permission.READ_SMS": true,
|
||||
"android.permission.RECEIVE_WAP_PUSH": true,
|
||||
"android.permission.RECEIVE_MMS": true,
|
||||
"android.permission.READ_EXTERNAL_STORAGE": true,
|
||||
"android.permission.WRITE_EXTERNAL_STORAGE": true,
|
||||
"android.permission.ACCESS_MEDIA_LOCATION": true,
|
||||
"android.permission.ACTIVITY_RECOGNITION": true,
|
||||
"android.permission.POST_NOTIFICATIONS": true,
|
||||
"android.permission.NEARBY_WIFI_DEVICES": true,
|
||||
"android.permission.READ_MEDIA_IMAGES": true,
|
||||
"android.permission.READ_MEDIA_VIDEO": true,
|
||||
"android.permission.READ_MEDIA_AUDIO": true,
|
||||
"android.permission.READ_MEDIA_VISUAL_USER_SELECTED": true,
|
||||
"android.permission.BLUETOOTH_CONNECT": true,
|
||||
"android.permission.BLUETOOTH_ADVERTISE": true,
|
||||
"android.permission.BLUETOOTH_SCAN": true,
|
||||
"android.permission.UWB_RANGING": true,
|
||||
"android.permission.BODY_SENSORS_BACKGROUND": true,
|
||||
}
|
||||
|
||||
func IsDangerous(permission string) bool {
|
||||
return dangerousPermissions[permission]
|
||||
}
|
||||
Reference in new issue
Block a user