@leejungkiin/awkit 1.7.1 → 1.7.4
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/bin/awk.js +576 -84
- package/core/CLAUDE.md +1 -1
- package/core/GEMINI.md +148 -167
- package/core/GEMINI.md.bak +149 -116
- package/core/skill-runtime-manifest.json +3 -0
- package/docs/Claude Fable 5.md +3826 -0
- package/docs/android_kotlin_system_instruction.md +210 -0
- package/docs/brainstorm_ponytail_integration.md +146 -0
- package/docs/brainstorm_smart_setup.md +113 -0
- package/docs/deep-research-report (1).md +293 -0
- package/docs/history/GEMINI.v1.md +135 -0
- package/docs/history/brainstorm_antigravity_unified_architecture.v1.md +105 -0
- package/docs/history/implementation_plan.v1.md +58 -0
- package/package.json +4 -1
- package/scripts/artifact-storage.js +130 -0
- package/scripts/automation-gate.js +35 -2
- package/scripts/claude-plan.js +76 -0
- package/scripts/dependency-manager.js +210 -0
- package/scripts/exec-rtk.js +11 -5
- package/scripts/i18n-helper.js +381 -0
- package/scripts/multi-model-pipeline.js +144 -0
- package/skill-packs/mobile-ios/pack.json +4 -2
- package/skill-packs/reverse-engineering/pack.json +1 -0
- package/skills/CATALOG.md +20 -0
- package/skills/GEMINI.md +9 -1
- package/skills/TRIGGER_INDEX.md +10 -0
- package/skills/ai-music/SKILL.md +275 -0
- package/skills/android-re-analyzer/SKILL.md +238 -0
- package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
- package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
- package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
- package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
- package/skills/android-re-analyzer/references/setup-guide.md +221 -0
- package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
- package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
- package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
- package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
- package/skills/animal-island-ui-style/SKILL.md +1450 -0
- package/skills/app-store-review-agent/SKILL.md +164 -0
- package/skills/app-store-review-agent/references/guidelines/README.md +154 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/ai_apps.md +37 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/all_apps.md +50 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/crypto_finance.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/games.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/health_fitness.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/kids.md +27 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/macos.md +38 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/social_ugc.md +32 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/subscription_iap.md +34 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/vpn.md +18 -0
- package/skills/app-store-review-agent/references/rules/design/minimum_functionality.md +96 -0
- package/skills/app-store-review-agent/references/rules/design/sign_in_with_apple.md +54 -0
- package/skills/app-store-review-agent/references/rules/entitlements/unused_entitlements.md +83 -0
- package/skills/app-store-review-agent/references/rules/metadata/accurate_metadata.md +54 -0
- package/skills/app-store-review-agent/references/rules/metadata/apple_trademark.md +99 -0
- package/skills/app-store-review-agent/references/rules/metadata/china_storefront.md +72 -0
- package/skills/app-store-review-agent/references/rules/metadata/competitor_terms.md +56 -0
- package/skills/app-store-review-agent/references/rules/metadata/subscription_metadata.md +81 -0
- package/skills/app-store-review-agent/references/rules/privacy/privacy_manifest.md +84 -0
- package/skills/app-store-review-agent/references/rules/privacy/unnecessary_data.md +60 -0
- package/skills/app-store-review-agent/references/rules/subscription/misleading_pricing.md +63 -0
- package/skills/app-store-review-agent/references/rules/subscription/missing_tos_pp.md +54 -0
- package/skills/awf-ponytail/SKILL.md +91 -0
- package/skills/awf-ponytail-review/SKILL.md +67 -0
- package/skills/awf-session-restore/SKILL.md +3 -3
- package/skills/brainstorm-agent/SKILL.md +11 -2
- package/skills/brainstorm-agent/templates/brief-template.md +8 -0
- package/skills/claude-planner/SKILL.md +47 -0
- package/skills/code-review/SKILL.md +87 -0
- package/skills/expo-game-development/SKILL.md +163 -0
- package/skills/flutter/LICENSE.txt +202 -0
- package/skills/flutter/SKILL.md +127 -0
- package/skills/flutter-project-creater/LICENSE.txt +202 -0
- package/skills/flutter-project-creater/SKILL.md +106 -0
- package/skills/game-developer/SKILL.md +163 -0
- package/skills/game-developer/references/ecs-patterns.md +501 -0
- package/skills/game-developer/references/multiplayer-networking.md +475 -0
- package/skills/game-developer/references/performance-optimization.md +422 -0
- package/skills/game-developer/references/unity-patterns.md +271 -0
- package/skills/game-developer/references/unreal-cpp.md +352 -0
- package/skills/generate-gui-assets/SKILL.md +305 -0
- package/skills/generate-gui-assets/agents/openai.yaml +4 -0
- package/skills/generate-gui-assets/references/catalog-schema.md +58 -0
- package/skills/generate-gui-assets/references/extraction-techniques.md +21 -0
- package/skills/generate-gui-assets/references/prompt-patterns.md +58 -0
- package/skills/generate-gui-assets/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/generate-gui-assets/scripts/build_gui_contact_sheet.py +51 -0
- package/skills/generate-gui-assets/scripts/clean_chroma_edges.py +262 -0
- package/skills/generate-gui-assets/scripts/copy_approved_icons.py +64 -0
- package/skills/generate-gui-assets/scripts/prepare_gui_asset_run.py +91 -0
- package/skills/generate-gui-assets/scripts/suggest_grid_options.py +63 -0
- package/skills/generate-gui-assets/scripts/validate_gui_catalog.py +50 -0
- package/skills/godot-game-development/SKILL.md +142 -0
- package/skills/hatch-pet/LICENSE.txt +201 -0
- package/skills/hatch-pet/SKILL.md +420 -0
- package/skills/hatch-pet/agents/openai.yaml +4 -0
- package/skills/hatch-pet/references/animation-rows.md +29 -0
- package/skills/hatch-pet/references/codex-pet-contract.md +35 -0
- package/skills/hatch-pet/references/qa-rubric.md +60 -0
- package/skills/hatch-pet/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/hatch-pet/scripts/clean_chroma_edges.py +262 -0
- package/skills/hatch-pet/scripts/compose_atlas.py +150 -0
- package/skills/hatch-pet/scripts/derive_running_left_from_running_right.py +143 -0
- package/skills/hatch-pet/scripts/extract_strip_frames.py +323 -0
- package/skills/hatch-pet/scripts/finalize_pet_run.py +382 -0
- package/skills/hatch-pet/scripts/generate_pet_images.py +287 -0
- package/skills/hatch-pet/scripts/inspect_frames.py +246 -0
- package/skills/hatch-pet/scripts/make_contact_sheet.py +96 -0
- package/skills/hatch-pet/scripts/package_custom_pet.py +108 -0
- package/skills/hatch-pet/scripts/pet_job_status.py +117 -0
- package/skills/hatch-pet/scripts/prepare_pet_run.py +673 -0
- package/skills/hatch-pet/scripts/queue_pet_repairs.py +172 -0
- package/skills/hatch-pet/scripts/record_imagegen_result.py +250 -0
- package/skills/hatch-pet/scripts/render_animation_videos.py +134 -0
- package/skills/hatch-pet/scripts/render_animation_videos.sh +5 -0
- package/skills/hatch-pet/scripts/validate_atlas.py +139 -0
- package/skills/i18n-orchestrator/SKILL.md +37 -0
- package/skills/ios-simulator-skill/SKILL.md +390 -0
- package/skills/ios-simulator-skill/scripts/accessibility_audit.py +300 -0
- package/skills/ios-simulator-skill/scripts/app_launcher.py +326 -0
- package/skills/ios-simulator-skill/scripts/app_state_capture.py +400 -0
- package/skills/ios-simulator-skill/scripts/appearance.py +385 -0
- package/skills/ios-simulator-skill/scripts/build_and_test.py +348 -0
- package/skills/ios-simulator-skill/scripts/clipboard.py +103 -0
- package/skills/ios-simulator-skill/scripts/common/__init__.py +61 -0
- package/skills/ios-simulator-skill/scripts/common/cache_utils.py +289 -0
- package/skills/ios-simulator-skill/scripts/common/device_utils.py +462 -0
- package/skills/ios-simulator-skill/scripts/common/env_config.py +35 -0
- package/skills/ios-simulator-skill/scripts/common/hang_pipeline.py +862 -0
- package/skills/ios-simulator-skill/scripts/common/hang_sessions.py +490 -0
- package/skills/ios-simulator-skill/scripts/common/idb_utils.py +180 -0
- package/skills/ios-simulator-skill/scripts/common/screenshot_utils.py +338 -0
- package/skills/ios-simulator-skill/scripts/container.py +668 -0
- package/skills/ios-simulator-skill/scripts/gesture.py +394 -0
- package/skills/ios-simulator-skill/scripts/hang_watcher.py +1533 -0
- package/skills/ios-simulator-skill/scripts/keyboard.py +391 -0
- package/skills/ios-simulator-skill/scripts/localization_audit.py +483 -0
- package/skills/ios-simulator-skill/scripts/location.py +467 -0
- package/skills/ios-simulator-skill/scripts/log_monitor.py +493 -0
- package/skills/ios-simulator-skill/scripts/model_inspector.py +645 -0
- package/skills/ios-simulator-skill/scripts/navigator.py +461 -0
- package/skills/ios-simulator-skill/scripts/privacy_manager.py +310 -0
- package/skills/ios-simulator-skill/scripts/push_notification.py +240 -0
- package/skills/ios-simulator-skill/scripts/screen_mapper.py +296 -0
- package/skills/ios-simulator-skill/scripts/sim_health_check.sh +245 -0
- package/skills/ios-simulator-skill/scripts/sim_list.py +299 -0
- package/skills/ios-simulator-skill/scripts/simctl_boot.py +312 -0
- package/skills/ios-simulator-skill/scripts/simctl_create.py +316 -0
- package/skills/ios-simulator-skill/scripts/simctl_delete.py +357 -0
- package/skills/ios-simulator-skill/scripts/simctl_erase.py +351 -0
- package/skills/ios-simulator-skill/scripts/simctl_shutdown.py +290 -0
- package/skills/ios-simulator-skill/scripts/simulator_selector.py +375 -0
- package/skills/ios-simulator-skill/scripts/status_bar.py +250 -0
- package/skills/ios-simulator-skill/scripts/test_recorder.py +323 -0
- package/skills/ios-simulator-skill/scripts/visual_diff.py +235 -0
- package/skills/ios-simulator-skill/scripts/xcode/__init__.py +13 -0
- package/skills/ios-simulator-skill/scripts/xcode/builder.py +397 -0
- package/skills/ios-simulator-skill/scripts/xcode/cache.py +204 -0
- package/skills/ios-simulator-skill/scripts/xcode/config.py +178 -0
- package/skills/ios-simulator-skill/scripts/xcode/reporter.py +343 -0
- package/skills/ios-simulator-skill/scripts/xcode/xcresult.py +451 -0
- package/skills/ios-visual-qa-strategist/SKILL.md +111 -0
- package/skills/ios-visual-qa-strategist/agents/openai.yaml +4 -0
- package/skills/ios-visual-qa-strategist/references/ios-tool-selection.md +61 -0
- package/skills/ios-visual-qa-strategist/references/minimal-capture-policy.md +56 -0
- package/skills/ios-visual-qa-strategist/references/visual-reasoning-heuristics.md +53 -0
- package/skills/orchestrator/SKILL.md +0 -20
- package/skills/persistent-storage/SKILL.md +55 -0
- package/skills/short-maker/SKILL.md +23 -0
- package/skills/short-maker/scripts/effects.js +56 -0
- package/skills/short-maker/scripts/shortmaker-bridge.js +332 -0
- package/skills/short-maker/scripts/videomix.js +601 -0
- package/skills/short-maker/templates/hyperframes/cinematic-character.template.html +172 -0
- package/skills/short-maker/templates/hyperframes/index.template.html +194 -0
- package/skills/smali-to-kotlin/SKILL.md +128 -0
- package/skills/smali-to-kotlin/examples/getting-started/tech-stack.md +58 -0
- package/skills/smali-to-kotlin/examples/pipeline/data-ui-parity.md +118 -0
- package/skills/smali-to-kotlin/examples/pipeline/scanner-and-bootstrap.md +106 -0
- package/skills/smali-to-kotlin/library-patterns.md +189 -0
- package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
- package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
- package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
- package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
- package/skills/smali-to-kotlin/phase-3-build.md +248 -0
- package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
- package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
- package/skills/smali-to-kotlin/templates/app-map.md +101 -0
- package/skills/smali-to-kotlin/templates/architecture.md +142 -0
- package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
- package/skills/spec-gate/SKILL.md +6 -2
- package/skills/symphony-enforcer/SKILL.md +8 -0
- package/skills/symphony-enforcer/examples/mindful-stop.md +2 -0
- package/skills/symphony-enforcer/examples/three-phase.md +16 -0
- package/skills/symphony-enforcer/examples/trigger-points.md +7 -1
- package/skills/unity-game-development/SKILL.md +231 -0
- package/skills/video-edit/SKILL.md +36 -0
- package/skills/video-edit/scripts/video_edit.py +324 -0
- package/templates/project-identity/android.json +2 -2
- package/templates/project-identity/backend-nestjs.json +2 -2
- package/templates/project-identity/expo.json +2 -2
- package/templates/project-identity/ios.json +2 -2
- package/templates/project-identity/web-nextjs.json +2 -2
- package/templates/setup-mapping.json +48 -0
- package/templates/specs/design-template.md +161 -71
- package/templates/specs/requirements-template.md +65 -133
- package/templates/specs/task-spec-template.xml +3 -0
- package/workflows/_uncategorized/critic.md +40 -0
- package/workflows/_uncategorized/git-rebase-flow.md +81 -0
- package/workflows/_uncategorized/image-gen.md +118 -0
- package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
- package/workflows/_uncategorized/pixel-gen.md +86 -0
- package/workflows/_uncategorized/pixel-setup.md +90 -0
- package/workflows/_uncategorized/ponytail-review.md +59 -0
- package/workflows/_uncategorized/reverse-android-build.md +222 -0
- package/workflows/_uncategorized/reverse-android-design.md +139 -0
- package/workflows/_uncategorized/reverse-android-discover.md +150 -0
- package/workflows/_uncategorized/reverse-android-scan.md +158 -0
- package/workflows/_uncategorized/reverse-android.md +143 -0
- package/workflows/_uncategorized/reverse-ios-build.md +240 -0
- package/workflows/_uncategorized/reverse-ios-design.md +112 -0
- package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
- package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
- package/workflows/_uncategorized/reverse-ios.md +152 -0
- package/workflows/_uncategorized/safety-router.md +34 -0
- package/workflows/_uncategorized/teach.md +89 -0
- package/workflows/_uncategorized/verify-ui.md +53 -0
- package/workflows/_uncategorized/visualize-screenshots.md +34 -0
- package/workflows/ads/ads-analyst.md +201 -0
- package/workflows/ads/ads-audit.md +106 -0
- package/workflows/ads/ads-optimize.md +97 -0
- package/workflows/ads/ads-targeting.md +241 -0
- package/workflows/ads/adsExpert.md +160 -0
- package/workflows/ads/smali-ads-config.md +400 -0
- package/workflows/ads/smali-ads-flow.md +331 -0
- package/workflows/ads/smali-ads-interstitial.md +377 -0
- package/workflows/ads/smali-ads-native.md +382 -0
- package/workflows/context/teach.md +89 -0
- package/workflows/gitnexus.md +8 -8
- package/workflows/lifecycle/brainstorm.md +43 -0
- package/workflows/lifecycle/code.md +5 -0
- package/workflows/lifecycle/init.md +23 -5
- package/workflows/lifecycle/multi-model-pipeline.md +60 -0
- package/workflows/quality/ponytail-review.md +59 -0
- package/workflows/roles/critic.md +40 -0
- package/workflows/roles/safety-router.md +34 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
iOS Privacy & Permissions Manager
|
|
4
|
+
|
|
5
|
+
Grant/revoke app permissions for testing permission flows.
|
|
6
|
+
Supports 13+ services with audit trail tracking.
|
|
7
|
+
|
|
8
|
+
Usage: python scripts/privacy_manager.py --grant camera --bundle-id com.app
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
|
|
16
|
+
from common import resolve_udid
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PrivacyManager:
|
|
20
|
+
"""Manages iOS app privacy and permissions."""
|
|
21
|
+
|
|
22
|
+
# Supported services
|
|
23
|
+
SUPPORTED_SERVICES = {
|
|
24
|
+
"camera": "Camera access",
|
|
25
|
+
"microphone": "Microphone access",
|
|
26
|
+
"location": "Location services",
|
|
27
|
+
"contacts": "Contacts access",
|
|
28
|
+
"photos": "Photos library access",
|
|
29
|
+
"calendar": "Calendar access",
|
|
30
|
+
"health": "Health data access",
|
|
31
|
+
"reminders": "Reminders access",
|
|
32
|
+
"motion": "Motion & fitness",
|
|
33
|
+
"keyboard": "Keyboard access",
|
|
34
|
+
"mediaLibrary": "Media library",
|
|
35
|
+
"calls": "Call history",
|
|
36
|
+
"siri": "Siri access",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def __init__(self, udid: str | None = None):
|
|
40
|
+
"""Initialize privacy manager.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
udid: Optional device UDID (auto-detects booted simulator if None)
|
|
44
|
+
"""
|
|
45
|
+
self.udid = udid
|
|
46
|
+
|
|
47
|
+
def grant_permission(
|
|
48
|
+
self,
|
|
49
|
+
bundle_id: str,
|
|
50
|
+
service: str,
|
|
51
|
+
scenario: str | None = None,
|
|
52
|
+
step: int | None = None,
|
|
53
|
+
) -> bool:
|
|
54
|
+
"""
|
|
55
|
+
Grant permission for app.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
bundle_id: App bundle ID
|
|
59
|
+
service: Service name (camera, microphone, location, etc.)
|
|
60
|
+
scenario: Test scenario name for audit trail
|
|
61
|
+
step: Step number in test scenario
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Success status
|
|
65
|
+
"""
|
|
66
|
+
if service not in self.SUPPORTED_SERVICES:
|
|
67
|
+
print(f"Error: Unknown service '{service}'")
|
|
68
|
+
print(f"Supported: {', '.join(self.SUPPORTED_SERVICES.keys())}")
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
cmd = ["xcrun", "simctl", "privacy"]
|
|
72
|
+
|
|
73
|
+
if self.udid:
|
|
74
|
+
cmd.append(self.udid)
|
|
75
|
+
else:
|
|
76
|
+
cmd.append("booted")
|
|
77
|
+
|
|
78
|
+
cmd.extend(["grant", service, bundle_id])
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
subprocess.run(cmd, capture_output=True, check=True)
|
|
82
|
+
|
|
83
|
+
# Log audit entry
|
|
84
|
+
self._log_audit("grant", bundle_id, service, scenario, step)
|
|
85
|
+
|
|
86
|
+
return True
|
|
87
|
+
except subprocess.CalledProcessError:
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
def revoke_permission(
|
|
91
|
+
self,
|
|
92
|
+
bundle_id: str,
|
|
93
|
+
service: str,
|
|
94
|
+
scenario: str | None = None,
|
|
95
|
+
step: int | None = None,
|
|
96
|
+
) -> bool:
|
|
97
|
+
"""
|
|
98
|
+
Revoke permission for app.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
bundle_id: App bundle ID
|
|
102
|
+
service: Service name
|
|
103
|
+
scenario: Test scenario name for audit trail
|
|
104
|
+
step: Step number in test scenario
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Success status
|
|
108
|
+
"""
|
|
109
|
+
if service not in self.SUPPORTED_SERVICES:
|
|
110
|
+
print(f"Error: Unknown service '{service}'")
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
cmd = ["xcrun", "simctl", "privacy"]
|
|
114
|
+
|
|
115
|
+
if self.udid:
|
|
116
|
+
cmd.append(self.udid)
|
|
117
|
+
else:
|
|
118
|
+
cmd.append("booted")
|
|
119
|
+
|
|
120
|
+
cmd.extend(["revoke", service, bundle_id])
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
subprocess.run(cmd, capture_output=True, check=True)
|
|
124
|
+
|
|
125
|
+
# Log audit entry
|
|
126
|
+
self._log_audit("revoke", bundle_id, service, scenario, step)
|
|
127
|
+
|
|
128
|
+
return True
|
|
129
|
+
except subprocess.CalledProcessError:
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
def reset_permission(
|
|
133
|
+
self,
|
|
134
|
+
bundle_id: str,
|
|
135
|
+
service: str,
|
|
136
|
+
scenario: str | None = None,
|
|
137
|
+
step: int | None = None,
|
|
138
|
+
) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Reset permission to default.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
bundle_id: App bundle ID
|
|
144
|
+
service: Service name
|
|
145
|
+
scenario: Test scenario name for audit trail
|
|
146
|
+
step: Step number in test scenario
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Success status
|
|
150
|
+
"""
|
|
151
|
+
if service not in self.SUPPORTED_SERVICES:
|
|
152
|
+
print(f"Error: Unknown service '{service}'")
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
cmd = ["xcrun", "simctl", "privacy"]
|
|
156
|
+
|
|
157
|
+
if self.udid:
|
|
158
|
+
cmd.append(self.udid)
|
|
159
|
+
else:
|
|
160
|
+
cmd.append("booted")
|
|
161
|
+
|
|
162
|
+
cmd.extend(["reset", service, bundle_id])
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
subprocess.run(cmd, capture_output=True, check=True)
|
|
166
|
+
|
|
167
|
+
# Log audit entry
|
|
168
|
+
self._log_audit("reset", bundle_id, service, scenario, step)
|
|
169
|
+
|
|
170
|
+
return True
|
|
171
|
+
except subprocess.CalledProcessError:
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
@staticmethod
|
|
175
|
+
def _log_audit(
|
|
176
|
+
action: str,
|
|
177
|
+
bundle_id: str,
|
|
178
|
+
service: str,
|
|
179
|
+
scenario: str | None = None,
|
|
180
|
+
step: int | None = None,
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Log permission change to audit trail (for test tracking).
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
action: grant, revoke, or reset
|
|
186
|
+
bundle_id: App bundle ID
|
|
187
|
+
service: Service name
|
|
188
|
+
scenario: Test scenario name
|
|
189
|
+
step: Step number
|
|
190
|
+
"""
|
|
191
|
+
# Could write to file, but for now just log to stdout for transparency
|
|
192
|
+
timestamp = datetime.now().isoformat()
|
|
193
|
+
location = f" (step {step})" if step else ""
|
|
194
|
+
scenario_info = f" in {scenario}" if scenario else ""
|
|
195
|
+
print(
|
|
196
|
+
f"[Audit] {timestamp}: {action.upper()} {service} for {bundle_id}{scenario_info}{location}"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def main():
|
|
201
|
+
"""Main entry point."""
|
|
202
|
+
parser = argparse.ArgumentParser(description="Manage iOS app privacy and permissions")
|
|
203
|
+
|
|
204
|
+
# Required
|
|
205
|
+
parser.add_argument("--bundle-id", required=True, help="App bundle ID (e.g., com.example.app)")
|
|
206
|
+
|
|
207
|
+
# Action (mutually exclusive)
|
|
208
|
+
action_group = parser.add_mutually_exclusive_group(required=True)
|
|
209
|
+
action_group.add_argument(
|
|
210
|
+
"--grant",
|
|
211
|
+
help="Grant permission (service name or comma-separated list)",
|
|
212
|
+
)
|
|
213
|
+
action_group.add_argument(
|
|
214
|
+
"--revoke", help="Revoke permission (service name or comma-separated list)"
|
|
215
|
+
)
|
|
216
|
+
action_group.add_argument(
|
|
217
|
+
"--reset",
|
|
218
|
+
help="Reset permission to default (service name or comma-separated list)",
|
|
219
|
+
)
|
|
220
|
+
action_group.add_argument(
|
|
221
|
+
"--list",
|
|
222
|
+
action="store_true",
|
|
223
|
+
help="List all supported services",
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Test tracking
|
|
227
|
+
parser.add_argument(
|
|
228
|
+
"--scenario",
|
|
229
|
+
help="Test scenario name for audit trail",
|
|
230
|
+
)
|
|
231
|
+
parser.add_argument("--step", type=int, help="Step number in test scenario")
|
|
232
|
+
|
|
233
|
+
# Device
|
|
234
|
+
parser.add_argument(
|
|
235
|
+
"--udid",
|
|
236
|
+
help="Device UDID (auto-detects booted simulator if not provided)",
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
args = parser.parse_args()
|
|
240
|
+
|
|
241
|
+
# List supported services
|
|
242
|
+
if args.list:
|
|
243
|
+
print("Supported Privacy Services:\n")
|
|
244
|
+
for service, description in PrivacyManager.SUPPORTED_SERVICES.items():
|
|
245
|
+
print(f" {service:<15} - {description}")
|
|
246
|
+
print()
|
|
247
|
+
print("Examples:")
|
|
248
|
+
print(" python scripts/privacy_manager.py --grant camera --bundle-id com.app")
|
|
249
|
+
print(" python scripts/privacy_manager.py --revoke location --bundle-id com.app")
|
|
250
|
+
print(" python scripts/privacy_manager.py --grant camera,photos --bundle-id com.app")
|
|
251
|
+
sys.exit(0)
|
|
252
|
+
|
|
253
|
+
# Resolve UDID with auto-detection
|
|
254
|
+
try:
|
|
255
|
+
udid = resolve_udid(args.udid)
|
|
256
|
+
except RuntimeError as e:
|
|
257
|
+
print(f"Error: {e}")
|
|
258
|
+
sys.exit(1)
|
|
259
|
+
|
|
260
|
+
manager = PrivacyManager(udid=udid)
|
|
261
|
+
|
|
262
|
+
# Parse service names (support comma-separated list)
|
|
263
|
+
if args.grant:
|
|
264
|
+
services = [s.strip() for s in args.grant.split(",")]
|
|
265
|
+
action = "grant"
|
|
266
|
+
action_fn = manager.grant_permission
|
|
267
|
+
elif args.revoke:
|
|
268
|
+
services = [s.strip() for s in args.revoke.split(",")]
|
|
269
|
+
action = "revoke"
|
|
270
|
+
action_fn = manager.revoke_permission
|
|
271
|
+
else: # reset
|
|
272
|
+
services = [s.strip() for s in args.reset.split(",")]
|
|
273
|
+
action = "reset"
|
|
274
|
+
action_fn = manager.reset_permission
|
|
275
|
+
|
|
276
|
+
# Execute action for each service
|
|
277
|
+
all_success = True
|
|
278
|
+
for service in services:
|
|
279
|
+
if service not in PrivacyManager.SUPPORTED_SERVICES:
|
|
280
|
+
print(f"Error: Unknown service '{service}'")
|
|
281
|
+
all_success = False
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
success = action_fn(
|
|
285
|
+
args.bundle_id,
|
|
286
|
+
service,
|
|
287
|
+
scenario=args.scenario,
|
|
288
|
+
step=args.step,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
if success:
|
|
292
|
+
description = PrivacyManager.SUPPORTED_SERVICES[service]
|
|
293
|
+
print(f"✓ {action.capitalize()} {service}: {description}")
|
|
294
|
+
else:
|
|
295
|
+
print(f"✗ Failed to {action} {service}")
|
|
296
|
+
all_success = False
|
|
297
|
+
|
|
298
|
+
if not all_success:
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
|
|
301
|
+
# Summary
|
|
302
|
+
if len(services) > 1:
|
|
303
|
+
print(f"\nPermissions {action}ed: {', '.join(services)}")
|
|
304
|
+
|
|
305
|
+
if args.scenario:
|
|
306
|
+
print(f"Test scenario: {args.scenario}" + (f" (step {args.step})" if args.step else ""))
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
if __name__ == "__main__":
|
|
310
|
+
main()
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
iOS Push Notification Simulator
|
|
4
|
+
|
|
5
|
+
Send simulated push notifications to test notification handling.
|
|
6
|
+
Supports custom payloads and test tracking.
|
|
7
|
+
|
|
8
|
+
Usage: python scripts/push_notification.py --bundle-id com.app --title "Alert" --body "Message"
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import tempfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from common import resolve_udid
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PushNotificationSender:
|
|
22
|
+
"""Sends simulated push notifications to iOS simulator."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, udid: str | None = None):
|
|
25
|
+
"""Initialize push notification sender.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
udid: Optional device UDID (auto-detects booted simulator if None)
|
|
29
|
+
"""
|
|
30
|
+
self.udid = udid
|
|
31
|
+
|
|
32
|
+
def send(
|
|
33
|
+
self,
|
|
34
|
+
bundle_id: str,
|
|
35
|
+
payload: dict | str,
|
|
36
|
+
_test_name: str | None = None,
|
|
37
|
+
_expected_behavior: str | None = None,
|
|
38
|
+
) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Send push notification to app.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
bundle_id: Target app bundle ID
|
|
44
|
+
payload: Push payload (dict or JSON string) or path to JSON file
|
|
45
|
+
test_name: Test scenario name for tracking
|
|
46
|
+
expected_behavior: Expected behavior after notification arrives
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Success status
|
|
50
|
+
"""
|
|
51
|
+
# Handle different payload formats
|
|
52
|
+
if isinstance(payload, str):
|
|
53
|
+
# Check if it's a file path
|
|
54
|
+
payload_path = Path(payload)
|
|
55
|
+
if payload_path.exists():
|
|
56
|
+
with open(payload_path) as f:
|
|
57
|
+
payload_data = json.load(f)
|
|
58
|
+
else:
|
|
59
|
+
# Try to parse as JSON string
|
|
60
|
+
try:
|
|
61
|
+
payload_data = json.loads(payload)
|
|
62
|
+
except json.JSONDecodeError:
|
|
63
|
+
print(f"Error: Invalid JSON payload: {payload}")
|
|
64
|
+
return False
|
|
65
|
+
else:
|
|
66
|
+
payload_data = payload
|
|
67
|
+
|
|
68
|
+
# Ensure payload has aps dictionary
|
|
69
|
+
if "aps" not in payload_data:
|
|
70
|
+
payload_data = {"aps": payload_data}
|
|
71
|
+
|
|
72
|
+
# Create temp file with payload
|
|
73
|
+
try:
|
|
74
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
75
|
+
json.dump(payload_data, f)
|
|
76
|
+
temp_payload_path = f.name
|
|
77
|
+
|
|
78
|
+
# Build simctl command
|
|
79
|
+
cmd = ["xcrun", "simctl", "push"]
|
|
80
|
+
|
|
81
|
+
if self.udid:
|
|
82
|
+
cmd.append(self.udid)
|
|
83
|
+
else:
|
|
84
|
+
cmd.append("booted")
|
|
85
|
+
|
|
86
|
+
cmd.extend([bundle_id, temp_payload_path])
|
|
87
|
+
|
|
88
|
+
# Send notification
|
|
89
|
+
subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
90
|
+
|
|
91
|
+
# Clean up temp file
|
|
92
|
+
Path(temp_payload_path).unlink()
|
|
93
|
+
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
except subprocess.CalledProcessError as e:
|
|
97
|
+
print(f"Error sending push notification: {e.stderr}")
|
|
98
|
+
return False
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"Error: {e}")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def send_simple(
|
|
104
|
+
self,
|
|
105
|
+
bundle_id: str,
|
|
106
|
+
title: str | None = None,
|
|
107
|
+
body: str | None = None,
|
|
108
|
+
badge: int | None = None,
|
|
109
|
+
sound: bool = True,
|
|
110
|
+
) -> bool:
|
|
111
|
+
"""
|
|
112
|
+
Send simple push notification with common parameters.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
bundle_id: Target app bundle ID
|
|
116
|
+
title: Alert title
|
|
117
|
+
body: Alert body
|
|
118
|
+
badge: Badge number
|
|
119
|
+
sound: Whether to play sound
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Success status
|
|
123
|
+
"""
|
|
124
|
+
payload = {}
|
|
125
|
+
|
|
126
|
+
if title or body:
|
|
127
|
+
alert = {}
|
|
128
|
+
if title:
|
|
129
|
+
alert["title"] = title
|
|
130
|
+
if body:
|
|
131
|
+
alert["body"] = body
|
|
132
|
+
payload["alert"] = alert
|
|
133
|
+
|
|
134
|
+
if badge is not None:
|
|
135
|
+
payload["badge"] = badge
|
|
136
|
+
|
|
137
|
+
if sound:
|
|
138
|
+
payload["sound"] = "default"
|
|
139
|
+
|
|
140
|
+
# Wrap in aps
|
|
141
|
+
full_payload = {"aps": payload}
|
|
142
|
+
|
|
143
|
+
return self.send(bundle_id, full_payload)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def main():
|
|
147
|
+
"""Main entry point."""
|
|
148
|
+
parser = argparse.ArgumentParser(description="Send simulated push notification to iOS app")
|
|
149
|
+
|
|
150
|
+
# Required
|
|
151
|
+
parser.add_argument(
|
|
152
|
+
"--bundle-id", required=True, help="Target app bundle ID (e.g., com.example.app)"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Simple payload options
|
|
156
|
+
parser.add_argument("--title", help="Alert title (for simple notifications)")
|
|
157
|
+
parser.add_argument("--body", help="Alert body message")
|
|
158
|
+
parser.add_argument("--badge", type=int, help="Badge number")
|
|
159
|
+
parser.add_argument("--no-sound", action="store_true", help="Don't play notification sound")
|
|
160
|
+
|
|
161
|
+
# Custom payload
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
"--payload",
|
|
164
|
+
help="Custom JSON payload file or inline JSON string",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Test tracking
|
|
168
|
+
parser.add_argument("--test-name", help="Test scenario name for tracking")
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"--expected",
|
|
171
|
+
help="Expected behavior after notification",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Device
|
|
175
|
+
parser.add_argument(
|
|
176
|
+
"--udid",
|
|
177
|
+
help="Device UDID (auto-detects booted simulator if not provided)",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
args = parser.parse_args()
|
|
181
|
+
|
|
182
|
+
# Resolve UDID with auto-detection
|
|
183
|
+
try:
|
|
184
|
+
udid = resolve_udid(args.udid)
|
|
185
|
+
except RuntimeError as e:
|
|
186
|
+
print(f"Error: {e}")
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
|
|
189
|
+
sender = PushNotificationSender(udid=udid)
|
|
190
|
+
|
|
191
|
+
# Send notification
|
|
192
|
+
if args.payload:
|
|
193
|
+
# Custom payload mode
|
|
194
|
+
success = sender.send(args.bundle_id, args.payload)
|
|
195
|
+
else:
|
|
196
|
+
# Simple notification mode
|
|
197
|
+
success = sender.send_simple(
|
|
198
|
+
args.bundle_id,
|
|
199
|
+
title=args.title,
|
|
200
|
+
body=args.body,
|
|
201
|
+
badge=args.badge,
|
|
202
|
+
sound=not args.no_sound,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if success:
|
|
206
|
+
# Token-efficient output
|
|
207
|
+
output = "Push notification sent"
|
|
208
|
+
|
|
209
|
+
if args.test_name:
|
|
210
|
+
output += f" (test: {args.test_name})"
|
|
211
|
+
|
|
212
|
+
print(output)
|
|
213
|
+
|
|
214
|
+
if args.expected:
|
|
215
|
+
print(f"Expected: {args.expected}")
|
|
216
|
+
|
|
217
|
+
print()
|
|
218
|
+
print("Notification details:")
|
|
219
|
+
if args.title:
|
|
220
|
+
print(f" Title: {args.title}")
|
|
221
|
+
if args.body:
|
|
222
|
+
print(f" Body: {args.body}")
|
|
223
|
+
if args.badge:
|
|
224
|
+
print(f" Badge: {args.badge}")
|
|
225
|
+
|
|
226
|
+
print()
|
|
227
|
+
print("Verify notification handling:")
|
|
228
|
+
print("1. Check app log output: python scripts/log_monitor.py --app " + args.bundle_id)
|
|
229
|
+
print(
|
|
230
|
+
"2. Capture state: python scripts/app_state_capture.py --app-bundle-id "
|
|
231
|
+
+ args.bundle_id
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
else:
|
|
235
|
+
print("Failed to send push notification")
|
|
236
|
+
sys.exit(1)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
if __name__ == "__main__":
|
|
240
|
+
main()
|