karnel-termux 4.7.6 → 4.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -225
- package/install.sh +30 -9
- package/karnel/bin/karnel +2 -2
- package/karnel/cli/commands/backup.sh +10 -2
- package/karnel/cli/commands/brain.sh +28 -23
- package/karnel/cli/commands/cleanup.sh +10 -4
- package/karnel/cli/commands/deploy.sh +53 -0
- package/karnel/cli/commands/doctor/code.sh +225 -0
- package/karnel/cli/commands/doctor/code_detect.sh +184 -0
- package/karnel/cli/commands/doctor/code_exec.sh +278 -0
- package/karnel/cli/commands/doctor/code_langs.sh +229 -0
- package/karnel/cli/commands/doctor/fixes.sh +63 -46
- package/karnel/cli/commands/doctor.sh +222 -80
- package/karnel/cli/commands/env.sh +2 -0
- package/karnel/cli/commands/ia.sh +12 -2
- package/karnel/cli/commands/init.sh +17 -9
- package/karnel/cli/commands/install.sh +90 -54
- package/karnel/cli/commands/list.sh +36 -0
- package/karnel/cli/commands/pg.sh +21 -6
- package/karnel/cli/commands/reinstall.sh +99 -0
- package/karnel/cli/commands/restore.sh +43 -3
- package/karnel/cli/commands/search.sh +2 -2
- package/karnel/cli/commands/update.sh +6 -1
- package/karnel/cli/commands/upgrade.sh +8 -1
- package/karnel/cli/commands/voice.sh +2 -1
- package/karnel/cli/karnel.sh +11 -5
- package/karnel/modules/games.sh +11 -0
- package/karnel/tools/ai/all.sh +1 -0
- package/karnel/tools/ai/copilot-termux/README.md +40 -0
- package/karnel/tools/ai/copilot-termux/install.sh +80 -0
- package/karnel/tools/ai/copilot-termux/package.json +20 -0
- package/karnel/tools/db/all.sh +2 -0
- package/karnel/tools/deploy/railway/install.sh +2 -2
- package/karnel/tools/dev/all.sh +8 -0
- package/karnel/tools/dev/applaunch/__pycache__/applaunch.cpython-314.pyc +0 -0
- package/karnel/tools/dev/applaunch/applaunch.py +434 -0
- package/karnel/tools/dev/applaunch/install.sh +51 -0
- package/karnel/tools/dev/fconv/__pycache__/fconv.cpython-314.pyc +0 -0
- package/karnel/tools/dev/fconv/fconv.py +749 -0
- package/karnel/tools/dev/fconv/install.sh +51 -0
- package/karnel/tools/dev/filecheck/__pycache__/filecheck.cpython-314.pyc +0 -0
- package/karnel/tools/dev/filecheck/filecheck.py +429 -0
- package/karnel/tools/dev/filecheck/install.sh +51 -0
- package/karnel/tools/dev/notes/__pycache__/notes.cpython-314.pyc +0 -0
- package/karnel/tools/dev/notes/install.sh +51 -0
- package/karnel/tools/dev/notes/notes.py +849 -0
- package/karnel/tools/dev/passman/__pycache__/passman.cpython-314.pyc +0 -0
- package/karnel/tools/dev/passman/install.sh +51 -0
- package/karnel/tools/dev/passman/passman.py +536 -0
- package/karnel/tools/dev/splash/__pycache__/splash.cpython-314.pyc +0 -0
- package/karnel/tools/dev/splash/install.sh +51 -0
- package/karnel/tools/dev/splash/splash.py +188 -0
- package/karnel/tools/dev/treex/install.sh +51 -0
- package/karnel/tools/dev/treex/treex.py +2036 -0
- package/karnel/tools/dev/websites/__pycache__/websites.cpython-314.pyc +0 -0
- package/karnel/tools/dev/websites/install.sh +51 -0
- package/karnel/tools/dev/websites/websites.py +844 -0
- package/karnel/tools/games/all.sh +62 -0
- package/karnel/tools/games/arcade/__pycache__/arcade.cpython-314.pyc +0 -0
- package/karnel/tools/games/arcade/arcade.py +193 -0
- package/karnel/tools/games/arcade/install.sh +51 -0
- package/karnel/tools/games/buzz/__pycache__/buzz.cpython-314.pyc +0 -0
- package/karnel/tools/games/buzz/buzz.py +1264 -0
- package/karnel/tools/games/buzz/install.sh +51 -0
- package/karnel/tools/games/ctfgod/__pycache__/ctfgod.cpython-314.pyc +0 -0
- package/karnel/tools/games/ctfgod/ctfgod.py +1576 -0
- package/karnel/tools/games/ctfgod/install.sh +51 -0
- package/karnel/tools/games/detective/__pycache__/detective.cpython-314.pyc +0 -0
- package/karnel/tools/games/detective/detective.py +2091 -0
- package/karnel/tools/games/detective/install.sh +51 -0
- package/karnel/tools/games/pet-friends/__pycache__/pet-friends.cpython-314.pyc +0 -0
- package/karnel/tools/games/pet-friends/install.sh +51 -0
- package/karnel/tools/games/pet-friends/pet-friends.py +15802 -0
- package/karnel/tools/games/tamagotchi/__pycache__/tamagotchi.cpython-314.pyc +0 -0
- package/karnel/tools/games/tamagotchi/install.sh +51 -0
- package/karnel/tools/games/tamagotchi/tamagotchi.py +1224 -0
- package/karnel/tools/games/terminal-arcade/__pycache__/terminal-arcade.cpython-314.pyc +0 -0
- package/karnel/tools/games/terminal-arcade/terminal-arcade.py +193 -0
- package/karnel/tools/npm/all.sh +1 -0
- package/karnel/utils/log.sh +34 -12
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Copilot-Termux
|
|
2
|
+
|
|
3
|
+
GitHub Copilot CLI for Termux (Android aarch64).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g @bash0816/copilot-termux
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or via Karnel:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
karnel install ai --copilot-termux
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
copilot -p "how do I list files in Node.js?"
|
|
21
|
+
copilot -p "explain this shell script"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
First-time setup:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
copilot-termux setup
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Integration
|
|
31
|
+
|
|
32
|
+
- Registered in `karnel list ai`
|
|
33
|
+
- Scanned by `karnel ia sessions`
|
|
34
|
+
- Listed in `karnel ia routes`
|
|
35
|
+
- Checked by `karnel doctor`
|
|
36
|
+
|
|
37
|
+
## Links
|
|
38
|
+
|
|
39
|
+
- npm: `@bash0816/copilot-termux`
|
|
40
|
+
- Source: https://github.com/bash0816/Github-Copilot-Termux
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
import "@/utils/log"
|
|
4
|
+
import "@/utils/version"
|
|
5
|
+
|
|
6
|
+
LOG_FILE="$KARNEL_CACHE/install_ai.log"
|
|
7
|
+
|
|
8
|
+
install_copilot_termux() {
|
|
9
|
+
if command -v copilot &>/dev/null; then
|
|
10
|
+
log_info "Copilot-Termux is already installed"
|
|
11
|
+
return 2
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
log_info "Installing Copilot-Termux..."
|
|
15
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
16
|
+
|
|
17
|
+
if ! command -v npm &>/dev/null; then
|
|
18
|
+
log_error "npm is required. Install Node.js first: karnel install lang --nodejs"
|
|
19
|
+
return 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if ! npm install -g @bash0816/copilot-termux &>>"$LOG_FILE"; then
|
|
23
|
+
log_error "Failed to install Copilot-Termux via npm"
|
|
24
|
+
return 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
hash -r 2>/dev/null || true
|
|
28
|
+
|
|
29
|
+
if ! command -v copilot &>/dev/null; then
|
|
30
|
+
log_error "Copilot-Termux installed but binary not on PATH"
|
|
31
|
+
log_info "Try: hash -r; copilot --help"
|
|
32
|
+
return 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
log_success "Copilot-Termux installed"
|
|
36
|
+
log_info "Run: copilot -p \"your question\""
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
uninstall_copilot_termux() {
|
|
41
|
+
if ! command -v copilot &>/dev/null; then
|
|
42
|
+
log_info "Copilot-Termux is not installed"
|
|
43
|
+
return 2
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
log_info "Uninstalling Copilot-Termux..."
|
|
47
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
48
|
+
|
|
49
|
+
if npm uninstall -g @bash0816/copilot-termux &>>"$LOG_FILE"; then
|
|
50
|
+
log_success "Copilot-Termux uninstalled"
|
|
51
|
+
return 0
|
|
52
|
+
else
|
|
53
|
+
log_error "Failed to uninstall Copilot-Termux"
|
|
54
|
+
return 1
|
|
55
|
+
fi
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
update_copilot_termux() {
|
|
59
|
+
_check_update_needed "Copilot-Termux" \
|
|
60
|
+
"$(_get_installed_npm_version @bash0816/copilot-termux)" \
|
|
61
|
+
"$(_get_remote_npm_version @bash0816/copilot-termux)" \
|
|
62
|
+
_do_update_copilot_termux
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_do_update_copilot_termux() {
|
|
66
|
+
log_info "Updating Copilot-Termux..."
|
|
67
|
+
|
|
68
|
+
if npm update -g @bash0816/copilot-termux &>>"$LOG_FILE"; then
|
|
69
|
+
log_success "Copilot-Termux updated"
|
|
70
|
+
return 0
|
|
71
|
+
else
|
|
72
|
+
log_error "Failed to update Copilot-Termux"
|
|
73
|
+
return 1
|
|
74
|
+
fi
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
reinstall_copilot_termux() {
|
|
78
|
+
uninstall_copilot_termux
|
|
79
|
+
install_copilot_termux
|
|
80
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bash0816/copilot-termux",
|
|
3
|
+
"version": "1.0.70",
|
|
4
|
+
"description": "GitHub Copilot CLI for Termux (Android aarch64)",
|
|
5
|
+
"bin": {
|
|
6
|
+
"copilot": "bin/copilot"
|
|
7
|
+
},
|
|
8
|
+
"license": "GPL-3.0-only",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/bash0816/Github-Copilot-Termux.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"termux",
|
|
15
|
+
"github-copilot",
|
|
16
|
+
"cli",
|
|
17
|
+
"ai",
|
|
18
|
+
"karnel"
|
|
19
|
+
]
|
|
20
|
+
}
|
package/karnel/tools/db/all.sh
CHANGED
|
@@ -29,7 +29,7 @@ _install_railway_manual_impl() {
|
|
|
29
29
|
log_info "Downloading Railway ${version} for ARM64 Linux..."
|
|
30
30
|
if ! curl -fsSL --connect-timeout 15 "$tarball_url" -o "$RAILWAY_DATA_DIR/railway.tar.gz" 2>>"$LOG_FILE"; then
|
|
31
31
|
log_warn "ARM64 binary not available. Railway CLI does not provide ARM Linux builds."
|
|
32
|
-
log_info "
|
|
32
|
+
log_info "Use 'npx @railway/cli' instead."
|
|
33
33
|
return 1
|
|
34
34
|
fi
|
|
35
35
|
|
|
@@ -77,7 +77,7 @@ install_railway() {
|
|
|
77
77
|
|
|
78
78
|
log_error "Railway CLI installation failed. Railway does not provide official ARM Linux builds."
|
|
79
79
|
log_info "Workaround: use 'npx @railway/cli' instead of 'railway' command."
|
|
80
|
-
log_info "Or install Railway on a
|
|
80
|
+
log_info "Or install Railway on a cloud machine."
|
|
81
81
|
return 1
|
|
82
82
|
}
|
|
83
83
|
|
package/karnel/tools/dev/all.sh
CHANGED
|
Binary file
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
#!/data/data/com.termux/files/usr/bin/python3
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import re
|
|
5
|
+
import concurrent.futures
|
|
6
|
+
import tempfile
|
|
7
|
+
import sys
|
|
8
|
+
import zipfile
|
|
9
|
+
import json
|
|
10
|
+
import xml.etree.ElementTree as ET
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
# NOTE: This script is now intended to be run *directly* on an Android device
|
|
15
|
+
# inside a shell environment like Termux.
|
|
16
|
+
|
|
17
|
+
def get_installed_apps():
|
|
18
|
+
"""
|
|
19
|
+
Retrieves a mapping of package names to their APK paths using a native shell command.
|
|
20
|
+
"""
|
|
21
|
+
package_map = {}
|
|
22
|
+
cmd = "cmd package list packages --user 0 -e -f"
|
|
23
|
+
try:
|
|
24
|
+
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
25
|
+
output = result.stdout.strip()
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print("Error running package list command:", e)
|
|
28
|
+
return package_map
|
|
29
|
+
|
|
30
|
+
for line in output.splitlines():
|
|
31
|
+
line = line.strip()
|
|
32
|
+
if line.startswith("package:"):
|
|
33
|
+
line = line[len("package:"):]
|
|
34
|
+
# Replace '.apk=' with '.apk ' for easier splitting
|
|
35
|
+
line = line.replace(".apk=", ".apk ")
|
|
36
|
+
if " " in line:
|
|
37
|
+
apk_path, package = line.split(" ", 1)
|
|
38
|
+
package_map[package.strip()] = apk_path.strip()
|
|
39
|
+
return package_map
|
|
40
|
+
|
|
41
|
+
def get_launchable_packages():
|
|
42
|
+
"""
|
|
43
|
+
Retrieves a dictionary mapping package names to their full launchable component
|
|
44
|
+
using a native shell command.
|
|
45
|
+
"""
|
|
46
|
+
launchable = {}
|
|
47
|
+
cmd = "cmd package query-activities --user 0 --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER"
|
|
48
|
+
try:
|
|
49
|
+
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
50
|
+
output = result.stdout.strip()
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print("Error running query-activities command:", e)
|
|
53
|
+
return launchable
|
|
54
|
+
|
|
55
|
+
for line in output.splitlines():
|
|
56
|
+
line = line.strip()
|
|
57
|
+
if '/' in line:
|
|
58
|
+
parts = line.split('/', 1)
|
|
59
|
+
if len(parts) == 2:
|
|
60
|
+
pkg, act = parts
|
|
61
|
+
component = f"{pkg}/{act}"
|
|
62
|
+
launchable[pkg] = component
|
|
63
|
+
return launchable
|
|
64
|
+
|
|
65
|
+
def get_app_label(apk_path, package):
|
|
66
|
+
"""
|
|
67
|
+
Uses aapt (must be available in the shell PATH, e.g., via Android SDK tools)
|
|
68
|
+
to extract the friendly label from the APK.
|
|
69
|
+
"""
|
|
70
|
+
cmd = f"aapt dump badging \"{apk_path}\""
|
|
71
|
+
try:
|
|
72
|
+
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
73
|
+
match = re.search(r"application: label='([^']+)'", result.stdout)
|
|
74
|
+
if match:
|
|
75
|
+
return match.group(1)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
print(f"Error running aapt on {apk_path}: {e}")
|
|
78
|
+
return package
|
|
79
|
+
|
|
80
|
+
def select_option(options, header):
|
|
81
|
+
"""
|
|
82
|
+
Uses fzf to let the user select one option from a list.
|
|
83
|
+
"""
|
|
84
|
+
with tempfile.NamedTemporaryFile("w", delete=False) as tmp:
|
|
85
|
+
tmp.write("\n".join(options))
|
|
86
|
+
tmp.flush()
|
|
87
|
+
tmp_name = tmp.name
|
|
88
|
+
|
|
89
|
+
env = os.environ.copy()
|
|
90
|
+
env["FZF_DEFAULT_COMMAND"] = f"cat {tmp_name}"
|
|
91
|
+
try:
|
|
92
|
+
# Read/Write directly from Termux TTY
|
|
93
|
+
with open("/dev/tty", "r") as tty_in, open("/dev/tty", "w") as tty_out:
|
|
94
|
+
result = subprocess.run(
|
|
95
|
+
["fzf", "--header", header],
|
|
96
|
+
env=env,
|
|
97
|
+
stdin=tty_in,
|
|
98
|
+
stdout=subprocess.PIPE,
|
|
99
|
+
stderr=tty_out,
|
|
100
|
+
text=True
|
|
101
|
+
)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print("Error running fzf:", e)
|
|
104
|
+
os.unlink(tmp_name)
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
os.unlink(tmp_name)
|
|
108
|
+
return result.stdout.strip()
|
|
109
|
+
|
|
110
|
+
class SimpleAppPermInspector:
|
|
111
|
+
"""
|
|
112
|
+
Simplified version of App Perm Inspector for integration
|
|
113
|
+
"""
|
|
114
|
+
def __init__(self):
|
|
115
|
+
self.dangerous_permissions = self.load_dangerous_permissions()
|
|
116
|
+
self.tracker_databases = self.load_tracker_databases()
|
|
117
|
+
|
|
118
|
+
def load_dangerous_permissions(self):
|
|
119
|
+
"""Load dangerous Android permissions database"""
|
|
120
|
+
dangerous_perms = {
|
|
121
|
+
"ACCESS_FINE_LOCATION": {"risk": "high", "description": "Access precise location (GPS)", "threat": "Can track your exact location"},
|
|
122
|
+
"ACCESS_COARSE_LOCATION": {"risk": "medium", "description": "Access approximate location", "threat": "Can track your general area"},
|
|
123
|
+
"CAMERA": {"risk": "high", "description": "Access device camera", "threat": "Can take pictures/video without consent"},
|
|
124
|
+
"RECORD_AUDIO": {"risk": "high", "description": "Record audio", "threat": "Can record conversations"},
|
|
125
|
+
"READ_CONTACTS": {"risk": "high", "description": "Read contact list", "threat": "Can access all your contacts"},
|
|
126
|
+
"WRITE_CONTACTS": {"risk": "medium", "description": "Modify contacts", "threat": "Can alter your contact data"},
|
|
127
|
+
"SEND_SMS": {"risk": "critical", "description": "Send SMS messages", "threat": "Can send premium rate SMS"},
|
|
128
|
+
"READ_SMS": {"risk": "critical", "description": "Read SMS messages", "threat": "Can read all your messages"},
|
|
129
|
+
"CALL_PHONE": {"risk": "critical", "description": "Make phone calls", "threat": "Can make calls without consent"},
|
|
130
|
+
"READ_PHONE_STATE": {"risk": "high", "description": "Access phone information", "threat": "Can get phone number, IMEI, etc."},
|
|
131
|
+
"READ_EXTERNAL_STORAGE": {"risk": "medium", "description": "Read external storage", "threat": "Can access your files, photos, documents"},
|
|
132
|
+
"WRITE_EXTERNAL_STORAGE": {"risk": "medium", "description": "Write to external storage", "threat": "Can modify or delete your files"},
|
|
133
|
+
"ACCESS_BACKGROUND_LOCATION": {"risk": "critical", "description": "Access location in background", "threat": "Can track location even when app not in use"},
|
|
134
|
+
"BODY_SENSORS": {"risk": "high", "description": "Access health sensors", "threat": "Can access heart rate, step count, etc."}
|
|
135
|
+
}
|
|
136
|
+
return dangerous_perms
|
|
137
|
+
|
|
138
|
+
def load_tracker_databases(self):
|
|
139
|
+
"""Load known tracker libraries"""
|
|
140
|
+
trackers = {
|
|
141
|
+
"Google Analytics": {"signatures": ["com.google.analytics", "com.google.android.gms.analytics"], "category": "Analytics", "privacy_impact": "high"},
|
|
142
|
+
"Facebook SDK": {"signatures": ["com.facebook", "com.facebook.ads"], "category": "Advertising", "privacy_impact": "high"},
|
|
143
|
+
"Firebase Analytics": {"signatures": ["com.google.firebase.analytics"], "category": "Analytics", "privacy_impact": "medium"},
|
|
144
|
+
"AdMob": {"signatures": ["com.google.android.gms.ads"], "category": "Advertising", "privacy_impact": "high"},
|
|
145
|
+
"Flurry": {"signatures": ["com.flurry.android"], "category": "Analytics", "privacy_impact": "medium"},
|
|
146
|
+
"AppsFlyer": {"signatures": ["com.appsflyer"], "category": "Analytics", "privacy_impact": "high"}
|
|
147
|
+
}
|
|
148
|
+
return trackers
|
|
149
|
+
|
|
150
|
+
def extract_permissions_basic(self, apk_path):
|
|
151
|
+
"""Extract permissions from APK using basic analysis"""
|
|
152
|
+
try:
|
|
153
|
+
with zipfile.ZipFile(apk_path, 'r') as apk_zip:
|
|
154
|
+
permission_pattern = r'android\.permission\.[A-Z_]+'
|
|
155
|
+
permissions_found = set()
|
|
156
|
+
|
|
157
|
+
for file_name in apk_zip.namelist():
|
|
158
|
+
if file_name.endswith('.xml') or file_name.endswith('.dex') or file_name.endswith('.arsc'):
|
|
159
|
+
try:
|
|
160
|
+
with apk_zip.open(file_name) as file:
|
|
161
|
+
content = file.read().decode('utf-8', errors='ignore')
|
|
162
|
+
found_perms = re.findall(permission_pattern, content)
|
|
163
|
+
permissions_found.update(found_perms)
|
|
164
|
+
except:
|
|
165
|
+
continue
|
|
166
|
+
|
|
167
|
+
return list(permissions_found)
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(f"❌ Permission extraction failed: {e}")
|
|
171
|
+
return []
|
|
172
|
+
|
|
173
|
+
def detect_trackers(self, apk_path):
|
|
174
|
+
"""Detect tracking libraries in APK"""
|
|
175
|
+
trackers_found = []
|
|
176
|
+
try:
|
|
177
|
+
with zipfile.ZipFile(apk_path, 'r') as apk_zip:
|
|
178
|
+
all_files = apk_zip.namelist()
|
|
179
|
+
|
|
180
|
+
for tracker_name, tracker_info in self.tracker_databases.items():
|
|
181
|
+
for signature in tracker_info['signatures']:
|
|
182
|
+
if any(signature in file_path for file_path in all_files):
|
|
183
|
+
trackers_found.append({
|
|
184
|
+
'name': tracker_name,
|
|
185
|
+
'category': tracker_info['category'],
|
|
186
|
+
'privacy_impact': tracker_info['privacy_impact']
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
return trackers_found
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
print(f"❌ Tracker detection error: {e}")
|
|
193
|
+
return []
|
|
194
|
+
|
|
195
|
+
def analyze_app_permissions(self, apk_path, app_name):
|
|
196
|
+
"""Main analysis function"""
|
|
197
|
+
print(f"\n🔍 Analyzing: {app_name}")
|
|
198
|
+
print("="*60)
|
|
199
|
+
|
|
200
|
+
# Extract permissions
|
|
201
|
+
all_permissions = self.extract_permissions_basic(apk_path)
|
|
202
|
+
|
|
203
|
+
# Categorize permissions
|
|
204
|
+
dangerous_perms = []
|
|
205
|
+
normal_perms = []
|
|
206
|
+
|
|
207
|
+
for perm in all_permissions:
|
|
208
|
+
perm_name = perm.split('.')[-1] if '.' in perm else perm
|
|
209
|
+
if perm_name in self.dangerous_permissions:
|
|
210
|
+
dangerous_perms.append({
|
|
211
|
+
'name': perm,
|
|
212
|
+
'risk': self.dangerous_permissions[perm_name]['risk'],
|
|
213
|
+
'description': self.dangerous_permissions[perm_name]['description'],
|
|
214
|
+
'threat': self.dangerous_permissions[perm_name]['threat']
|
|
215
|
+
})
|
|
216
|
+
else:
|
|
217
|
+
normal_perms.append(perm)
|
|
218
|
+
|
|
219
|
+
# Detect trackers
|
|
220
|
+
trackers = self.detect_trackers(apk_path)
|
|
221
|
+
|
|
222
|
+
# Calculate risk score
|
|
223
|
+
risk_score = 0
|
|
224
|
+
for perm in dangerous_perms:
|
|
225
|
+
if perm['risk'] == 'critical': risk_score += 10
|
|
226
|
+
elif perm['risk'] == 'high': risk_score += 7
|
|
227
|
+
elif perm['risk'] == 'medium': risk_score += 4
|
|
228
|
+
|
|
229
|
+
risk_level = "HIGH" if risk_score >= 20 else "MEDIUM" if risk_score >= 10 else "LOW"
|
|
230
|
+
|
|
231
|
+
# Display results
|
|
232
|
+
print(f"\n📊 SECURITY ANALYSIS RESULTS")
|
|
233
|
+
print("-" * 40)
|
|
234
|
+
print(f"App: {app_name}")
|
|
235
|
+
print(f"Risk Level: {risk_level} ({risk_score} points)")
|
|
236
|
+
print(f"Total Permissions: {len(all_permissions)}")
|
|
237
|
+
print(f"Dangerous Permissions: {len(dangerous_perms)}")
|
|
238
|
+
print(f"Trackers Found: {len(trackers)}")
|
|
239
|
+
|
|
240
|
+
if dangerous_perms:
|
|
241
|
+
print(f"\n🔴 DANGEROUS PERMISSIONS:")
|
|
242
|
+
for perm in dangerous_perms:
|
|
243
|
+
risk_color = "🔴" if perm['risk'] == 'critical' else "🟡" if perm['risk'] == 'high' else "🟠"
|
|
244
|
+
print(f" {risk_color} {perm['name']}")
|
|
245
|
+
print(f" Description: {perm['description']}")
|
|
246
|
+
print(f" Threat: {perm['threat']}")
|
|
247
|
+
|
|
248
|
+
if trackers:
|
|
249
|
+
print(f"\n📊 TRACKING LIBRARIES:")
|
|
250
|
+
for tracker in trackers:
|
|
251
|
+
impact_icon = "🔴" if tracker['privacy_impact'] == 'high' else "🟡"
|
|
252
|
+
print(f" {impact_icon} {tracker['name']} ({tracker['category']})")
|
|
253
|
+
|
|
254
|
+
if not dangerous_perms and not trackers:
|
|
255
|
+
print(f"\n✅ No major security issues detected!")
|
|
256
|
+
|
|
257
|
+
print("\n" + "="*60)
|
|
258
|
+
|
|
259
|
+
# Save report
|
|
260
|
+
self.save_analysis_report(app_name, apk_path, dangerous_perms, trackers, risk_level, risk_score)
|
|
261
|
+
|
|
262
|
+
def save_analysis_report(self, app_name, apk_path, dangerous_perms, trackers, risk_level, risk_score):
|
|
263
|
+
"""Save analysis report to file"""
|
|
264
|
+
try:
|
|
265
|
+
base_storage = os.path.join(os.path.expanduser('~'), 'storage', 'shared')
|
|
266
|
+
reports_dir = os.path.join(base_storage, 'Download', "App_Security_Reports")
|
|
267
|
+
os.makedirs(reports_dir, exist_ok=True)
|
|
268
|
+
|
|
269
|
+
safe_name = "".join(c for c in app_name if c.isalnum() or c in (' ', '-', '_')).rstrip()
|
|
270
|
+
report_file = os.path.join(reports_dir, f"{safe_name}_security_report.txt")
|
|
271
|
+
|
|
272
|
+
with open(report_file, 'w', encoding='utf-8') as f:
|
|
273
|
+
f.write("="*60 + "\n")
|
|
274
|
+
f.write(" APP SECURITY ANALYSIS REPORT\n")
|
|
275
|
+
f.write("="*60 + "\n\n")
|
|
276
|
+
|
|
277
|
+
f.write(f"App Name: {app_name}\n")
|
|
278
|
+
f.write(f"APK Path: {apk_path}\n")
|
|
279
|
+
f.write(f"Analysis Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
280
|
+
f.write(f"Risk Level: {risk_level}\n")
|
|
281
|
+
f.write(f"Risk Score: {risk_score}\n\n")
|
|
282
|
+
|
|
283
|
+
if dangerous_perms:
|
|
284
|
+
f.write("DANGEROUS PERMISSIONS:\n")
|
|
285
|
+
f.write("-" * 40 + "\n")
|
|
286
|
+
for perm in dangerous_perms:
|
|
287
|
+
f.write(f"• {perm['name']}\n")
|
|
288
|
+
f.write(f" Risk: {perm['risk'].upper()} | {perm['description']}\n")
|
|
289
|
+
f.write(f" Threat: {perm['threat']}\n\n")
|
|
290
|
+
|
|
291
|
+
if trackers:
|
|
292
|
+
f.write("TRACKING LIBRARIES:\n")
|
|
293
|
+
f.write("-" * 40 + "\n")
|
|
294
|
+
for tracker in trackers:
|
|
295
|
+
f.write(f"• {tracker['name']}\n")
|
|
296
|
+
f.write(f" Category: {tracker['category']}\n")
|
|
297
|
+
f.write(f" Privacy Impact: {tracker['privacy_impact'].upper()}\n\n")
|
|
298
|
+
|
|
299
|
+
f.write("="*60 + "\n")
|
|
300
|
+
f.write("Report generated by Android App Launcher\n")
|
|
301
|
+
f.write("="*60 + "\n")
|
|
302
|
+
|
|
303
|
+
print(f"📄 Report saved: {report_file}")
|
|
304
|
+
|
|
305
|
+
except Exception as e:
|
|
306
|
+
print(f"❌ Error saving report: {e}")
|
|
307
|
+
|
|
308
|
+
def analyze_app_permissions_option(apk_path, package_name, app_label):
|
|
309
|
+
"""
|
|
310
|
+
Handle the App Perm Inspector option
|
|
311
|
+
"""
|
|
312
|
+
inspector = SimpleAppPermInspector()
|
|
313
|
+
inspector.analyze_app_permissions(apk_path, app_label)
|
|
314
|
+
|
|
315
|
+
def main():
|
|
316
|
+
package_map = get_installed_apps()
|
|
317
|
+
if not package_map:
|
|
318
|
+
print("No apps found. Please check your permissions or environment.")
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
launchable_map = get_launchable_packages()
|
|
322
|
+
if not launchable_map:
|
|
323
|
+
print("No launchable apps found.")
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
filtered_map = {pkg: apk for pkg, apk in package_map.items() if pkg in launchable_map}
|
|
327
|
+
if not filtered_map:
|
|
328
|
+
print("No launchable apps found in installed apps.")
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
# Use ThreadPoolExecutor to parallelize the extraction of app labels
|
|
332
|
+
apps = []
|
|
333
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
334
|
+
future_to_pkg = {
|
|
335
|
+
executor.submit(get_app_label, apk_path, pkg): pkg
|
|
336
|
+
for pkg, apk_path in filtered_map.items()
|
|
337
|
+
}
|
|
338
|
+
for future in concurrent.futures.as_completed(future_to_pkg):
|
|
339
|
+
pkg = future_to_pkg[future]
|
|
340
|
+
try:
|
|
341
|
+
label = future.result()
|
|
342
|
+
except Exception as exc:
|
|
343
|
+
print(f"Error retrieving label for {pkg}: {exc}")
|
|
344
|
+
label = pkg
|
|
345
|
+
apps.append((label, pkg))
|
|
346
|
+
|
|
347
|
+
apps.sort(key=lambda x: x[0].lower())
|
|
348
|
+
|
|
349
|
+
app_labels = [label for label, _ in apps]
|
|
350
|
+
selected_label = select_option(app_labels, "Select a launchable app to manage:")
|
|
351
|
+
if not selected_label:
|
|
352
|
+
print("No selection made.")
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
selected_package = next((pkg for label, pkg in apps if label == selected_label), None)
|
|
356
|
+
if not selected_package:
|
|
357
|
+
print("Selected app not found.")
|
|
358
|
+
return
|
|
359
|
+
|
|
360
|
+
# UPDATED OPTIONS: Added "App Perm Inspector" option
|
|
361
|
+
options = ["Launch", "App Info", "Uninstall", "Extract APK", "App Perm Inspector"]
|
|
362
|
+
chosen_option = select_option(options, f"Selected '{selected_label}'. Choose an action:")
|
|
363
|
+
if not chosen_option:
|
|
364
|
+
print("No option selected.")
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
if chosen_option == "Launch":
|
|
368
|
+
component = launchable_map.get(selected_package)
|
|
369
|
+
if component:
|
|
370
|
+
print(f"Launching '{selected_label}' ({selected_package})...")
|
|
371
|
+
os.system(f"am start -n {component} > /dev/null 2>&1")
|
|
372
|
+
else:
|
|
373
|
+
print("Launch component not found for the selected app.")
|
|
374
|
+
|
|
375
|
+
elif chosen_option == "App Info":
|
|
376
|
+
print(f"Opening App Info for '{selected_label}' ({selected_package})...")
|
|
377
|
+
os.system(f"am start -a android.settings.APPLICATION_DETAILS_SETTINGS -d package:{selected_package} > /dev/null 2>&1")
|
|
378
|
+
|
|
379
|
+
elif chosen_option == "Uninstall":
|
|
380
|
+
print(f"Uninstalling '{selected_label}' ({selected_package}) using the system uninstall method...")
|
|
381
|
+
os.system(f"am start -a android.intent.action.DELETE -d package:{selected_package} > /dev/null 2>&1")
|
|
382
|
+
|
|
383
|
+
elif chosen_option == "Extract APK":
|
|
384
|
+
apk_path_on_device = package_map.get(selected_package)
|
|
385
|
+
if apk_path_on_device:
|
|
386
|
+
# Construct the target directory path
|
|
387
|
+
base_storage = os.path.join(os.path.expanduser('~'), 'storage', 'shared')
|
|
388
|
+
downloads_dir = os.path.join(base_storage, 'Download')
|
|
389
|
+
target_dir = os.path.join(downloads_dir, "Extracted APK's")
|
|
390
|
+
destination_file = os.path.join(target_dir, f"{selected_package}.apk")
|
|
391
|
+
|
|
392
|
+
print(f"Ensuring destination directory exists: '{target_dir}'...")
|
|
393
|
+
|
|
394
|
+
# Create the destination directory recursively if it doesn't exist
|
|
395
|
+
if os.system(f"mkdir -p \"{target_dir}\"") != 0:
|
|
396
|
+
print("Error: Could not create the destination directory. Check Termux storage permissions ('termux-setup-storage').")
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
print(f"Copying APK from '{apk_path_on_device}' to '{destination_file}'...")
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
# Use native Android 'cp' (copy) command
|
|
403
|
+
copy_cmd = ["cp", apk_path_on_device, destination_file]
|
|
404
|
+
result = subprocess.run(copy_cmd, capture_output=True, text=True)
|
|
405
|
+
|
|
406
|
+
if result.returncode == 0:
|
|
407
|
+
print(f"Successfully extracted APK to {destination_file}")
|
|
408
|
+
else:
|
|
409
|
+
print(f"Error extracting APK. Copy command failed with return code {result.returncode}.")
|
|
410
|
+
if result.stderr:
|
|
411
|
+
print(f"Error details: {result.stderr.strip()}")
|
|
412
|
+
except FileNotFoundError:
|
|
413
|
+
print("Error: 'cp' command not found. This should not happen in a standard Termux shell.")
|
|
414
|
+
except Exception as e:
|
|
415
|
+
print(f"An unexpected error occurred during copy: {e}")
|
|
416
|
+
else:
|
|
417
|
+
print("APK path not found for the selected app.")
|
|
418
|
+
|
|
419
|
+
elif chosen_option == "App Perm Inspector":
|
|
420
|
+
apk_path_on_device = package_map.get(selected_package)
|
|
421
|
+
if apk_path_on_device:
|
|
422
|
+
analyze_app_permissions_option(apk_path_on_device, selected_package, selected_label)
|
|
423
|
+
else:
|
|
424
|
+
print("APK path not found for the selected app.")
|
|
425
|
+
|
|
426
|
+
else:
|
|
427
|
+
print("No valid option selected.")
|
|
428
|
+
|
|
429
|
+
# After performing the selected action, run Settings.py
|
|
430
|
+
print("\nLaunching settings...")
|
|
431
|
+
os.system(f"{sys.executable} Settings.py")
|
|
432
|
+
|
|
433
|
+
if __name__ == "__main__":
|
|
434
|
+
main()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
import "@/utils/log"
|
|
4
|
+
import "@/utils/version"
|
|
5
|
+
|
|
6
|
+
LOG_FILE="$KARNEL_CACHE/install_dev.log"
|
|
7
|
+
|
|
8
|
+
install_applaunch() {
|
|
9
|
+
local TOOL_DIR="$KARNEL_PATH/tools/dev/applaunch"
|
|
10
|
+
local BIN_NAME="applaunch"
|
|
11
|
+
local DOWNLOAD_URL="https://raw.githubusercontent.com/dedsec1121fk/DedSec/main/Scripts/Other%20Tools/Android%20App%20Launcher.py"
|
|
12
|
+
if command -v applaunch &>/dev/null; then
|
|
13
|
+
log_info "App Launcher is already installed"
|
|
14
|
+
return 2
|
|
15
|
+
fi
|
|
16
|
+
log_info "Installing App Launcher..."
|
|
17
|
+
mkdir -p "$(dirname "$LOG_FILE")" "$TOOL_DIR"
|
|
18
|
+
if ! command -v python3 &>/dev/null; then pkg install python -y &>>"$LOG_FILE" || return 1; fi
|
|
19
|
+
pkg install termux-api -y &>>"$LOG_FILE" || true
|
|
20
|
+
curl -sL "$DOWNLOAD_URL" -o "$TOOL_DIR/$BIN_NAME.py" || return 1
|
|
21
|
+
chmod +x "$TOOL_DIR/$BIN_NAME.py"
|
|
22
|
+
sed -i "1s|.*|#\!$PREFIX/bin/python3|" "$TOOL_DIR/$BIN_NAME.py"
|
|
23
|
+
ln -sf "$TOOL_DIR/$BIN_NAME.py" "$PREFIX/bin/$BIN_NAME"
|
|
24
|
+
log_success "App Launcher installed"
|
|
25
|
+
return 0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
uninstall_applaunch() {
|
|
29
|
+
local BIN_NAME="applaunch"
|
|
30
|
+
local TOOL_DIR="$KARNEL_PATH/tools/dev/applaunch"
|
|
31
|
+
if ! command -v applaunch &>/dev/null; then log_info "App Launcher is not installed"; return 2; fi
|
|
32
|
+
log_info "Uninstalling App Launcher..."
|
|
33
|
+
rm -f "$PREFIX/bin/$BIN_NAME" "$TOOL_DIR/$BIN_NAME.py"
|
|
34
|
+
log_success "App Launcher uninstalled"
|
|
35
|
+
return 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
update_applaunch() {
|
|
39
|
+
local TOOL_DIR="$KARNEL_PATH/tools/dev/applaunch"
|
|
40
|
+
local BIN_NAME="applaunch"
|
|
41
|
+
local DOWNLOAD_URL="https://raw.githubusercontent.com/dedsec1121fk/DedSec/main/Scripts/Other%20Tools/Android%20App%20Launcher.py"
|
|
42
|
+
log_info "Updating App Launcher..."
|
|
43
|
+
curl -sL "$DOWNLOAD_URL" -o "$TOOL_DIR/$BIN_NAME.py" || return 1
|
|
44
|
+
log_success "App Launcher updated"
|
|
45
|
+
return 0
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
reinstall_applaunch() {
|
|
49
|
+
uninstall_applaunch
|
|
50
|
+
install_applaunch
|
|
51
|
+
}
|
|
Binary file
|