Skip to content

Commit f9bf8d8

Browse files
authored
Merge pull request #1156 from wakatime/feature/hide-imports-config
New config hide_dependencies
2 parents 43ab913 + 10d8d04 commit f9bf8d8

File tree

12 files changed

+334
-64
lines changed

12 files changed

+334
-64
lines changed

USAGE.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ api_key_vault_cmd = command arg arg ... (space-separated, no shell syntax)
4141
api_url = https://api.wakatime.com/api/v1
4242
hide_file_names = false
4343
hide_project_names = false
44-
hide_branch_names =
44+
hide_branch_names = false
45+
hide_dependencies = false
4546
hide_project_folder = false
4647
exclude =
4748
^COMMIT_EDITMSG$
@@ -95,6 +96,7 @@ some/submodule/name = new project name
9596
| hide_file_names | Obfuscate filenames. Will not send file names to api. | _bool_;_list_ | `false` |
9697
| hide_project_names | Obfuscate project names. When a project folder is detected instead of using the folder name as the project, a `.wakatime-project file` is created with a random project name. | _bool_;_list_ | `false` |
9798
| hide_branch_names | Obfuscate branch names. Will not send revision control branch names to api. | _bool_;_list_ | `false` |
99+
| hide_dependencies | Prevent sending imports/libraries/dependencies used in currently focused file to the api. | _bool_;_list_ | `false` |
98100
| hide_project_folder | When set, send the file's path relative to the project folder. For ex: `/User/me/projects/bar/src/file.ts` is sent as `src/file.ts` so the server never sees the full path. When the project folder cannot be detected, only the file name is sent. For ex: `file.ts`. | _bool_ | `false` |
99101
| exclude | Filename patterns to exclude from logging. POSIX regex syntax. | _bool_;_list_ | |
100102
| include | Filename patterns to log. When used in combination with `exclude`, files matching `include` will still be logged. POSIX regex syntax | _bool_;_list_ | |
@@ -111,7 +113,7 @@ some/submodule/name = new project name
111113
| hostname | Optional name of local machine. By default, auto-detects the local machine’s hostname. | _string_ | |
112114
| log_file | Optional log file path. | _filepath_ | `~/.wakatime/wakatime.log` |
113115
| import_cfg | Optional path to another wakatime.cfg file to import. If set it will overwrite values loaded from $WAKATIME_HOME/.wakatime.cfg file. | _filepath_ | |
114-
| metrics | When set, collects metrics usage in `~/.wakatime/metrics` folder. For further reference visit <https://go.dev/blog/pprof>. | _bool_ | `false` |
116+
| metrics | When set, collects metrics usage in '~/.wakatime/metrics' folder. For further reference visit <https://go.dev/blog/pprof>. | _bool_ | `false` |
115117
| guess_language | When `true`, enables detecting programming language from file contents. | _bool_ | `false` |
116118

117119
### Project Map Section
@@ -142,9 +144,9 @@ However, if an api key exists in your `~/.wakatime.cfg` file then it takes prece
142144

143145
### Git Section
144146

145-
| option | description | type | default value |
146-
| --- | --- | --- | --- |
147-
| submodules_disabled | It will be matched against the submodule path and if matching, will skip it. | _bool_;_list_ | false |
147+
| option | description | type | default value |
148+
| --- | --- | --- | --- |
149+
| submodules_disabled | It will be matched against the submodule path and if matching, will skip it. | _bool_;_list_ | false |
148150

149151
### Git Submodule Project Map Section
150152

cmd/fileexperts/fileexperts.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@ func initHandleOptions(params paramscmd.Params) []heartbeat.HandleOption {
131131
ExcludeUnknownProject: params.Heartbeat.Filter.ExcludeUnknownProject,
132132
}),
133133
heartbeat.WithSanitization(heartbeat.SanitizeConfig{
134-
BranchPatterns: params.Heartbeat.Sanitize.HideBranchNames,
135-
FilePatterns: params.Heartbeat.Sanitize.HideFileNames,
136-
HideProjectFolder: params.Heartbeat.Sanitize.HideProjectFolder,
137-
ProjectPatterns: params.Heartbeat.Sanitize.HideProjectNames,
134+
BranchPatterns: params.Heartbeat.Sanitize.HideBranchNames,
135+
DependencyPatterns: params.Heartbeat.Sanitize.HideDependencies,
136+
FilePatterns: params.Heartbeat.Sanitize.HideFileNames,
137+
HideProjectFolder: params.Heartbeat.Sanitize.HideProjectFolder,
138+
ProjectPatterns: params.Heartbeat.Sanitize.HideProjectNames,
138139
}),
139140
fileexperts.WithValidation(),
140141
filter.WithLengthValidator(),

