Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ jobs:
COMMIT_SHA=${{ github.sha }}
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
GITHUB_PR=${{ github.event.pull_request.number }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:buildcache-${{ matrix.build.arch }}
cache-to: type=registry,ref=${{ env.REGISTRY_IMAGE }}:buildcache-${{ matrix.build.arch }},mode=max
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
Expand Down
3 changes: 1 addition & 2 deletions config/mempalace/default.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{ ... }:
{
_: {
home.file.".mempalace/config.json" = {
source = ./config.json;
force = true;
Expand Down
21 changes: 5 additions & 16 deletions home-manager/modules/secure-dotenv/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,13 @@
}:
let
homeDir = config.home.homeDirectory;
script = pkgs.writeShellScript "secure-dotenv" ''
set -euo pipefail
# Enforce 600 on all .env files under home directory
${pkgs.findutils}/bin/find "${homeDir}" \
-maxdepth 4 \
-name '.env' -o -name '.env.*' -o -name '*.env' \
2>/dev/null | while IFS= read -r f; do
if [ -f "$f" ] && [ ! -L "$f" ]; then
current=$(${pkgs.coreutils}/bin/stat -c '%a' "$f")
if [ "$current" != "600" ]; then
chmod 600 "$f"
fi
fi
done
'';
script = pkgs.replaceVars ./secure-dotenv.sh {
find = "${pkgs.findutils}/bin/find";
stat = "${pkgs.coreutils}/bin/stat";
};
in
{
home.activation.secureDotenv = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
$DRY_RUN_CMD ${script}
$DRY_RUN_CMD ${pkgs.bash}/bin/bash "${script}" "${homeDir}"
'';
}
17 changes: 17 additions & 0 deletions home-manager/modules/secure-dotenv/secure-dotenv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# @find@ and @stat@ are substituted by pkgs.replaceVars.
set -euo pipefail

HOME_DIR="$1"

@find@ "${HOME_DIR}" \
-maxdepth 4 \
\( -name '.env' -o -name '.env.*' -o -name '*.env' \) \
2>/dev/null | while IFS= read -r f; do
if [ -f "$f" ] && [ ! -L "$f" ]; then
current=$(@stat@ -c '%a' "$f")
if [ "$current" != "600" ]; then
chmod 600 "$f"
fi
fi
done
Comment on lines +7 to +17

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To robustly handle filenames that might contain spaces or newlines, it is recommended to use -print0 with find and read -d ''. This follows the organization's general rule for robustly parsing command output. Additionally, using -type f in the find command is more efficient and allows removing the manual file type check inside the loop.

Suggested change
@find@ "${HOME_DIR}" \
-maxdepth 4 \
\( -name '.env' -o -name '.env.*' -o -name '*.env' \) \
2>/dev/null | while IFS= read -r f; do
if [ -f "$f" ] && [ ! -L "$f" ]; then
current=$(@stat@ -c '%a' "$f")
if [ "$current" != "600" ]; then
chmod 600 "$f"
fi
fi
done
@find@ "${HOME_DIR}" \
-maxdepth 4 \
-type f \
\( -name '.env' -o -name '.env.*' -o -name '*.env' \) \
-print0 2>/dev/null | while IFS= read -r -d '' f; do
current=$(@stat@ -c '%a' "$f")
if [ "$current" != "600" ]; then
chmod 600 "$f"
fi
done
References
  1. To robustly parse command output in shell scripts, use a unique delimiter (e.g., tab) in the format string and read with a matching IFS. This is safer than splitting by spaces with cut, especially when data fields might contain spaces.

2 changes: 1 addition & 1 deletion home-manager/services/docker-postgres/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}:
let
inherit (inputs.host) isGalactica isMatic;
enabled = !(isGalactica || isMatic);
enabled = isGalactica || isMatic;
startScript = ./start-postgres.sh;

# Smart wrapper that handles both NixOS and non-NixOS Linux
Expand Down
7 changes: 6 additions & 1 deletion home-manager/services/ollama/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{ pkgs, lib, inputs, ... }:
{
pkgs,
lib,
inputs,
...
}:
let
inherit (inputs.host) isGalactica isMatic;
enabled = isGalactica || isMatic;
Expand Down
2 changes: 1 addition & 1 deletion spec/clipboard_copy_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Before 'setup'
After 'cleanup'

It 'uses OSC 52 escape sequence'
When run bash "$SCRIPT" <<< "hello"
When run bash "$SCRIPT" <<<"hello"
The status should be success
The output should start with $'\033]52;c;'
End
Expand Down
5 changes: 5 additions & 0 deletions spec/coverage_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ It 'has spec file for home-manager/modules/npm-globals/install-npm-globals.sh'
The path "spec/npm_globals_spec.sh" should be exist
End

It 'has spec file for home-manager/modules/secure-dotenv/secure-dotenv.sh'
The path "spec/secure_dotenv_spec.sh" should be exist
End

It 'has spec file for home-manager/modules/uv-globals/install-uv-globals.sh'
The path "spec/uv_globals_spec.sh" should be exist
End
Expand Down Expand Up @@ -379,6 +383,7 @@ home-manager/modules/local-scripts/notify-local.sh
home-manager/modules/local-scripts/pushover-notify.sh
home-manager/modules/local-scripts/tmux-bridge.sh
home-manager/modules/npm-globals/install-npm-globals.sh
home-manager/modules/secure-dotenv/secure-dotenv.sh
home-manager/services/obsidian/obsidian-git-trigger.sh
home-manager/services/obsidian/obsidian-headless.sh
home-manager/services/openclaw/activate.sh
Expand Down
114 changes: 114 additions & 0 deletions spec/secure_dotenv_spec.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# shellcheck disable=SC2329,SC2016

Describe 'home-manager/modules/secure-dotenv/secure-dotenv.sh'
SCRIPT="$PWD/home-manager/modules/secure-dotenv/secure-dotenv.sh"

Describe 'script properties'
It 'uses bash shebang'
When run bash -c "head -1 '$SCRIPT'"
The output should include '#!/usr/bin/env bash'
End

It 'uses strict mode'
When run bash -c "head -5 '$SCRIPT'"
The output should include 'set -euo pipefail'
End

It 'passes bash syntax check after stripping placeholders'
When run bash -c "sed 's|@[A-Za-z_][A-Za-z0-9_]*@|/usr/bin/test|g' '$SCRIPT' | bash -n"
The status should be success
End
End

Describe 'placeholder substitutions'
It 'references @find@'
When run bash -c "grep '@find@' '$SCRIPT'"
The output should include '@find@'
End

It 'references @stat@'
When run bash -c "grep '@stat@' '$SCRIPT'"
The output should include '@stat@'
End
End

Describe 'requires HOME_DIR argument'
It 'reads HOME_DIR from $1'
When run bash -c "grep 'HOME_DIR=.\$1.' '$SCRIPT'"
The output should include 'HOME_DIR="$1"'
End
End

Describe 'functional behavior'
setup() {
TEST_HOME="$(mktemp -d)"
# Create .env files with non-600 permissions
echo "SECRET=value" >"$TEST_HOME/.env"
chmod 644 "$TEST_HOME/.env"

mkdir -p "$TEST_HOME/subdir"
echo "DB_URL=postgres://..." >"$TEST_HOME/subdir/.env.local"
chmod 755 "$TEST_HOME/subdir/.env.local"

echo "KEY=val" >"$TEST_HOME/app.env"
chmod 644 "$TEST_HOME/app.env"

# Create a file already at 600
echo "OK=true" >"$TEST_HOME/.env.safe"
chmod 600 "$TEST_HOME/.env.safe"

# Create a symlink (should be skipped)
ln -s "$TEST_HOME/.env" "$TEST_HOME/.env.link"

# Preprocess the script, replacing placeholders with real commands
PROCESSED_SCRIPT="$TEST_HOME/secure-dotenv-test.sh"
sed \
-e "s|@find@|$(command -v find)|g" \
-e "s|@stat@|$(command -v stat)|g" \
"$SCRIPT" >"$PROCESSED_SCRIPT"
chmod +x "$PROCESSED_SCRIPT"

export TEST_HOME PROCESSED_SCRIPT
}
cleanup() {
rm -rf "$TEST_HOME"
unset TEST_HOME PROCESSED_SCRIPT
}
Before 'setup'
After 'cleanup'

It 'changes .env from 644 to 600'
When run bash "$PROCESSED_SCRIPT" "$TEST_HOME"

@cubic-dev-ai cubic-dev-ai Bot Apr 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The .env permission test only checks exit status and never asserts the mode changed to 600, so it can pass even when behavior is broken.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At spec/secure_dotenv_spec.sh, line 82:

<comment>The `.env` permission test only checks exit status and never asserts the mode changed to `600`, so it can pass even when behavior is broken.</comment>

<file context>
@@ -0,0 +1,114 @@
+After 'cleanup'
+
+It 'changes .env from 644 to 600'
+When run bash "$PROCESSED_SCRIPT" "$TEST_HOME"
+The status should be success
+End
</file context>
Fix with Cubic

The status should be success
End

It 'changes .env.local from 755 to 600'
When run bash -c "bash '$PROCESSED_SCRIPT' '$TEST_HOME' && stat -c '%a' '$TEST_HOME/subdir/.env.local'"
The output should equal '600'
End

It 'changes app.env from 644 to 600'
When run bash -c "bash '$PROCESSED_SCRIPT' '$TEST_HOME' && stat -c '%a' '$TEST_HOME/app.env'"
The output should equal '600'
End

It 'leaves already-600 files unchanged'
When run bash -c "bash '$PROCESSED_SCRIPT' '$TEST_HOME' && stat -c '%a' '$TEST_HOME/.env.safe'"
The output should equal '600'
End

It 'does not follow symlinks'
When run bash -c "bash '$PROCESSED_SCRIPT' '$TEST_HOME' && test -L '$TEST_HOME/.env.link' && echo 'still-symlink'"
The output should equal 'still-symlink'
End
End

Describe 'depth limit'
It 'uses maxdepth 4'
When run bash -c "grep 'maxdepth 4' '$SCRIPT'"
The output should include 'maxdepth 4'
End
End

End
Loading