MCPcopy Create free account
hub / github.com/coder/coder / writeSecretFiles

Function writeSecretFiles

agent/agent.go:1634–1697  ·  view source on GitHub ↗

writeSecretFiles writes user secrets with file_path set to disk. Errors are logged but do not block workspace startup.

(ctx context.Context, logger slog.Logger, fs afero.Fs, homeDir string, secrets []agentsdk.WorkspaceSecret)

Source from the content-addressed store, hash-verified

1632// writeSecretFiles writes user secrets with file_path set to disk.
1633// Errors are logged but do not block workspace startup.
1634func writeSecretFiles(ctx context.Context, logger slog.Logger, fs afero.Fs, homeDir string, secrets []agentsdk.WorkspaceSecret) {
1635 // Track resolved paths to detect collisions after ~/ expansion.
1636 // Two secrets with different file_path values can resolve to
1637 // the same absolute path (e.g. ~/x and /home/coder/x). The API
1638 // layer prevents duplicates on the raw file_path but cannot see
1639 // post-resolution collisions. We still write both, with the
1640 // later one winning, but log a warning so the conflict is
1641 // visible.
1642 seen := make(map[string]string, len(secrets))
1643
1644 for _, secret := range secrets {
1645 if secret.FilePath == "" {
1646 continue
1647 }
1648
1649 filePath := secret.FilePath
1650 if strings.HasPrefix(filePath, "~/") {
1651 if homeDir == "" {
1652 logger.Warn(ctx, "skipping secret file with ~/ path: home directory unknown",
1653 slog.F("file_path", filePath),
1654 )
1655 continue
1656 }
1657 filePath = filepath.Join(homeDir, filePath[2:])
1658 }
1659 filePath = filepath.Clean(filePath)
1660
1661 if original, ok := seen[filePath]; ok {
1662 // Known shortcoming: the winning secret is determined by the order
1663 // of secrets in the manifest, which is currently alphabetical by
1664 // secret name from ListUserSecretsWithValues. This ordering is not
1665 // user-controllable and has no semantic meaning; users should avoid
1666 // path collisions rather than rely on which secret wins.
1667 logger.Warn(ctx, "multiple secrets resolve to the same file path; later secret in manifest order will win (not user-controllable)",
1668 slog.F("resolved_path", filePath),
1669 slog.F("first_file_path", original),
1670 slog.F("conflicting_file_path", secret.FilePath),
1671 )
1672 }
1673 seen[filePath] = secret.FilePath
1674
1675 dir := filepath.Dir(filePath)
1676 if err := fs.MkdirAll(dir, 0o700); err != nil {
1677 logger.Warn(ctx, "failed to create directory for secret file",
1678 slog.F("file_path", filePath),
1679 slog.Error(err),
1680 )
1681 continue
1682 }
1683
1684 // The 0o600 perm only applies when the file is created.
1685 // If the file already exists, its permissions are
1686 // preserved. We only update the content.
1687 if err := afero.WriteFile(fs, filePath, secret.Value, 0o600); err != nil {
1688 logger.Warn(ctx, "failed to write secret file",
1689 slog.F("file_path", filePath),
1690 slog.Error(err),
1691 )

Callers 2

TestWriteSecretFilesFunction · 0.85
handleManifestMethod · 0.85

Calls 3

MkdirAllMethod · 0.80
WriteFileMethod · 0.65
ErrorMethod · 0.45

Tested by 1

TestWriteSecretFilesFunction · 0.68