Files
mfer/TODO.md
T
clawbot 7e601929c8
check / check (push) Successful in 1m10s
Refuse fetch writes through a symlink in the destination (closes #86)
sanitizePath checks manifest paths only as text, so a symlink already
inside the destination directory could send fetch's writes outside it.
checkNoSymlinks now looks at each existing part of a path with os.Lstat
and refuses the path if any part is a symlink, wherever it points.
fetch runs it immediately before each write: creating the parent
directories, creating the temp file, and renaming it into place. The new
test puts such a symlink at each of those three places, and once inside
a plain directory, and checks that the fetch fails and nothing outside
changes. The G304 comment now states what holds. A symlink swapped in
between a check and its write is not caught; os.Root closes that once
the Go version is raised.

Model: opus-5-5
2026-10-03 16:08:09 +02:00

6.5 KiB

Workflow

  • branch (from main)
  • do the work in Next Step
  • move Next Step to the top of Completed Steps
  • move the top item of Future Steps into Next Step
  • commit (TODO.md changes in the same commit as the work)
  • merge to main if the branch is not protected, otherwise open a PR
  • push

Status

pre-1.0. No git tags. README section "TODO: Remaining Work for 1.0" lists open design questions and implementation tasks; policy compliance work is in flight and unmerged.

Next Step

Work through the remaining compliance items folded from the 2026-07-02 audit (the first group under Future Steps): .editorconfig, .gitignore coverage, gofumpt-based fmt-check, README "Getting Started", and the rest. .golangci.yml and TODO.md are tracked and committed as of 2026-08-07, so the only thing left of the chore/align-repo-policies branch is the list below.

Completed Steps

  • 2026-10-03: fetch refuses any manifest path that runs through a symlink already in the destination directory, checked before each of its writes (directories, temp file, rename), so such a symlink cannot send a write outside it (#86)
  • 2026-10-02: a plain docker build . of a clone now stamps the tag or short commit into mfer version instead of nothing: .dockerignore sends .git (not .git/config), and the build stage takes the VERSION build argument, otherwise git describe --tags --always, failing if .git is present and no version comes out. script/docker is the canonical copy, which passes VERSION; bin/gitrev.sh uses --tags too (#112)
  • 2026-09-21: validate manifest entry paths on deserialize so untrusted .mf files cannot make Checker stat or read outside basePath (#61)
  • 2026-09-21: rewrote script/test to the canonical pattern (30s timeout, -race -cover, quiet-first with verbose-on-failure rerun) and fixed the process-global logger data race it surfaced (#67)
  • 2026-09-21: added the canonical .editorconfig, made .gitignore cover secrets, OS, editor, and Go artifacts, and removed the dead Drone CI references from .gitignore and bin/gitrev.sh (#72)
  • 2026-08-09: added .prettierrc/.prettierignore, gave script/fmt and script/fmt-check one shared prettier file set via script/prettier, dropped the || true that hid prettier failures, and added a node-based Dockerfile stage so a markdown formatting violation fails docker build . (#69)
  • 2026-08-07: updated golangci-lint to v2.12.2 everywhere it is pinned (Makefile, Dockerfile), added the canonical .golangci.yml (default: all), and fixed all resulting lint findings across the codebase
  • 2026-07-07 Adopted scripts-to-rule-them-all: script/ entrypoints, Makefile shims, README Entrypoints section
  • 2026-07-03: aligned repo tooling, docs, and config with standardized policies (7d9a138, on chore/align-repo-policies, unmerged)
  • 2026-06-28: moved to standardized repo policies (#56, on main)
  • 2026-04-07: added 1.0 roadmap as README TODO section, removed old TODO.md (#54)
  • 2026-03-20: added Gitea Actions CI workflow (#53)
  • 2026-03-17: added REPO_POLICIES.md, renamed CLAUDE.md to AGENTS.md (#51); removed committed .index.mf (#52)
  • 2026-03-15: split Dockerfile with pre-built golangci-lint stage for faster CI (#45)
  • 2026-03-01: 1.0 quality polish: code review, tests, bug fixes, docs (#32)
  • 2026-02-20: deterministic file ordering in Builder.Build() (#28); removed committed vendor/modcache archives (#35)
  • 2026-02-08: added --seed flag for deterministic manifest UUID

Future Steps

  • Compliance (fold of TODO.md audit 2026-07-02; verify which items the in-flight branch already closes, then check off):
    • Add .editorconfig (canonical copy from sneak/prompts)
    • Make .gitignore cover secrets (.env, _.key, .pem), OS files (.DS_Store), and editor files (.swp, _~)
    • Make fmt-check/lint verify with gofumpt, not gofmt -l, so make check matches what make fmt writes
    • Add README "Getting Started" section with copy-pasteable install/usage block
    • Move FORMAT.md from repo root to docs/ and update the AGENTS.md reference
    • Pin Makefile-installed Go tools (protoc-gen-go@v1.28.1, golangci-lint@v2.12.2) by module hash, not mutable tag
    • Add explicit README "Rationale" heading (content exists under other names); name the author in the README Description first line
    • Reconcile root-level AGENTS.md with directory-hygiene policy (keep or relocate)
    • Add a make build target
    • Rewrite make hooks to use printf or a heredoc instead of non-portable echo '...\n...'
  • Answer the 14 owner design questions in the README 1.0 roadmap:
    • Format: simplify MFFileChecksum; store file mode; drop atime; specify path normalization rules; version byte after magic; length-prefix after magic
    • Signatures: hash covers compressed or uncompressed data; sign raw bytes vs hex canonical string; detached .mf.sig support; GPG subprocess vs pure-Go crypto
    • Implementation: deterministic manifests by default; consolidate duplicate scanner/checker implementations; export the manifest type; canonical Go module path for 1.0
  • Format and correctness:
    • Resolve proto go_package vs go.mod module path inconsistency
    • Specify and validate path invariants (UTF-8, forward-slash, relative, no .., no leading /)
    • Remove or deprecate atime; reserve mode field; add version byte (all pending design answers)
    • Write a standalone format specification document
  • Library:
    • Delete internal/scanner and internal/checker; consolidate on the mfer/ package versions (pending design answer)
    • Add decompression size limit via io.LimitReader in deserializeInner()
    • Fix errors.Is dead code in checker; make AddFile verify totalRead == size
    • Export manifest type or define a public interface (pending)
    • Replace GPG subprocess with pure-Go crypto (pending); add timeouts to remaining subprocess calls
  • CLI:
    • Kebab-case primary flag names; fix fetch URL construction with url.JoinPath; add http.Client timeout and retry with backoff to fetch; rate-limit Checker progress output; add --deterministic flag or default; wire top-level --version properly
  • Testing:
    • Fuzz NewManifestFromReader; end-to-end tests for freshen and fetch
  • Documentation:
    • Promote docs/FORMAT.md as primary spec reference; audit error messages; document the signature scheme fully
  • Release:
    • Finalize module path, bump version constant, SemVer --version output, tag v1.0.0