Skip to content

SECURITY.md: supported-versions table, sanitize_label escaping claim, and followlinks wording no longer match the code #2007

Description

@Job28703

While reading SECURITY.md against the implementation, three statements did not hold. None of these is a vulnerability report — the two that touch real behaviour have been sent through private vulnerability reporting separately. These three are documentation only.

1. Supported Versions table is two majors behind

| Version | Supported |
|---------|-----------|
| 0.3.x   | Yes       |
| < 0.3   | No        |

Current release is 0.9.19. Read literally, the table says no currently-published version is supported. Suggested fix: state the supported line as the current minor, or replace the table with "latest release only".

2. sanitize_label() is described as HTML-escaping; it does not escape

SECURITY.md says:

security.sanitize_label() strips control characters, caps at 256 chars, and HTML-escapes all node labels and edge titles before pyvis embeds them.

Three parts of that sentence don't match the code:

  • sanitize_label() does not escape. Its own docstring says so, and tests/test_security.py asserts it explicitly in test_sanitize_label_passthrough_html_chars.
  • pyvis is not a dependency of this project and appears nowhere in the source tree — only in this sentence.
  • The escaping is real, but it lives in graphify/exporters/html.py (_html.escape on the interpolated fields, plus the JS-side esc() helper).

So the defense is genuinely there — the documentation just credits the wrong function and a library you don't use. Worth fixing precisely because a reader auditing this file will grep for sanitize_label, see it doesn't escape, and reasonably conclude there's an XSS hole when there isn't one.

Also minor: SECURITY.md names "edge titles" as covered by the 256-char cap and control-char strip, but edge title in exporters/html.py is HTML-escaped without going through sanitize_label, so it has neither.

3. followlinks=False is described as literal and repo-wide

Symlink traversal | os.walk(..., followlinks=False) is explicit throughout detect.py.

In detect.py:1261 it is followlinks=follow_symlinks — a parameter that defaults to False. The posture is correct; the wording ("explicit throughout") is stale. Separately extract.py:5104 passes followlinks=True, guarded by a _resolves_under_root containment check — outside detect.py and therefore outside the literal claim, but a reader takes the sentence as a global property.

Why file this at all

SECURITY.md here is unusually detailed and is clearly meant to be audited — that's why the mismatches are worth correcting rather than shrugging at. A threat table that names a specific function is a promise that the function is the thing doing the work.

How this was found

Static read of SECURITY.md against graphify/security.py, graphify/exporters/html.py, graphify/detect.py, graphify/extract.py, and tests/test_security.py. Test suite was not executed (no uv in the audit environment); the security.py checks were exercised by importing the module directly, since it is stdlib-only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions