Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

[tool.bumpversion]
current_version = "0.10.1"
current_version = "0.10.2"
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)((?P<pre_l>a|b|rc)(?P<pre_n>\\d+))?"
serialize = [
"{major}.{minor}.{patch}{pre_l}{pre_n}",
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/security_vulnerability.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ body:
attributes:
label: Zenzic version
description: Output of `zenzic --version`
placeholder: "0.10.1"
placeholder: "0.10.2"
validations:
required: true

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#
# repos:
# - repo: https://github.com/PythonWoods/zenzic
# rev: v0.10.1
# rev: v0.10.2
# hooks:
# - id: zenzic-verify # quality gate — corrisponde a `just verify` lato zenzic
# - id: zenzic-guard # fast staged-file credential scan
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@

---

## [0.10.2] - 2026-06-07

### Fixed

- **Core Engine (AST Parser):** Fixed a blindspot in the AST parser where image nodes (`![alt][id]`) were not being harvested into the `used_ids` set, causing false-positive Z302 (Orphan Definition) warnings.
- **Core Engine (Path Resolver):** The local path resolver now strips URL fragments (`#...`) and query strings (`?...`) before interrogating the filesystem. This prevents false-positive Z101/Z104 errors when using GFM suffixes on local file links (e.g., `../assets/img.png#gh-light-mode-only`).

---

## [0.10.1] - 2026-06-07

### Changed
Expand All @@ -40,5 +49,5 @@
## Historical Releases

- v0.9.x archive: [changelogs/v0.9.md](./changelogs/v0.9.md)
- v0.8.x archive: [changelogs/v0.8.md](./changelogs/v0.8.md)

Check notice on line 52 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.10)

Z106

CHANGELOG.md:52: './changelogs/v0.8.md' is part of a circular link cycle

Check notice on line 52 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.14)

Z106

CHANGELOG.md:52: './changelogs/v0.8.md' is part of a circular link cycle
- v0.1.x–v0.7.x archive index: [changelogs/README.md](./changelogs/README.md)

Check notice on line 53 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.10)

Z106

CHANGELOG.md:53: './changelogs/README.md' is part of a circular link cycle

Check notice on line 53 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.14)

Z106

CHANGELOG.md:53: './changelogs/README.md' is part of a circular link cycle
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract: >-
performs deterministic static analysis using a two-pass reference
pipeline and a RE2-backed credential scanner, with zero subprocess
calls and full SARIF 2.1.0 support for CI/CD integration.
version: 0.10.1
version: 0.10.2
date-released: 2026-06-07
url: "https://zenzic.dev"
repository-code: "https://github.com/PythonWoods/zenzic"
Expand Down
8 changes: 4 additions & 4 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
<!-- SPDX-License-Identifier: Apache-2.0 -->
# Release Procedure — Zenzic Core

> **[MAINTAINER SOP]** *This document contains the Standard Operating Procedure for Core Maintainers to cut and publish a new release. If you are an end-user looking for new features, please see the [CHANGELOG](./CHANGELOG.md).*

Check notice on line 5 in RELEASE.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.10)

Z106

RELEASE.md:5: './CHANGELOG.md' is part of a circular link cycle

Check notice on line 5 in RELEASE.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.14)

Z106

RELEASE.md:5: './CHANGELOG.md' is part of a circular link cycle

## Release Metadata

| Field | Value |
| :------- | :--------- |
| Version | v0.10.1 |
| Version | v0.10.2 |
| Codename | Magnetite |
| Date | 2026-06-07 |
| Status | Stable |
Expand All @@ -21,7 +21,7 @@
- [ ] `zenzic lab all` — all 20 scenarios exit with expected code
- [ ] `zenzic score --stamp` committed — badge in README.md and README.it.md reflects current score
- [ ] `zenzic check all .` — zero findings in the repo root
- [ ] `pyproject.toml` version matches the tag (`0.10.1`)
- [ ] `pyproject.toml` version matches the tag (`0.10.2`)
- [ ] `CITATION.cff` version and date updated
- [ ] `CHANGELOG.md` — `[Unreleased]` section moved to the new version heading
- [ ] Update SECURITY.md support table (Add new release, demote previous to Critical/EOL).
Expand Down Expand Up @@ -54,13 +54,13 @@
git pull origin main

# 3. Tag the main branch and push
git tag v0.10.1
git tag v0.10.2
git push origin main --tags
```

- [ ] Create GitHub Release from the tag, using the `## v0.10.1` CHANGELOG section as the release body.
- [ ] Create GitHub Release from the tag, using the `## v0.10.2` CHANGELOG section as the release body.

## Changelog Reference

For a detailed list of changes, see [CHANGELOG.md](./CHANGELOG.md).

Check notice on line 65 in RELEASE.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.10)

Z106

RELEASE.md:65: './CHANGELOG.md' is part of a circular link cycle

Check notice on line 65 in RELEASE.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.14)

Z106

RELEASE.md:65: './CHANGELOG.md' is part of a circular link cycle
For full history, see [Historical Archives](./changelogs/README.md).

Check notice on line 66 in RELEASE.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.10)

Z106

RELEASE.md:66: './changelogs/README.md' is part of a circular link cycle

