feat: add canonical verification project discovery - #180
Conversation
Greptile SummaryThis PR introduces canonical project-root resolution (
Confidence Score: 5/5Safe to merge. The change adds new hardening infrastructure that replaces several ad-hoc manifest walkers with a single, consistently fail-closed implementation. All three adoption sites (lint-strict config, secret declarations, and the new verification discovery) now share one security-hardened manifest loader. The dual lexical/canonical root check, the directory-symlink rejection, and the fail-closed unknown-platform hardlink guard all look correct and are covered by 33 focused tests plus the full 2k+ nextest suite. The one observation worth addressing before the next iteration is that Files Needing Attention: No files require special attention for merge safety;
|
| Filename | Overview |
|---|---|
| src/config.rs | Adds shared load_project_manifest / load_project_manifest_file helpers and cross-platform has_multiple_hardlinks; the non-unix/non-windows fallback now correctly fails closed (returns true), addressing the previously noted gap. |
| src/project.rs | New ProjectRoot::discover performs dual lexical + canonical manifest search and applies a second hardlink/symlink guard before committing the result; AmbiguousRoots error uses a non-root directory as one of the two "roots" in some arms (noted in previous outside-diff comment). |
| src/verification/manifest.rs | Versioned manifest parser with exhaustive path-validation (path escape, build-output, existence, hardlink, symlink-escape, extension-class consistency, duplicate/overlap detection); all critical checks are ordered correctly and covered by tests. |
| src/verification/discovery.rs | Deterministic recursive discovery that skips build-output dirs, hidden dirs, and node_modules; explicitly rejects directory symlinks (previously silently dropped per previous thread), and catches escape/hardlink/build-output for file symlinks; duplicate canonical targets detected across both auto and configured sources. |
| src/stdlib/secrets.rs | Refactored load_declarations to use the shared load_project_manifest helper; symlinked or hardlinked manifests now return SecretDeclarations::Invalid (fail-closed) instead of being transparently read, which is the intended new behavior. |
| src/main.rs | read_project_config_strict simplified to use load_project_manifest; non-regular manifests now return false (strict disabled) rather than potentially reading a symlinked config. |
| tests/verification_manifest_tests.rs | 33 focused tests covering root resolution, manifest loading, resource validation, and discovery; includes explicit regression tests for symlink escapes, hardlinks, directory symlinks, build-output aliases, and the cross-root ambiguity cases. |
| Cargo.toml | Adds windows-sys 0.59 as a Windows-only dependency for the hardlink helper (GetFileInformationByHandle); feature flags are minimal and correct. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ProjectRoot::discover start] --> B[load_project_manifest lexical walk]
A --> C[canonicalize path]
C --> D[load_project_manifest canonical walk]
B --> E{Match lexical + canonical results}
D --> E
E -->|Both found, roots agree| F[Re-verify manifest: not symlink / hardlink]
E -->|Both found, roots differ| ERR1[AmbiguousRoots error]
E -->|Only one found| ERR1
E -->|Neither found| ERR2[ManifestNotFound error]
F --> G[ProjectRoot]
G --> H[VerificationManifest::load]
H --> H1[Re-check manifest not symlink / hardlink]
H1 --> H2[Parse TOML verification table]
H2 --> H3{version == 1?}
H3 -->|No| ERR3[UnsupportedVersion]
H3 -->|Yes| H4[Sort files by path]
H4 --> H5[For each file: validate path, check existence, hardlinks, symlink escape, build-output, extension-class]
H5 -->|Any check fails| ERR4[ManifestError]
H5 -->|All pass| I[VerificationManifest]
G --> J[ProjectDiscovery::discover]
I --> J
J --> J1{manifest.project_root == root.path?}
J1 -->|No| ERR5[ManifestRootMismatch]
J1 -->|Yes| J2[collect_automatic: recurse intent files, skip hidden dirs / build outputs / nested manifests]
J2 --> J3[Check auto-found .intent files: symlink escape, directory symlink, build-output alias, hardlink]
J3 --> J4[Add manifest-configured files, check AutomaticManifestOverlap]
J4 --> J5[Canonicalize all paths, check DuplicateResource by canonical]
J5 --> J6[ProjectDiscovery: sorted DiscoveredFile list]
Reviews (3): Last reviewed commit: "fix: report lexical project root ambigui..." | Re-trigger Greptile
…ort' into feat/dd078-slice-2a-project-manifest
Summary
Validation
Stack
Depends on #179 (DD-078 Slice 1B).