Skip to content

Carry the whole data posture in the security policy, not two thirds of it - #113

Merged
iderex merged 1 commit into
mainfrom
security-policy-carries-the-whole-posture
Aug 16, 2026
Merged

Carry the whole data posture in the security policy, not two thirds of it#113
iderex merged 1 commit into
mainfrom
security-policy-carries-the-whole-posture

Conversation

@iderex

@iderex iderex commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

The section ## What this project holds about you in SECURITY.md gains the two
statements it did not carry. Nothing else changes.

What the rule is

decisions/data-posture.md names four sections that repeat the posture, and then
says what each of them owes:

git show origin/main:decisions/data-posture.md | grep -n -A 4 'Four things have to survive'
63:that name on the published page. Four things have to survive an edit to any of
64-them: that a server fetching the file sends nothing about itself or its users,
65-that the hosting provider is named rather than implied, that the catalogue makes
66-no promise about the plugins it lists, and that sending data anywhere is
67-something an operator configures rather than a default.

What was missing, measured

The last two of those four. Both sentences wrap in every file that carries them,
so the count is taken with the line breaks removed rather than by git grep,
which reads a line at a time and would report a file that carries a sentence as
one that does not:

for ref in origin/main HEAD; do
  echo "== $ref =="
  for f in README.md NOTICE.md SECURITY.md docs/index.html; do
    u=$(git show $ref:$f | tr '\n' ' ' | tr -s ' ')
    printf "%-16s plugins=%s federation=%s\n" "$f" \
      "$(printf '%s' "$u" | grep -c 'separate projects')" \
      "$(printf '%s' "$u" | grep -c 'unless the operator')"
  done
done

== origin/main ==
README.md        plugins=1 federation=1
NOTICE.md        plugins=1 federation=1
SECURITY.md      plugins=0 federation=0
docs/index.html  plugins=1 federation=1
== HEAD ==
README.md        plugins=1 federation=1
NOTICE.md        plugins=1 federation=1
SECURITY.md      plugins=1 federation=1
docs/index.html  plugins=1 federation=1

Run 2026-08-16, origin/main at e376e7a and HEAD at aa24c16.

The two probes are the wording each document already uses, so they say a
sentence on the subject is present rather than that it says the right thing.
That part is a reading, and it is what the review is for.

The security policy's ## What is out of scope section is a different boundary
from the plugin one, which is why the new paragraph is not a restatement of it:
that section decides where a report goes, and this one says who answers for what
a plugin does with data.

The gate

go run . gate

gate examined 14 of 14 legs.
  build                           passed
  test                            passed
  format                          passed
  editorconfig                    passed
  tests-reach-nothing             passed
  no-hardcoded-names              passed
  site-fetches-nothing-outside    passed
  site-links-resolve              passed
  coverage                        passed
  site-declares-its-language      passed
  install-address-is-answered     passed
  focus-stands-off-every-surface  passed
  page-matches-the-token-file     passed
  pr-hygiene                      passed

Run at aa24c16 in a checkout holding nothing else. The harness disclosure the
run prints after that table is unchanged by this change: none of the three
requirements was asked for, and no leg depends on one.

This adds no check, so there is no guard to show refusing anything. What it adds
is prose, and what backs it is the measurement above rather than a test.

What this does not do

#49 is not met by this. That issue asks for the posture in the operator
instruction as well, and no such document is in the tree. It cannot be written
yet either, because it would have to print the address an operator pastes and
that address does not answer:

curl -sS -o /dev/null -w "%{http_code}\n" https://flowfin.dev/manifest.json
404

Run 2026-08-16. install-address-is-answered refuses a tracked file that prints
an install address before it has been read as answering, and the list that leg
reads is empty:

git grep -n 'var Answered' origin/main -- internal/address/address.go
origin/main:internal/address/address.go:54:var Answered = []string{}

Nobody else has read this change. The evidence above stands in place of a second
reader rather than beside one.

Named in #49.

A correction to this body

The paragraph above read "It does not close #49" until 2026-08-16, and the merge
closed #49 anyway: the keyword parser matches close #49 and does not read the
word in front of it. I reopened the issue. The state it is in now is the state
the paragraph describes, and the wording no longer carries the keyword.

…f it

decisions/data-posture.md names four sections that repeat the posture and says
four things have to survive an edit to any of them: that a server fetching the
file sends nothing about itself or its users, that the hosting provider is named
rather than implied, that the catalogue makes no promise about the plugins it
lists, and that sending data anywhere is something an operator configures rather
than a default.

The section in the security policy carried the first two and stopped. The other
three documents carry all four. This adds the missing two to it.

What that prevents is a reader who opens the security policy first, which is
where somebody goes once they are already worried, and gets a posture that ends
before the two sentences they can act on. It also keeps a wrong reading of the
policy's own scope section: that one decides where a report goes, which is a
different boundary from who answers for what a plugin does with data, so the new
paragraph states the difference rather than restating the section above it.

Named in #49, which this does not close. That issue also asks for the posture in
the operator instruction, and no such document exists yet.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit 65773d0 into main Aug 16, 2026
36 checks passed
@iderex
iderex deleted the security-policy-carries-the-whole-posture branch August 16, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Put the data posture in the documents an operator actually reads

1 participant