Check notice on line 66 in RELEASE.md

View workflow job for this annotation

GitHub Actions / Audit (ubuntu-latest, 3.14)

Z106

RELEASE.md:66: './changelogs/README.md' is part of a circular link cycle
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ build-backend = "hatchling.build"

[project]
name = "zenzic"
version = "0.10.1"
version = "0.10.2"
description = "Engineering-grade, engine-agnostic static analyzer and credential scanner for Markdown documentation"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion src/zenzic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# SPDX-License-Identifier: Apache-2.0
"""Zenzic — engine-agnostic static analyzer and credential scanner for Markdown documentation."""

__version__ = "0.10.1"
__version__ = "0.10.2"
__version_name__ = "Basalt" # Release codename stored separately from the package version.
2 changes: 1 addition & 1 deletion src/zenzic/cli/_standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -1270,7 +1270,7 @@ def _scaffold_plugin(repo_root: Path, plugin_name: str, force: bool) -> None:
description = "Custom Zenzic plugin rule package"
readme = "README.md"
requires-python = ">=3.11"
dependencies = ["zenzic>=0.10.1"]
dependencies = ["zenzic>=0.10.2"]

[project.entry-points."zenzic.rules"]
{project_slug} = "{module_name}.rules:{class_name}"
Expand Down
1 change: 1 addition & 0 deletions src/zenzic/cli/_target_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def _resolve_target(repo_root: Path, config: ZenzicConfig, raw: str) -> Path:
*repo_root/docs_dir*. Files must have the ``.md`` extension.
Exits with code 1 if nothing is found or the extension is wrong.
"""
raw = raw.split("#")[0].split("?")[0]
p = Path(raw)
candidates: list[Path] = (
[p] if p.is_absolute() else [repo_root / p, repo_root / config.docs_dir / p]
Expand Down
8 changes: 4 additions & 4 deletions src/zenzic/core/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1271,8 +1271,6 @@ def cross_check(self) -> list[ReferenceFinding]:
clean = _INLINE_CODE_RE.sub(lambda m: " " * len(m.group()), line)

for m in _RE_REF_LINK.finditer(clean):
if m.start() > 0 and clean[m.start() - 1] == "!":
continue
text = m.group(2)
ref_id = m.group(3) if m.group(3) else text # collapsed ref
url = self.ref_map.resolve(ref_id)
Expand All @@ -1292,10 +1290,12 @@ def cross_check(self) -> list[ReferenceFinding]:

# Shortcut reference links: [text] (CommonMark §4.7)
for m in _RE_REF_SHORTCUT.finditer(clean):
if m.start() > 0 and clean[m.start() - 1] in "!]":
if m.start() > 0 and clean[m.start() - 1] == "]":
continue
tail = clean[m.end() : m.end() + 1]
if tail in "[:(":
if tail in "[(":
continue
if tail == ":" and clean[: m.start()].strip() == "":
continue
ref_id = m.group(1)
self.ref_map.resolve(ref_id) # mark as used if defined
Expand Down
9 changes: 9 additions & 0 deletions tests/test_references.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,15 @@ def test_collapsed_reference_link(self, tmp_path: Path) -> None:
errors = [f for f in findings if not f.is_warning]
assert errors == []

def test_image_reference_link_resolved(self, tmp_path: Path) -> None:
"""![alt][ref] is an image reference — should resolve to [ref]: url."""
content = "[imgref]: https://example.com/img.png\n\nSee ![alt][imgref].\n"
scanner = self._make_scanner(tmp_path, content)
findings = scanner.cross_check()
errors = [f for f in findings if not f.is_warning]
assert errors == []
assert "imgref" in scanner.ref_map.used_ids

def test_multiple_dangling_refs(self, tmp_path: Path) -> None:
content = (
"[valid]: https://example.com\n\nSee [A][ghost1] and [B][ghost2] and [C][valid].\n"
Expand Down
35 changes: 35 additions & 0 deletions tests/test_target_resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev>
# SPDX-License-Identifier: Apache-2.0

from pathlib import Path

from zenzic.cli._target_resolver import _resolve_target
from zenzic.models.config import ZenzicConfig


def test_resolve_target_strips_fragments_and_queries(tmp_path: Path) -> None:
"""_resolve_target must strip #fragments and ?queries before Path.exists() checks."""
repo_root = tmp_path / "repo"
docs_dir = repo_root / "docs"
docs_dir.mkdir(parents=True)

# Create a dummy md file
target_file = docs_dir / "page.md"
target_file.touch()

config = ZenzicConfig(docs_dir=Path("docs"))

# Test with fragment
raw_fragment = "docs/page.md#gh-light-mode-only"
resolved_fragment = _resolve_target(repo_root, config, raw_fragment)
assert resolved_fragment == target_file.resolve()

# Test with query string
raw_query = "docs/page.md?version=1.0"
resolved_query = _resolve_target(repo_root, config, raw_query)
assert resolved_query == target_file.resolve()

# Test with both
raw_both = "docs/page.md?version=1.0#gh-light-mode-only"
resolved_both = _resolve_target(repo_root, config, raw_both)
assert resolved_both == target_file.resolve()
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading