forked from trent_larson/crowd-funder-for-time-pwa
add a privacy-fixer project that may have fixed the GoogleToolboxForMac privacy manifext problem
https://github.com/crasowas/app_privacy_manifest_fixer
This commit is contained in:
124
ios/App/app_privacy_manifest_fixer/Report/report-template.html
Normal file
124
ios/App/app_privacy_manifest_fixer/Report/report-template.html
Normal file
@@ -0,0 +1,124 @@
|
||||
<!--
|
||||
Copyright (c) 2024, crasowas.
|
||||
|
||||
Use of this source code is governed by a MIT-style license
|
||||
that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Privacy Access Report</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
color: #333;
|
||||
background-color: #f9f9f9;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
min-width: 735px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2em;
|
||||
margin: 0 0 15px;
|
||||
padding: 12px 20px;
|
||||
color: #fff;
|
||||
background-color: #5a9e6d;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h2 .version {
|
||||
font-size: 0.7em;
|
||||
color: #5a9e6d;
|
||||
background: #f1f1f1;
|
||||
padding: 2px 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #5a9e6d;
|
||||
background-color: #fcfcfc;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #5a9e6d;
|
||||
border-radius: 5px;
|
||||
font-size: 0.9em;
|
||||
margin-right: 16px;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #fff;
|
||||
background-color: #5a9e6d;
|
||||
}
|
||||
|
||||
a.warning {
|
||||
color: #e0b73c;
|
||||
background-color: #fcfcfc;
|
||||
border: 1px solid #e0b73c;
|
||||
}
|
||||
|
||||
a.warning:hover {
|
||||
color: #fff;
|
||||
background-color: #e0b73c;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 12px 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
color: #fff;
|
||||
background-color: #b0b8b1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card" style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>
|
||||
This report was generated using version <strong>{{TOOL_VERSION}}</strong>.
|
||||
</span>
|
||||
<a href="https://github.com/crasowas/app_privacy_manifest_fixer" target="_blank">Like this
|
||||
project? 🌟Star it on GitHub!</a>
|
||||
</div>
|
||||
{{REPORT_CONTENT}}
|
||||
</body>
|
||||
</html>
|
||||
285
ios/App/app_privacy_manifest_fixer/Report/report.sh
Executable file
285
ios/App/app_privacy_manifest_fixer/Report/report.sh
Executable file
@@ -0,0 +1,285 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2024, crasowas.
|
||||
#
|
||||
# Use of this source code is governed by a MIT-style license
|
||||
# that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
set -e
|
||||
|
||||
# Absolute path of the script and the tool's root directory
|
||||
script_path="$(realpath "$0")"
|
||||
tool_root_path="$(dirname "$(dirname "$script_path")")"
|
||||
|
||||
# Load common constants and utils
|
||||
source "$tool_root_path/Common/constants.sh"
|
||||
source "$tool_root_path/Common/utils.sh"
|
||||
|
||||
# Path to the app
|
||||
app_path="$1"
|
||||
|
||||
# Check if the app exists
|
||||
if [ ! -d "$app_path" ] || [[ "$app_path" != *.app ]]; then
|
||||
echo "Unable to find the app: $app_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the app is iOS or macOS
|
||||
is_ios_app=true
|
||||
frameworks_dir="$app_path/Frameworks"
|
||||
if [ -d "$app_path/Contents/MacOS" ]; then
|
||||
is_ios_app=false
|
||||
frameworks_dir="$app_path/Contents/Frameworks"
|
||||
fi
|
||||
|
||||
report_output_file="$2"
|
||||
# Additional arguments as template usage records
|
||||
template_usage_records=("${@:2}")
|
||||
|
||||
# Copy report template to output file
|
||||
report_template_file="$tool_root_path/Report/report-template.html"
|
||||
if ! rsync -a "$report_template_file" "$report_output_file"; then
|
||||
echo "Failed to copy the report template to $report_output_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read the current tool's version from the VERSION file
|
||||
tool_version_file="$tool_root_path/VERSION"
|
||||
tool_version="N/A"
|
||||
if [ -f "$tool_version_file" ]; then
|
||||
tool_version="$(cat "$tool_version_file")"
|
||||
fi
|
||||
|
||||
# Initialize report content
|
||||
report_content=""
|
||||
|
||||
# Get the template file used for fixing based on the app or framework name
|
||||
function get_used_template_file() {
|
||||
local name="$1"
|
||||
|
||||
for template_usage_record in "${template_usage_records[@]}"; do
|
||||
if [[ "$template_usage_record" == "$name$DELIMITER"* ]]; then
|
||||
echo "${template_usage_record#*$DELIMITER}"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Analyze accessed API types and their corresponding reasons
|
||||
function analyze_privacy_accessed_api() {
|
||||
local privacy_manifest_file="$1"
|
||||
local -a results=()
|
||||
|
||||
if [ -f "$privacy_manifest_file" ]; then
|
||||
local api_count=$(xmllint --xpath 'count(//dict/key[text()="NSPrivacyAccessedAPIType"])' "$privacy_manifest_file")
|
||||
|
||||
for ((i=1; i<=api_count; i++)); do
|
||||
local api_type=$(xmllint --xpath "(//dict/key[text()='NSPrivacyAccessedAPIType']/following-sibling::string[1])[$i]/text()" "$privacy_manifest_file" 2>/dev/null)
|
||||
local api_reasons=$(xmllint --xpath "(//dict/key[text()='NSPrivacyAccessedAPITypeReasons']/following-sibling::array[1])[position()=$i]/string/text()" "$privacy_manifest_file" 2>/dev/null | paste -sd "/" -)
|
||||
|
||||
if [ -z "$api_type" ]; then
|
||||
api_type="N/A"
|
||||
fi
|
||||
|
||||
if [ -z "$api_reasons" ]; then
|
||||
api_reasons="N/A"
|
||||
fi
|
||||
|
||||
results+=("$api_type$DELIMITER$api_reasons")
|
||||
done
|
||||
fi
|
||||
|
||||
echo "${results[@]}"
|
||||
}
|
||||
|
||||
# Get the path to the `Info.plist` file for the specified app or framework
|
||||
function get_plist_file() {
|
||||
local path="$1"
|
||||
local version_path="$2"
|
||||
local plist_file=""
|
||||
|
||||
if [[ "$path" == *.app ]]; then
|
||||
if [ "$is_ios_app" == true ]; then
|
||||
plist_file="$path/Info.plist"
|
||||
else
|
||||
plist_file="$path/Contents/Info.plist"
|
||||
fi
|
||||
elif [[ "$path" == *.framework ]]; then
|
||||
local framework_path="$(get_framework_path "$path" "$version_path")"
|
||||
|
||||
if [ "$is_ios_app" == true ]; then
|
||||
plist_file="$framework_path/Info.plist"
|
||||
else
|
||||
plist_file="$framework_path/Resources/Info.plist"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$plist_file"
|
||||
}
|
||||
|
||||
# Add an HTML <div> element with the `card` class
|
||||
function add_html_card_container() {
|
||||
local card="$1"
|
||||
|
||||
report_content="$report_content<div class=\"card\">$card</div>"
|
||||
}
|
||||
|
||||
# Generate an HTML <h2> element
|
||||
function generate_html_header() {
|
||||
local title="$1"
|
||||
local version="$2"
|
||||
|
||||
echo "<h2>$title<span class=\"version\">Version $version</span></h2>"
|
||||
}
|
||||
|
||||
# Generate an HTML <a> element with optional `warning` class
|
||||
function generate_html_anchor() {
|
||||
local text="$1"
|
||||
local href="$2"
|
||||
local warning="$3"
|
||||
|
||||
if [ "$warning" == true ]; then
|
||||
echo "<a class=\"warning\" href=\"$href\">$text</a>"
|
||||
else
|
||||
echo "<a href=\"$href\">$text</a>"
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate an HTML <table> element
|
||||
function generate_html_table() {
|
||||
local thead="$1"
|
||||
local tbody="$2"
|
||||
|
||||
echo "<table>$thead$tbody</table>"
|
||||
}
|
||||
|
||||
# Generate an HTML <thead> element
|
||||
function generate_html_thead() {
|
||||
local ths=("$@")
|
||||
local tr=""
|
||||
|
||||
for th in "${ths[@]}"; do
|
||||
tr="$tr<th>$th</th>"
|
||||
done
|
||||
|
||||
echo "<thead><tr>$tr</tr></thead>"
|
||||
}
|
||||
|
||||
# Generate an HTML <tbody> element
|
||||
function generate_html_tbody() {
|
||||
local trs=("$@")
|
||||
local tbody=""
|
||||
|
||||
for tr in "${trs[@]}"; do
|
||||
tbody="$tbody<tr>"
|
||||
local tds=($(split_string_by_delimiter "$tr"))
|
||||
|
||||
for td in "${tds[@]}"; do
|
||||
tbody="$tbody<td>$td</td>"
|
||||
done
|
||||
|
||||
tbody="$tbody</tr>"
|
||||
done
|
||||
|
||||
echo "<tbody>$tbody</tbody>"
|
||||
}
|
||||
|
||||
# Generate the report content for the specified directory
|
||||
function generate_report_content() {
|
||||
local path="$1"
|
||||
local version_path="$2"
|
||||
local privacy_manifest_file=""
|
||||
|
||||
if [[ "$path" == *.app ]]; then
|
||||
# Per the documentation, the privacy manifest should be placed at the root of the app’s bundle for iOS, while for macOS, it should be located in `Contents/Resources/` within the app’s bundle
|
||||
# Reference: https://developer.apple.com/documentation/bundleresources/adding-a-privacy-manifest-to-your-app-or-third-party-sdk#Add-a-privacy-manifest-to-your-app
|
||||
if [ "$is_ios_app" == true ]; then
|
||||
privacy_manifest_file="$path/$PRIVACY_MANIFEST_FILE_NAME"
|
||||
else
|
||||
privacy_manifest_file="$path/Contents/Resources/$PRIVACY_MANIFEST_FILE_NAME"
|
||||
fi
|
||||
else
|
||||
# Per the documentation, the privacy manifest should be placed at the root of the iOS framework, while for a macOS framework with multiple versions, it should be located in the `Resources` directory within the corresponding version
|
||||
# Some SDKs don’t follow the guideline, so we use a search-based approach for now
|
||||
# Reference: https://developer.apple.com/documentation/bundleresources/adding-a-privacy-manifest-to-your-app-or-third-party-sdk#Add-a-privacy-manifest-to-your-framework
|
||||
local framework_path="$(get_framework_path "$path" "$version_path")"
|
||||
local privacy_manifest_files=($(search_privacy_manifest_files "$framework_path"))
|
||||
privacy_manifest_file="$(get_privacy_manifest_file "${privacy_manifest_files[@]}")"
|
||||
fi
|
||||
|
||||
local name="$(basename "$path")"
|
||||
local title="$name"
|
||||
if [ -n "$version_path" ]; then
|
||||
title="$name ($version_path)"
|
||||
fi
|
||||
|
||||
local plist_file="$(get_plist_file "$path" "$version_path")"
|
||||
local version="$(get_plist_version "$plist_file")"
|
||||
local card="$(generate_html_header "$title" "$version")"
|
||||
|
||||
if [ -f "$privacy_manifest_file" ]; then
|
||||
card="$card$(generate_html_anchor "$PRIVACY_MANIFEST_FILE_NAME" "$privacy_manifest_file" false)"
|
||||
|
||||
local used_template_file="$(get_used_template_file "$name$version_path")"
|
||||
|
||||
if [ -f "$used_template_file" ]; then
|
||||
card="$card$(generate_html_anchor "Template Used: $(basename "$used_template_file")" "$used_template_file" false)"
|
||||
fi
|
||||
|
||||
local trs=($(analyze_privacy_accessed_api "$privacy_manifest_file"))
|
||||
|
||||
# Generate table only if the accessed privacy API types array is not empty
|
||||
if [[ ${#trs[@]} -gt 0 ]]; then
|
||||
local thead="$(generate_html_thead "NSPrivacyAccessedAPIType" "NSPrivacyAccessedAPITypeReasons")"
|
||||
local tbody="$(generate_html_tbody "${trs[@]}")"
|
||||
|
||||
card="$card$(generate_html_table "$thead" "$tbody")"
|
||||
fi
|
||||
else
|
||||
card="$card$(generate_html_anchor "Missing Privacy Manifest" "$path" true)"
|
||||
fi
|
||||
|
||||
add_html_card_container "$card"
|
||||
}
|
||||
|
||||
# Generate the report content for app
|
||||
function generate_app_report_content() {
|
||||
generate_report_content "$app_path" ""
|
||||
}
|
||||
|
||||
# Generate the report content for frameworks
|
||||
function generate_frameworks_report_content() {
|
||||
if ! [ -d "$frameworks_dir" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
for path in "$frameworks_dir"/*; do
|
||||
if [ -d "$path" ]; then
|
||||
local versions_dir="$path/Versions"
|
||||
|
||||
if [ -d "$versions_dir" ]; then
|
||||
for version in $(ls -1 "$versions_dir" | grep -vE '^Current$'); do
|
||||
local version_path="Versions/$version"
|
||||
generate_report_content "$path" "$version_path"
|
||||
done
|
||||
else
|
||||
generate_report_content "$path" ""
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Generate the final report with all content
|
||||
function generate_final_report() {
|
||||
# Replace placeholders in the template with the tool's version and report content
|
||||
sed -i "" -e "s|{{TOOL_VERSION}}|$tool_version|g" -e "s|{{REPORT_CONTENT}}|${report_content}|g" "$report_output_file"
|
||||
|
||||
echo "Privacy Access Report has been generated: $report_output_file"
|
||||
}
|
||||
|
||||
generate_app_report_content
|
||||
generate_frameworks_report_content
|
||||
generate_final_report
|
||||
Reference in New Issue
Block a user