aboutsummaryrefslogtreecommitdiff
path: root/tests/test-gptel-tools-git-status.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-16 11:30:04 -0500
committerCraig Jennings <c@cjennings.net>2026-05-16 11:30:04 -0500
commit244d4c56768fcc60bd1b23fe45df7a57c7b293ec (patch)
tree3a83c5953f7c963c3f3ab7b28044d6decd6ec2fb /tests/test-gptel-tools-git-status.el
parentb35d10bae7315fe3e497f1188bbe5ce86cef1bbf (diff)
downloaddotemacs-244d4c56768fcc60bd1b23fe45df7a57c7b293ec.tar.gz
dotemacs-244d4c56768fcc60bd1b23fe45df7a57c7b293ec.zip
feat(gptel-tools): harden path validation with file-truename realpath
Resolves PATH through file-truename before applying home-directory and read/write checks across the path-handling tools (git_status, git_log, git_diff, move_to_trash, read_text_file, update_text_file, write_text_file, list_directory_files, read_buffer, web_fetch). Without the resolve step, a symlink under HOME pointing outside HOME would pass the prefix check but the tool would act on the real target -- a symlink-escape. move_to_trash also tightens the trash-bin construction (treats empty file extensions correctly) and switches the "critical directories" list to truename-resolved canonical forms so a symlinked ~/.config can't be trashed via an aliased path. update_text_file fixes an off-by-one in the line-count derivation when the source content is empty. Each source change pairs with tests in tests/test-gptel-tools-*.el and tests/test-update-text-file.el covering the realpath escape paths, the empty-extension trash case, and the empty-content line- count edge. Combined coverage is now 100% across all ten gptel-tools source files: 516 / 516 executable lines, 217 tests.
Diffstat (limited to 'tests/test-gptel-tools-git-status.el')
-rw-r--r--tests/test-gptel-tools-git-status.el26
1 files changed, 26 insertions, 0 deletions
diff --git a/tests/test-gptel-tools-git-status.el b/tests/test-gptel-tools-git-status.el
index 734abb31..47193853 100644
--- a/tests/test-gptel-tools-git-status.el
+++ b/tests/test-gptel-tools-git-status.el
@@ -68,6 +68,18 @@
(should-error (cj/gptel-git-status--validate-path dir))
(when (file-exists-p dir) (delete-directory dir t)))))
+(ert-deftest test-gptel-tools-git-status-validate-path-error-symlink-outside-home ()
+ "Error: symlinked directories resolving outside HOME are rejected."
+ (let ((link (expand-file-name
+ (format ".test-gptel-tools-git-status-link-%s"
+ (format-time-string "%s%N"))
+ "~")))
+ (unwind-protect
+ (progn
+ (make-symbolic-link "/tmp" link t)
+ (should-error (cj/gptel-git-status--validate-path link)))
+ (when (file-symlink-p link) (delete-file link)))))
+
;; ---------- run
(ert-deftest test-gptel-tools-git-status-run-clean-tree ()
@@ -94,5 +106,19 @@
(let ((out (cj/gptel-git-status--run dir)))
(should (string-match-p "^## " out))))))
+(ert-deftest test-gptel-tools-git-status-run-error-on-git-status-failure ()
+ "Error: non-zero git status exits are surfaced."
+ (test-gptel-tools-git-status--with-repo
+ (lambda (dir)
+ (cl-letf (((symbol-function 'process-file)
+ (lambda (program infile destination display &rest args)
+ (if (member "status" args)
+ (progn
+ (when (bufferp destination)
+ (with-current-buffer destination (insert "bad status")))
+ 2)
+ (apply #'call-process program infile destination display args)))))
+ (should-error (cj/gptel-git-status--run dir))))))
+
(provide 'test-gptel-tools-git-status)
;;; test-gptel-tools-git-status.el ends here