Repository: twpayne/chezmoi (2026-03-29T00:17:56Z)

Issue:
re-add --re-encrypt: secret scanner warns on files that are already encrypted in source
## Summary

When running `chezmoi re-add --re-encrypt`, the secret scanner (`add.secrets: warning`) emits warnings for files that already have the `encrypted_` prefix in the source state. Since these files are stored as age-encrypted blobs in git, the secrets are not actually exposed.

## Steps to reproduce

1. Add a file containing an API key with encryption:
   ```
   chezmoi add --encrypt ~/.config/some-tool/config.toml
   ```
2. Modify the target file
3. Re-add with re-encryption:
   ```
   chezmoi re-add --re-encrypt
   ```

## Expected behavior

No warning for files that already have the `encrypted_` attribute in source state, since the plaintext never reaches git.

## Actual behavior

```
chezmoi: /home/user/.config/some-tool/config.toml:97: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
```

The operation succeeds (with `warning` mode), but the warning is misleading — it suggests the key is being exposed when it's actually about to be encrypted.

With `add.secrets: error`, this blocks `re-add --re-encrypt` entirely (exit code 1), making `error` mode unusable for repos with encrypted secrets.

## Question

Is this by design? Should the scanner skip files that already have the `encrypted_` source state attribute, or is there a recommended workflow I'm missing?

## Environment

- chezmoi v2.70.0
- encryption: age
- `add.secrets` at default (`warning`)


Discussion:
Oops, this is definitely a bug. Thanks for spotting it. For sure, if the file is encrypted then then secrets should be allowed.



Patch:
diff --git a/internal/chezmoi/sourcestate.go b/internal/chezmoi/sourcestate.go
index 2087c7fbeaa..127c8d10430 100644
--- a/internal/chezmoi/sourcestate.go
+++ b/internal/chezmoi/sourcestate.go
@@ -334,7 +334,7 @@ func NewSourceState(options ...SourceStateOption) *SourceState {
 }
 
 // A PreAddFunc is called before a new source state entry is added.
-type PreAddFunc func(targetRelPath RelPath, fileInfo fs.FileInfo) error
+type PreAddFunc func(targetRelPath RelPath, fileInfo fs.FileInfo, sourceStateEntry SourceStateEntry) error
 
 // A ReplaceFunc is called before a source state entry is replaced.
 type ReplaceFunc func(targetRelPath RelPath, newSourceStateEntry, oldSourceStateEntry SourceStateEntry) error
@@ -541,7 +541,7 @@ DEST_ABS_PATH:
 		}
 
 		if options.PreAddFunc != nil && destAbsPathInfo != nil {
-			switch err := options.PreAddFunc(targetRelPath, destAbsPathInfo); {
+			switch err := options.PreAddFunc(targetRelPath, destAbsPathInfo, newSourceStateEntry); {
 			case errors.Is(err, fs.SkipDir):
 				continue DEST_ABS_PATH
 			case err != nil:
diff --git a/internal/cmd/addcmd.go b/internal/cmd/addcmd.go
index 8ca1cbcf383..899cf5f8a97 100644
--- a/internal/cmd/addcmd.go
+++ b/internal/cmd/addcmd.go
@@ -87,9 +87,17 @@ func (c *Config) defaultOnIgnoreFunc(targetRelPath chezmoi.RelPath) {
 	}
 }
 
-func (c *Config) defaultPreAddFunc(targetRelPath chezmoi.RelPath, fileInfo fs.FileInfo) error {
+func (c *Config) defaultPreAddFunc(
+	targetRelPath chezmoi.RelPath,
+	fileInfo fs.FileInfo,
+	sourceStateEntry chezmoi.SourceStateEntry,
+) error {
 	// Scan unencrypted files for secrets, if configured.
-	if c.Add.Secrets.String() != severityIgnore && fileInfo.Mode().Type() == 0 && !c.Add.Encrypt {
+	encrypted := c.Add.Encrypt
+	if sourceStateFile, ok := sourceStateEntry.(*chezmoi.SourceStateFile); ok {
+		encrypted = sourceStateFile.Attr().Encrypted
+	}
+	if c.Add.Secrets.String() != severityIgnore && fileInfo.Mode().Type() == 0 && !encrypted {
 		absPath := c.DestDirAbsPath.Join(targetRelPath)
 		content, err := c.destSystem.ReadFile(absPath)
 		if err != nil {


Test patch:
diff --git a/internal/cmd/testdata/scripts/issue4976.txtar b/internal/cmd/testdata/scripts/issue4976.txtar
new file mode 100644
index 00000000000..ed384dcd191
--- /dev/null
+++ b/internal/cmd/testdata/scripts/issue4976.txtar
@@ -0,0 +1,13 @@
+mkageconfig
+appendline $CHEZMOICONFIGDIR/chezmoi.toml '[add]'
+appendline $CHEZMOICONFIGDIR/chezmoi.toml '    secrets = "error"'
+
+# test that chezmoi re-add --re-encrypt allows secrets in encrypted files
+exec chezmoi add --encrypt $HOME${/}.file
+! stderr .
+edit $HOME${/}.file
+exec chezmoi re-add --re-encrypt
+! stderr .
+
+-- home/user/.file --
+SUMO_ACCESS_KEY: gxq3rJQkS6qovOg9UY2Q70iH1jFZx0WBrrsiAYv4XHodogAwTKyLzvFK4neRN8Dk