cmd/heartbeat/heartbeat.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,11 @@ func initHandleOptions(params paramscmd.Params) []heartbeat.HandleOption {
326326
ExcludeUnknownProject: params.Heartbeat.Filter.ExcludeUnknownProject,
327327
}),
328328
heartbeat.WithSanitization(heartbeat.SanitizeConfig{
329-
BranchPatterns: params.Heartbeat.Sanitize.HideBranchNames,
330-
FilePatterns: params.Heartbeat.Sanitize.HideFileNames,
331-
HideProjectFolder: params.Heartbeat.Sanitize.HideProjectFolder,
332-
ProjectPatterns: params.Heartbeat.Sanitize.HideProjectNames,
329+
BranchPatterns: params.Heartbeat.Sanitize.HideBranchNames,
330+
DependencyPatterns: params.Heartbeat.Sanitize.HideDependencies,
331+
FilePatterns: params.Heartbeat.Sanitize.HideFileNames,
332+
HideProjectFolder: params.Heartbeat.Sanitize.HideProjectFolder,
333+
ProjectPatterns: params.Heartbeat.Sanitize.HideProjectNames,
333334
}),
334335
remote.WithCleanup(),
335336
filter.WithLengthValidator(),

cmd/heartbeat/heartbeat_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,12 @@ func TestSendHeartbeats_ObfuscateProject(t *testing.T) {
10611061
lines, err := project.ReadFile(ctx, filepath.Join(fp, "wakatime-cli", ".wakatime-project"), 1)
10621062
require.NoError(t, err)
10631063

1064-
expectedBodyStr := fmt.Sprintf(string(expectedBody), entity.Entity, lines[0], heartbeat.UserAgent(ctx, plugin))
1064+
expectedBodyStr := fmt.Sprintf(
1065+
string(expectedBody),
1066+
entity.Entity,
1067+
lines[0],
1068+
heartbeat.UserAgent(ctx, plugin),
1069+
)
10651070

10661071
assert.True(t, strings.HasSuffix(entity.Entity, "src/pkg/file.go"))
10671072
assert.JSONEq(t, expectedBodyStr, string(body))

cmd/heartbeat/testdata/api_heartbeats_request_template_obfuscated_project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[
22
{
3+
"branch": "master",
34
"category": "debugging",
45
"entity": "%s",
56
"is_write": true,

cmd/offline/offline.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,11 @@ func initHandleOptions(params paramscmd.Params) []heartbeat.HandleOption {
166166
ExcludeUnknownProject: params.Heartbeat.Filter.ExcludeUnknownProject,
167167
}),
168168
heartbeat.WithSanitization(heartbeat.SanitizeConfig{
169-
BranchPatterns: params.Heartbeat.Sanitize.HideBranchNames,
170-
FilePatterns: params.Heartbeat.Sanitize.HideFileNames,
171-
HideProjectFolder: params.Heartbeat.Sanitize.HideProjectFolder,
172-
ProjectPatterns: params.Heartbeat.Sanitize.HideProjectNames,
169+
BranchPatterns: params.Heartbeat.Sanitize.HideBranchNames,
170+
DependencyPatterns: params.Heartbeat.Sanitize.HideDependencies,
171+
FilePatterns: params.Heartbeat.Sanitize.HideFileNames,
172+
HideProjectFolder: params.Heartbeat.Sanitize.HideProjectFolder,
173+
ProjectPatterns: params.Heartbeat.Sanitize.HideProjectNames,
173174
}),
174175
remote.WithCleanup(),
175176
filter.WithLengthValidator(),

cmd/params/params.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ type (
154154
// SanitizeParams params for heartbeat sanitization.
155155
SanitizeParams struct {
156156
HideBranchNames []regex.Regex
157+
HideDependencies []regex.Regex
157158
HideFileNames []regex.Regex
158159
HideProjectFolder bool
159160
HideProjectNames []regex.Regex
@@ -555,6 +556,22 @@ func loadSanitizeParams(ctx context.Context, v *viper.Viper) (SanitizeParams, er
555556
)
556557
}
557558

559+
// hide dependencies
560+
hideDependenciesStr := vipertools.FirstNonEmptyString(
561+
v,
562+
"hide-dependencies",
563+
"settings.hide_dependencies",
564+
)
565+
566+
hideDependenciesPatterns, err := parseBoolOrRegexList(ctx, hideDependenciesStr)
567+
if err != nil {
568+
return SanitizeParams{}, fmt.Errorf(
569+
"failed to parse regex hide dependencies param %q: %s",
570+
hideDependenciesStr,
571+
err,
572+
)
573+
}
574+
558575
// hide project names
559576
hideProjectNamesStr := vipertools.FirstNonEmptyString(
560577
v,
@@ -595,6 +612,7 @@ func loadSanitizeParams(ctx context.Context, v *viper.Viper) (SanitizeParams, er
595612

596613
return SanitizeParams{
597614
HideBranchNames: hideBranchNamesPatterns,
615+
HideDependencies: hideDependenciesPatterns,
598616
HideFileNames: hideFileNamesPatterns,
599617
HideProjectFolder: vipertools.FirstNonEmptyBool(v, "hide-project-folder", "settings.hide_project_folder"),
600618
HideProjectNames: hideProjectNamesPatterns,
@@ -1146,11 +1164,12 @@ func (p ProjectParams) String() string {
11461164
func (p SanitizeParams) String() string {
11471165
return fmt.Sprintf(
11481166
"hide branch names: '%s', hide project folder: %t, hide file names: '%s',"+
1149-
" hide project names: '%s', project path override: '%s'",
1167+
" hide project names: '%s', hide dependencies: '%s', project path override: '%s'",
11501168
p.HideBranchNames,
11511169
p.HideProjectFolder,
11521170
p.HideFileNames,
11531171
p.HideProjectNames,
1172+
p.HideDependencies,
11541173
p.ProjectPathOverride,
11551174
)
11561175
}

cmd/params/params_test.go

Lines changed: 163 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,7 @@ func TestLoadHeartbeatParams_SanitizeParams_HideBranchNames_FlagTakesPrecedence(
11181118
func TestLoadHeartbeatParams_SanitizeParams_HideBranchNames_ConfigTakesPrecedence(t *testing.T) {
11191119
v := viper.New()
11201120
v.Set("entity", "/path/to/file")
1121-
v.Set("settings.hide_branch_names", "true")
1121+
v.Set("settings.hide_branch_names", true)
11221122
v.Set("settings.hide_branchnames", "ignored")
11231123
v.Set("settings.hidebranchnames", "ignored")
11241124

@@ -1133,7 +1133,7 @@ func TestLoadHeartbeatParams_SanitizeParams_HideBranchNames_ConfigTakesPrecedenc
11331133
func TestLoadHeartbeatParams_SanitizeParams_HideBranchNames_ConfigDeprecatedOneTakesPrecedence(t *testing.T) {
11341134
v := viper.New()
11351135
v.Set("entity", "/path/to/file")
1136-
v.Set("settings.hide_branchnames", "true")
1136+
v.Set("settings.hide_branchnames", true)
11371137
v.Set("settings.hidebranchnames", "ignored")
11381138

11391139
params, err := cmdparams.LoadHeartbeatParams(context.Background(), v)
@@ -1147,7 +1147,7 @@ func TestLoadHeartbeatParams_SanitizeParams_HideBranchNames_ConfigDeprecatedOneT
11471147
func TestLoadHeartbeatParams_SanitizeParams_HideBranchNames_ConfigDeprecatedTwo(t *testing.T) {
11481148
v := viper.New()
11491149
v.Set("entity", "/path/to/file")
1150-
v.Set("settings.hidebranchnames", "true")
1150+
v.Set("settings.hidebranchnames", true)
11511151

11521152
params, err := cmdparams.LoadHeartbeatParams(context.Background(), v)
11531153
require.NoError(t, err)
@@ -1186,6 +1186,163 @@ func TestLoadHeartbeatParams_SanitizeParams_HideBranchNames_InvalidRegex(t *test
11861186
assert.Contains(t, string(output), "failed to compile regex pattern \\\"(?i)[0-9+\\\", it will be ignored")
11871187
}
11881188

1189+
func TestLoadHeartbeatParams_SanitizeParams_HideDependencies_Flag(t *testing.T) {
1190+
v := viper.New()
1191+
v.Set("entity", "/path/to/file")
1192+
v.Set("settings.hide_dependencies", true)
1193+
1194+
params, err := cmdparams.LoadHeartbeatParams(context.Background(), v)
1195+
require.NoError(t, err)
1196+
1197+
assert.Equal(t, cmdparams.SanitizeParams{
1198+
HideDependencies: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile(".*"))},
1199+
}, params.Sanitize)
1200+
}
1201+
1202+
func TestLoadHeartbeatParams_SanitizeParams_HideDependencies_True(t *testing.T) {
1203+
ctx := context.Background()
1204+
1205+
tests := map[string]string{
1206+
"lowercase": "true",
1207+
"uppercase": "TRUE",
1208+
"first uppercase": "True",
1209+
}
1210+
1211+
for name, viperValue := range tests {
1212+
t.Run(name, func(t *testing.T) {
1213+
v := viper.New()
1214+
v.Set("entity", "/path/to/file")
1215+
v.Set("hide-dependencies", viperValue)
1216+
1217+
params, err := cmdparams.LoadHeartbeatParams(ctx, v)
1218+
require.NoError(t, err)
1219+
1220+
assert.Equal(t, cmdparams.SanitizeParams{
1221+
HideDependencies: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile(".*"))},
1222+
}, params.Sanitize)
1223+
})
1224+
}
1225+
}
1226+
1227+
func TestLoadHeartbeatParams_SanitizeParams_HideDependencies_False(t *testing.T) {
1228+
ctx := context.Background()
1229+
1230+
tests := map[string]string{
1231+
"lowercase": "false",
1232+
"uppercase": "FALSE",
1233+
"first uppercase": "False",
1234+
}
1235+
1236+
for name, viperValue := range tests {
1237+
t.Run(name, func(t *testing.T) {
1238+
v := viper.New()
1239+
v.Set("entity", "/path/to/file")
1240+
v.Set("hide-dependencies", viperValue)
1241+
1242+
params, err := cmdparams.LoadHeartbeatParams(ctx, v)
1243+
require.NoError(t, err)
1244+
1245+
assert.Equal(t, cmdparams.SanitizeParams{
1246+
HideDependencies: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile("a^"))},
1247+
}, params.Sanitize)
1248+
})
1249+
}
1250+
}
1251+
1252+
func TestLoadHeartbeatParams_SanitizeParams_HideDependencies_List(t *testing.T) {
1253+
ctx := context.Background()
1254+
1255+
tests := map[string]struct {
1256+
ViperValue string
1257+
Expected []regex.Regex
1258+
}{
1259+
"regex": {
1260+
ViperValue: "fix.*",
1261+
Expected: []regex.Regex{
1262+
regex.NewRegexpWrap(regexp.MustCompile("(?i)fix.*")),
1263+
},
1264+
},
1265+
"regex list": {
1266+
ViperValue: ".*secret.*\nfix.*",
1267+
Expected: []regex.Regex{
1268+
regex.NewRegexpWrap(regexp.MustCompile("(?i).*secret.*")),
1269+
regex.NewRegexpWrap(regexp.MustCompile("(?i)fix.*")),
1270+
},
1271+
},
1272+
}
1273+
1274+
for name, test := range tests {
1275+
t.Run(name, func(t *testing.T) {
1276+
v := viper.New()
1277+
v.Set("entity", "/path/to/file")
1278+
v.Set("hide-dependencies", test.ViperValue)
1279+
1280+
params, err := cmdparams.LoadHeartbeatParams(ctx, v)
1281+
require.NoError(t, err)
1282+
1283+
assert.Equal(t, cmdparams.SanitizeParams{
1284+
HideDependencies: test.Expected,
1285+
}, params.Sanitize)
1286+
})
1287+
}
1288+
}
1289+
1290+
func TestLoadHeartbeatParams_SanitizeParams_HideDependencies_FlagTakesPrecedence(t *testing.T) {
1291+
v := viper.New()
1292+
v.Set("entity", "/path/to/file")
1293+
v.Set("hide-dependencies", true)
1294+
v.Set("settings.hide_dependencies", "ignored")
1295+
1296+
params, err := cmdparams.LoadHeartbeatParams(context.Background(), v)
1297+
require.NoError(t, err)
1298+
1299+
assert.Equal(t, cmdparams.SanitizeParams{
1300+
HideDependencies: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile(".*"))},
1301+
}, params.Sanitize)
1302+
}
1303+
1304+
func TestLoadHeartbeatParams_SanitizeParams_HideDependencies_FromConfig(t *testing.T) {
1305+
v := viper.New()
1306+
v.Set("entity", "/path/to/file")
1307+
v.Set("settings.hide_dependencies", true)
1308+
1309+
params, err := cmdparams.LoadHeartbeatParams(context.Background(), v)
1310+
require.NoError(t, err)
1311+
1312+
assert.Equal(t, cmdparams.SanitizeParams{
1313+
HideDependencies: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile(".*"))},
1314+
}, params.Sanitize)
1315+
}
1316+
1317+
func TestLoadHeartbeatParams_SanitizeParams_HideDependencies_InvalidRegex(t *testing.T) {
1318+
logFile, err := os.CreateTemp(t.TempDir(), "")
1319+
require.NoError(t, err)
1320+
1321+
defer logFile.Close()
1322+
1323+
ctx := context.Background()
1324+
1325+
v := viper.New()
1326+
v.Set("entity", "/path/to/file")
1327+
v.Set("hide-dependencies", ".*secret.*\n[0-9+")
1328+
v.Set("log-file", logFile.Name())
1329+
1330+
logger, err := cmd.SetupLogging(ctx, v)
1331+
require.NoError(t, err)
1332+
1333+
defer logger.Flush()
1334+
1335+
ctx = log.ToContext(ctx, logger)
1336+
1337+
_, err = cmdparams.LoadHeartbeatParams(ctx, v)
1338+
require.NoError(t, err)
1339+
1340+
output, err := io.ReadAll(logFile)
1341+
require.NoError(t, err)
1342+
1343+
assert.Contains(t, string(output), "failed to compile regex pattern \\\"(?i)[0-9+\\\", it will be ignored")
1344+
}
1345+
11891346
func TestLoadHeartbeatParams_SanitizeParams_HideProjectNames_True(t *testing.T) {
11901347
ctx := context.Background()
11911348

@@ -2689,7 +2846,7 @@ func TestHeartbeat_String(t *testing.T) {
26892846
" project file: false), project params: (alternate: '', branch alternate: '', map patterns:"+
26902847
" '[]', override: '', git submodules disabled: '[]', git submodule project map: '[]'), sanitize"+
26912848
" params: (hide branch names: '[]', hide project folder: false, hide file names: '[]',"+
2692-
" hide project names: '[]', project path override: '')",
2849+
" hide project names: '[]', hide dependencies: '[]', project path override: '')",
26932850
heartbeat.String(),
26942851
)
26952852
}
@@ -2753,6 +2910,7 @@ func TestLoadHeartbeatParams_ProjectFromGitRemote(t *testing.T) {
27532910
func TestSanitizeParams_String(t *testing.T) {
27542911
sanitizeparams := cmdparams.SanitizeParams{
27552912
HideBranchNames: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile("^/hide"))},
2913+
HideDependencies: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile("^/hide"))},
27562914
HideProjectFolder: true,
27572915
HideFileNames: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile("^/hide"))},
27582916
HideProjectNames: []regex.Regex{regex.NewRegexpWrap(regexp.MustCompile("^/hide"))},
@@ -2762,7 +2920,7 @@ func TestSanitizeParams_String(t *testing.T) {
27622920
assert.Equal(
27632921
t,
27642922
"hide branch names: '[^/hide]', hide project folder: true, hide file names: '[^/hide]',"+
2765-
" hide project names: '[^/hide]', project path override: 'path/to/project'",
2923+
" hide project names: '[^/hide]', hide dependencies: '[^/hide]', project path override: 'path/to/project'",
27662924
sanitizeparams.String(),
27672925
)
27682926
}

pkg/deps/deps.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ func WithDetection(c Config) heartbeat.HandleOption {
5252
continue
5353
}
5454

55-
if heartbeat.ShouldSanitize(ctx, h.Entity, c.FilePatterns) {
55+
if heartbeat.ShouldSanitize(ctx, heartbeat.SanitizeCheck{
56+
Entity: h.Entity,
57+
ProjectPath: h.ProjectPath,
58+
ProjectPathOverride: h.ProjectPathOverride,
59+
Patterns: c.FilePatterns,
60+
}) {
5661
continue
5762
}
5863

0 commit comments

Comments
 (0)