Skip to content

Commit 3b1db1b

Browse files
committed
Add Privacy Guides integration
- Introduced a new supplement for applying Privacy Guides recommendations on top of existing presets or custom settings. - Updated documentation to include instructions for using the Privacy Guides feature in README.md, CONTRIBUTING.md, and new PRIVACY-GUIDES.md. - Enhanced the TUI to allow users to select a base preset when applying Privacy Guides. - Implemented backend logic to merge Privacy Guides settings with existing presets, ensuring no overlap. - Added tests for loading and merging Privacy Guides settings.
1 parent 339e059 commit 3b1db1b

15 files changed

Lines changed: 855 additions & 101 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Thank you for your interest in contributing. This document explains how to get s
7777

7878
- [README.md](README.md) — Install, usage, presets
7979
- [docs/ADDING-PRESETS.md](docs/ADDING-PRESETS.md) — How to add a preset
80+
- [docs/PRIVACY-GUIDES.md](docs/PRIVACY-GUIDES.md) — Privacy Guides supplement
8081
- [docs/PROJECT-LAYOUT.md](docs/PROJECT-LAYOUT.md) — Directory structure
8182
- [docs/FUTURE.md](docs/FUTURE.md) — Possible improvements
8283
- [.github/workflows/README.md](.github/workflows/README.md) — CI workflows

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ cowardly
7676
```
7777

7878
- **Apply a preset** — Choose a preset (Quick Debloat, Maximum Privacy, Balanced, Performance, Developer, Strict Parental) and apply it.
79+
- **Privacy Guides recommendations** — Apply [Privacy Guides](https://www.privacyguides.org/en/desktop-browsers/#brave) Brave config as a supplement on top of any preset or Custom (Quick Debloat by default; no overlap with presets).
7980
- **Custom** — Toggle individual settings by category (Telemetry, Privacy & Security, Brave Features, Performance & Bloat), then apply.
8081
- **View current settings** — See which policy keys are set.
8182
- **Reset all to default** — Remove all Brave policy settings (restore defaults).
@@ -95,6 +96,14 @@ After applying or resetting, **restart Brave Browser** for changes to take effec
9596
cowardly --apply=max-privacy
9697
```
9798

