-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproject.yml
More file actions
190 lines (183 loc) · 9.89 KB
/
Copy pathproject.yml
File metadata and controls
190 lines (183 loc) · 9.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# XcodeGen manifest for Network Monitor v2.
#
# Why this exists: a NetworkExtension content-filter ships as a `.systemextension`
# bundle embedded in the app and signed/notarized as one unit — none of which
# SwiftPM can express. This declarative manifest is the build system; the binary
# `.xcodeproj` is generated (`xcodegen generate`) and git-ignored.
#
# MonitorCore stays a SwiftPM package (so `swift test` keeps covering the logic in
# CI); both Xcode targets link it as a package product. Bundle ids, App Group, and
# entitlements match the staged FilterExtension/ sources exactly.
name: NetworkMonitor
options:
bundleIdPrefix: io.github.light-house-group.network-speed
deploymentTarget:
macOS: "13.0"
createIntermediateGroups: true
groupSortPosition: top
settings:
base:
DEVELOPMENT_TEAM: PT666QK286
CODE_SIGN_STYLE: Automatic
SWIFT_VERSION: "5.9"
MARKETING_VERSION: "1.0.0"
CURRENT_PROJECT_VERSION: "1"
# Required for notarization; harmless for local Apple Development signing.
ENABLE_HARDENED_RUNTIME: YES
# Both binaries link MonitorCore statically — no shared dynamic framework to
# embed, each process gets its own copy of the (small, pure) core.
DEAD_CODE_STRIPPING: YES
configs:
# A Developer-ID system extension CANNOT be archived with AUTOMATIC signing:
# automatic archives sign as Apple Development, and a Development profile only
# grants the plain `content-filter-provider` NE value — never the
# `content-filter-provider-systemextension` variant a *distributed* sysext
# requires. (Pinning the identity under Automatic just yields a "conflicting
# provisioning settings" error.) So Release uses MANUAL signing with Developer
# ID provisioning profiles — created once in the portal, names referenced per
# target below via PROVISIONING_PROFILE_SPECIFIER. Debug stays Automatic.
Release:
CODE_SIGN_STYLE: Manual
CODE_SIGN_IDENTITY: "Developer ID Application"
packages:
MonitorCore:
path: .
targets:
# ---- Host menu-bar app (the policy owner) -------------------------------
NetworkUsageMonitor:
type: application
platform: macOS
sources:
- path: Sources/NetworkUsageMonitor
# App-side activation glue (SystemExtensionController + FirewallCoordinator)
# drives OSSystemExtensionRequest + NEFilterManager. Kept physically under
# FilterExtension/host/ — excluded from the SwiftPM build — so `swift test`
# / CI never compile NetworkExtension host code; only the Xcode app target
# pulls it in (see the FIREWALL_GLUE compilation condition below).
- path: FilterExtension/host
# App icon (and any future bundled resources). AppIcon.icns is generated
# from logo.png; CFBundleIconFile in Scripts/Info.plist points at it. Non-
# source files under this path land in Copy Bundle Resources automatically.
- path: Resources
preBuildScripts:
# Release paywall guard: refuse to build a release that ships with no
# embedded trusted license public keys. An empty LicenseTrust.publicKeys makes
# the offline verifier reject EVERY real activation token, so a paying customer
# could never unlock Pro. The keys + sentinel now live in MonitorCore
# (LicenseTrust.swift) so the filter extension can verify a license too; the
# sentinel is flipped to `true` only once the real Ed25519 PUBLIC key(s) are
# pasted in there.
- name: "Guard: license keys configured for Release"
basedOnDependencyAnalysis: false
script: |
if [ "${CONFIGURATION}" = "Release" ]; then
KEYFILE="${SRCROOT}/Sources/MonitorCore/LicenseTrust.swift"
# Whitespace-tolerant: `=false`, `= false`, `= false` all trip it
# (the old exact-spacing grep was bypassed by reformatting).
if grep -Eq "trustedKeysConfigured[[:space:]]*=[[:space:]]*false" "${KEYFILE}"; then
echo "error: Release build blocked — trustedKeysConfigured is false (no trusted license public keys embedded). Populate LicenseTrust.trustedPublicKeysBase64 in Sources/MonitorCore/LicenseTrust.swift with the server's Ed25519 PUBLIC key(s) and set trustedKeysConfigured = true before shipping. (Empty keys reject every real activation token = broken paywall.)"
exit 1
fi
# Defense in depth: configured=true MUST ship with at least one embedded
# base64 key literal — otherwise the verifier trusts nothing and the
# paywall is still 100% broken, which the sentinel grep alone can't see.
if grep -Eq "trustedKeysConfigured[[:space:]]*=[[:space:]]*true" "${KEYFILE}"; then
if ! grep -Eq '"[A-Za-z0-9+/]{40,}={0,2}"' "${KEYFILE}"; then
echo "error: Release build blocked — trustedKeysConfigured = true but no embedded base64 public key found in LicenseTrust.swift. Embed the production Ed25519 PUBLIC key(s) before shipping."
exit 1
fi
fi
fi
# Release purchase-funnel guard: the "Buy Pro" button must not ship pointing
# at the placeholder/parked domain (review H-2) — there's no other in-app path
# to a key, so a dead URL = an unsellable build. Refuse to build Release while
# LicenseLinks.purchaseURL is still the placeholder.
- name: "Guard: production purchase URL set for Release"
basedOnDependencyAnalysis: false
script: |
if [ "${CONFIGURATION}" = "Release" ]; then
LICVIEW="${SRCROOT}/Sources/NetworkUsageMonitor/Views/LicenseView.swift"
if grep -Eq 'networkmonitor\.app|TODO: production URL' "${LICVIEW}"; then
echo "error: Release build blocked — LicenseLinks.purchaseURL is still the placeholder (networkmonitor.app / 'TODO: production URL'). Set the real storefront / key-issuer URL in Sources/NetworkUsageMonitor/Views/LicenseView.swift before shipping; the Buy Pro button must reach a working purchase page."
exit 1
fi
fi
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: io.github.light-house-group.network-speed
PRODUCT_NAME: NetworkUsageMonitor
INFOPLIST_FILE: Scripts/Info.plist
# Menu-bar accessory: no Dock icon, no default window.
LSUIElement: YES
configs:
# FIREWALL_GLUE marks the Xcode app build, which (unlike the SPM build)
# compiles the host glue under FilterExtension/host/. AppDelegate guards
# its FirewallCoordinator call behind this flag so `swift build`/`swift
# test` stay green without the glue. DEBUG is kept for the Debug config
# (LicenseController's dev-unlock and other `#if DEBUG` paths rely on it).
#
# The host's NE entitlement value differs by signing identity: Apple
# Development profiles grant `content-filter-provider`, Developer ID
# distribution profiles grant `content-filter-provider-systemextension`.
# Split per-config so each build signs against the value its profile grants.
Debug:
SWIFT_ACTIVE_COMPILATION_CONDITIONS: DEBUG FIREWALL_GLUE
CODE_SIGN_ENTITLEMENTS: FilterExtension/host/NetworkMonitor.dev.entitlements
Release:
SWIFT_ACTIVE_COMPILATION_CONDITIONS: FIREWALL_GLUE
CODE_SIGN_ENTITLEMENTS: FilterExtension/host/NetworkMonitor.entitlements
# Manual Developer ID signing (see project-level configs.Release).
# Developer ID distribution profile for App ID
# io.github.light-house-group.network-speed. Match the profile's embedded
# Name exactly (verify: security cms -D -i <file> | plutil -p - | grep Name).
PROVISIONING_PROFILE_SPECIFIER: "Network Speed Provisioning Profile"
dependencies:
- package: MonitorCore
product: MonitorCore
# Embed the filter system extension under Contents/Library/SystemExtensions.
- target: FilterExtension
embed: true
# ---- Filter system extension (the enforcement data plane) ---------------
FilterExtension:
type: system-extension
platform: macOS
sources:
- path: FilterExtension/main.swift
- path: FilterExtension/FilterDataProvider.swift
- path: FilterExtension/FlowMapping.swift
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: io.github.light-house-group.network-speed.networkfilter
# Clean module name so Info.plist's NEProviderClasses resolves to
# `FilterExtension.FilterDataProvider` (PRODUCT_MODULE_NAME = PRODUCT_NAME).
PRODUCT_NAME: FilterExtension
INFOPLIST_FILE: FilterExtension/Info.plist
configs:
# The Apple Development profile authorizes `content-filter-provider`; the
# Developer ID distribution profile authorizes the `-systemextension`
# variant. The entitlements MUST match the active profile's value or
# signing fails with "profile doesn't match the entitlements file's value".
Debug:
CODE_SIGN_ENTITLEMENTS: FilterExtension/FilterExtension.dev.entitlements
Release:
CODE_SIGN_ENTITLEMENTS: FilterExtension/FilterExtension.entitlements
# Manual Developer ID signing. Developer ID distribution profile for App ID
# io.github.light-house-group.network-speed.networkfilter (it carries the NE
# content-filter-provider-systemextension + App Group entitlements). Match
# the profile's embedded Name exactly.
PROVISIONING_PROFILE_SPECIFIER: "Network Speed Filter Provisioning Profile"
dependencies:
- package: MonitorCore
product: MonitorCore
schemes:
# Run this scheme in Xcode (My Mac) once to register the device + auto-generate
# the development provisioning profiles (incl. the self-serve NE capability).
# Building the app pulls in FilterExtension + MonitorCore via dependencies.
NetworkMonitor:
build:
targets:
NetworkUsageMonitor: all
run:
config: Debug
archive:
config: Release