99+
- **Privacy Guides recommendations** — Apply [Privacy Guides](https://www.privacyguides.org/en/desktop-browsers/#brave) supplement on top of any preset or Custom:
100+
101+
```bash
102+
cowardly --privacy-guides # base from config or Quick Debloat
103+
cowardly --privacy-guides=max-privacy # base: Maximum Privacy
104+
cowardly --privacy-guides=custom # base: your saved Custom settings
105+
```
106+
98107
- **Apply from a YAML file** (same format as preset `settings`):
99108

100109
```bash
@@ -191,7 +200,7 @@ Use **Space** to toggle, **Enter** to apply, **a** to select all, **n** to selec
191200

192201
## Project layout
193202

194-
The repo follows the [Standard Go Project Layout](https://github.com/golang-standards/project-layout). See **[docs/PROJECT-LAYOUT.md](docs/PROJECT-LAYOUT.md)** for the directory overview, **[docs/PLATFORMS.md](docs/PLATFORMS.md)** for current and possible future platform support (macOS / Linux / Windows), and **[docs/POLICY-ENFORCEMENT.md](docs/POLICY-ENFORCEMENT.md)** for how policy enforcement works on macOS (managed vs user preferences, raw XML plist, AppleScript auth). A summary of implemented features is in **[docs/FEATURES.md](docs/FEATURES.md)**; possible future improvements are in **[docs/FUTURE.md](docs/FUTURE.md)**.
203+
The repo follows the [Standard Go Project Layout](https://github.com/golang-standards/project-layout). See **[docs/PROJECT-LAYOUT.md](docs/PROJECT-LAYOUT.md)** for the directory overview, **[docs/PLATFORMS.md](docs/PLATFORMS.md)** for current and possible future platform support (macOS / Linux / Windows), and **[docs/POLICY-ENFORCEMENT.md](docs/POLICY-ENFORCEMENT.md)** for how policy enforcement works on macOS (managed vs user preferences, raw XML plist, AppleScript auth). A summary of implemented features is in **[docs/FEATURES.md](docs/FEATURES.md)**; possible future improvements are in **[docs/FUTURE.md](docs/FUTURE.md)**. For the Privacy Guides supplement, see **[docs/PRIVACY-GUIDES.md](docs/PRIVACY-GUIDES.md)**.
195204

196205
## Development
197206

cmd/cowardly/main.go

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ func main() {
4646
case strings.HasPrefix(arg, "apply="):
4747
applyPreset(strings.TrimPrefix(arg, "apply="))
4848
return
49+
case arg == "privacy-guides":
50+
applyPrivacyGuides(parsePrivacyGuidesBase("privacy-guides"))
51+
return
52+
case strings.HasPrefix(arg, "privacy-guides="):
53+
applyPrivacyGuides(strings.TrimPrefix(arg, "privacy-guides="))
54+
return
4955
case arg == "dry-run":
5056
dryRun("quick")
5157
return
@@ -105,6 +111,22 @@ func versionInfo() {
105111
}
106112
}
107113

114+
// parsePrivacyGuidesBase returns the base preset ID if presetID is privacy-guides form, else "".
115+
// "privacy-guides" -> config base if set, else "quick"; "privacy-guides:max-privacy" -> "max-privacy".
116+
func parsePrivacyGuidesBase(presetID string) string {
117+
if presetID == "privacy-guides" {
118+
base, _ := userconfig.PrivacyGuidesBaseFromConfig()
119+
if base != "" {
120+
return base
121+
}
122+
return presets.PrivacyGuidesBasePresetID
123+
}
124+
if strings.HasPrefix(presetID, "privacy-guides:") {
125+
return strings.TrimPrefix(presetID, "privacy-guides:")
126+
}
127+
return ""
128+
}
129+
108130
func findPreset(id string) *presets.Preset {
109131
plist, _ := presets.AllWithError()
110132
if plist == nil {
@@ -118,22 +140,59 @@ func findPreset(id string) *presets.Preset {
118140
return nil
119141
}
120142

143+
func privacyGuidesSettings(baseID string) ([]brave.Setting, error) {
144+
if baseID == "custom" {
145+
desired, _ := userconfig.Read()
146+
if desired == nil || len(desired.Settings) == 0 {
147+
return nil, fmt.Errorf("no custom settings in config to use as base")
148+
}
149+
supplement, err := presets.LoadPrivacyGuides()
150+
if err != nil {
151+
return nil, err
152+
}
153+
return presets.MergeSettingsWithSupplement(desired.Settings, supplement), nil
154+
}
155+
return presets.PrivacyGuidesMerged(baseID)
156+
}
157+
121158
func dryRun(presetID string) {
122-
p := findPreset(presetID)
123-
if p == nil {
124-
fmt.Fprintf(os.Stderr, "Preset %q not found.\n", presetID)
125-
os.Exit(1)
159+
var settings []brave.Setting
160+
if baseID := parsePrivacyGuidesBase(presetID); baseID != "" {
161+
var err error
162+
settings, err = privacyGuidesSettings(baseID)
163+
if err != nil {
164+
fmt.Fprintf(os.Stderr, "privacy-guides: %v\n", err)
165+
os.Exit(1)
166+
}
167+
} else {
168+
p := findPreset(presetID)
169+
if p == nil {
170+
fmt.Fprintf(os.Stderr, "Preset %q not found.\n", presetID)
171+
os.Exit(1)
172+
}
173+
settings = p.Settings
126174
}
127-
fmt.Println(brave.DryRun(p.Settings))
175+
fmt.Println(brave.DryRun(settings))
128176
}
129177

130178
func diffPreset(presetID string) {
131-
p := findPreset(presetID)
132-
if p == nil {
133-
fmt.Fprintf(os.Stderr, "Preset %q not found.\n", presetID)
134-
os.Exit(1)
179+
var settings []brave.Setting
180+
if baseID := parsePrivacyGuidesBase(presetID); baseID != "" {
181+
var err error
182+
settings, err = privacyGuidesSettings(baseID)
183+
if err != nil {
184+
fmt.Fprintf(os.Stderr, "privacy-guides: %v\n", err)
185+
os.Exit(1)
186+
}
187+
} else {
188+
p := findPreset(presetID)
189+
if p == nil {
190+
fmt.Fprintf(os.Stderr, "Preset %q not found.\n", presetID)
191+
os.Exit(1)
192+
}
193+
settings = p.Settings
135194
}
136-
diff := brave.Diff(p.Settings)
195+
diff := brave.Diff(settings)
137196
if diff == "" {
138197
fmt.Println("No changes (current values match preset).")
139198
return
@@ -142,6 +201,41 @@ func diffPreset(presetID string) {
142201
fmt.Println(diff)
143202
}
144203

204+
func applyPrivacyGuides(basePresetID string) {
205+
if basePresetID == "" {
206+
basePresetID = presets.PrivacyGuidesBasePresetID
207+
}
208+
if !brave.BraveInstalled() {
209+
fmt.Fprintln(os.Stderr, "Brave Browser not found in /Applications.")
210+
os.Exit(1)
211+
}
212+
if brave.BraveRunning() {
213+
fmt.Fprintln(os.Stderr, "Warning: Brave is running. Quit Brave for a clean apply.")
214+
}
215+
settings, err := privacyGuidesSettings(basePresetID)
216+
if err != nil {
217+
fmt.Fprintf(os.Stderr, "privacy-guides: %v\n", err)
218+
os.Exit(1)
219+
}
220+
if path, err := brave.BackupUserPlist(); err == nil {
221+
fmt.Fprintf(os.Stderr, "Backed up user plist to %s\n", path)
222+
}
223+
managed, err := brave.ApplySettings(settings)
224+
if err != nil {
225+
fmt.Fprintf(os.Stderr, "apply failed: %v\n", err)
226+
os.Exit(1)
227+
}
228+
if managed {
229+
fmt.Printf("Applied Privacy Guides recommendations (enforced). Restart Brave for changes to take effect.\n")
230+
} else {
231+
fmt.Printf("Applied Privacy Guides recommendations. Restart Brave. For enforced policies, approve the macOS authentication dialog when you run apply.\n")
232+
}
233+
fmt.Fprintf(os.Stderr, "Source: %s\n", presets.PrivacyGuidesURL)
234+
if err := userconfig.WritePrivacyGuides(basePresetID); err != nil {
235+
fmt.Fprintf(os.Stderr, "Note: could not save desired state to ~/.config/cowardly: %v\n", err)
236+
}
237+
}
238+
145239
func applyPreset(presetID string) {
146240
if !brave.BraveInstalled() {
147241
fmt.Fprintln(os.Stderr, "Brave Browser not found in /Applications.")
@@ -459,6 +553,7 @@ Usage:
459553
cowardly Start the TUI
460554
cowardly --apply, -a Apply Quick Debloat preset and exit
461555
cowardly --apply=<id> Apply preset by ID (e.g. quick, max-privacy)
556+
cowardly --privacy-guides [=base] Apply Privacy Guides supplement (default base: quick)
462557
cowardly --apply-file=<path> Apply settings from a YAML file
463558
cowardly --reapply Re-apply last saved desired state (~/.config/cowardly)
464559
cowardly --install-login-hook Install Launch Agent to run --reapply at login

configs/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,10 @@ Configuration file templates and default configs.
66

77
Brave debloat presets are **YAML** files here. Each file (e.g. `01-quick.yaml`) is one preset; order in the TUI is by filename. To add a preset, add a new `.yaml` file in **configs/presets/** and rebuild. See **[docs/ADDING-PRESETS.md](../docs/ADDING-PRESETS.md)** for the format and instructions.
88

9+
## supplements/
10+
11+
Supplements apply on top of presets (or Custom). Each subdirectory (e.g. **privacy-guides/**) is one supplement.
12+
13+
- **privacy-guides/**[Privacy Guides](https://www.privacyguides.org/en/desktop-browsers/#brave) recommended Brave configuration (Shields, P3A, De-AMP, etc.). Contains only settings not in presets. Apply via TUI or `--privacy-guides` / `--privacy-guides=<base>` (base: quick, max-privacy, custom, etc.).
14+
915
This directory is reserved per the [Standard Go Project Layout](https://github.com/golang-standards/project-layout). Tool configs (e.g. `.golangci.yml`, `renovate.json`) remain at repository root by convention.

configs/embed.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ import "embed"
99
//
1010
//go:embed presets/*.yaml
1111
var PresetsFS embed.FS
12+
13+
// PrivacyGuidesFS contains the Privacy Guides recommended Brave configuration.
14+
// See https://www.privacyguides.org/en/desktop-browsers/#brave
15+
//
16+
//go:embed supplements/privacy-guides/*.yaml
17+
var PrivacyGuidesFS embed.FS
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Privacy Guides supplement — applies on top of Quick Debloat preset
2+
# Source: https://www.privacyguides.org/en/desktop-browsers/#brave
3+
#
4+
# Contains only settings NOT in presets. Apply Privacy Guides = Quick Debloat + this.
5+
# Policy keys from Brave policy templates (brave-core).
6+
#
7+
# No policy available (configure manually in Brave):
8+
# - Use default filter lists (behavioral recommendation)
9+
# - Block Scripts (optional; disables JS entirely)
10+
# - Uncheck social media components (Shields UI)
11+
# - Automatically remove permissions from unused sites
12+
# - Use Google services for push messaging
13+
settings:
14+
# Data Collection — P3A, daily usage ping (Brave-specific; not in presets)
15+
- key: BraveP3AEnabled
16+
value: false
17+
type: bool
18+
- key: BraveStatsPingEnabled
19+
value: false
20+
type: bool
21+
# Shields — Trackers, HTTPS, fingerprinting, forget on close
22+
- key: DefaultBraveAdblockSetting
23+
value: 2
24+
type: integer
25+
- key: DefaultBraveHttpsUpgradeSetting
26+
value: 2
27+
type: integer
28+
- key: DefaultBraveFingerprintingV2Setting
29+
value: 3
30+
type: integer
31+
- key: DefaultBraveRemember1PStorageSetting
32+
value: 2
33+
type: integer
34+
# Privacy & Security — AMP, tracking redirects, language FP, V8 JIT
35+
- key: BraveDeAmpEnabled
36+
value: true
37+
type: bool
38+
- key: BraveDebouncingEnabled
39+
value: true
40+
type: bool
41+
- key: BraveReduceLanguageEnabled
42+
value: true
43+
type: bool
44+
- key: DefaultJavaScriptJitSetting
45+
value: 2
46+
type: integer

0 commit comments

Comments
 (0)