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
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ labels: ["bug", "needs-evidence"]
body:
- type: markdown
attributes:
value: "**Redact first** (`--redact`). Vulnerabilities go to `SECURITY.md`, never here."
value: "**Redact first**, by hand: 0.1 has no automatic redaction. Vulnerabilities go to `SECURITY.md`, never here."
- type: input
id: version
attributes: {label: ISEDRAF version}
Expand Down Expand Up @@ -42,5 +42,5 @@ body:
id: sanitized
attributes:
label: Sanitized evidence
description: "Redacted output (`--redact`). Never paste an unredacted identity or evidence bundle."
description: "Output, redacted by hand. Never paste an unredacted identity or evidence bundle."
validations: {required: true}
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/collection_failure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,5 @@ body:
id: sanitized
attributes:
label: Sanitized evidence
description: "Redacted output (`--redact`). Never paste an unredacted identity or evidence bundle."
description: "Output, redacted by hand. Never paste an unredacted identity or evidence bundle."
validations: {required: true}
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ contact_links:
about: "Report privately to contact@itcms.gr. A false PASS, unsafe privileged execution, evidence corruption or incorrect delta logic is security-sensitive."
- name: Support, supported platforms and how to redact
url: https://github.com/itcmsgr/isedraf/blob/main/SUPPORT.md
about: "What is safe to share, how to use --redact, and how collection failure differs from product failure."
about: "What is safe to share, how to redact by hand, and how collection failure differs from product failure."
- name: Is it in scope? Check the host evidence boundary
url: https://github.com/itcmsgr/isedraf/blob/main/docs/roadmap/ROADMAP.md
about: "Firewalls, AV/EDR, IDS/IPS, WAF, SIEM, cloud controls, CVE correlation and whole-filesystem FIM are deliberately out of scope."
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/false_negative.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ body:
reported as passing, that is a security-sensitive defect — consider reporting it privately via
`SECURITY.md` instead.

**Redact first.** Use `--redact`.
**Redact first.** ISEDRAF 0.1 has no automatic redaction: remove names, host names and key material by hand.
- type: input
id: version
attributes: {label: ISEDRAF version}
Expand Down Expand Up @@ -57,5 +57,5 @@ body:
id: sanitized
attributes:
label: Sanitized evidence
description: "Redacted output (`--redact`). Never paste an unredacted identity or evidence bundle."
description: "Output, redacted by hand. Never paste an unredacted identity or evidence bundle."
validations: {required: true}
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/false_positive.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ body:
- type: markdown
attributes:
value: |
**Redact first.** Use `--redact` and review what remains. Never paste an unredacted identity or
**Redact first**, by hand (0.1 has no automatic redaction), and review what remains. Never paste an unredacted identity or
evidence bundle. See `SUPPORT.md`.
- type: input
id: version
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/platform_compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ body:
id: sanitized
attributes:
label: Sanitized evidence
description: "Redacted output (`--redact`). Never paste an unredacted identity or evidence bundle."
description: "Output, redacted by hand. Never paste an unredacted identity or evidence bundle."
validations: {required: true}
19 changes: 19 additions & 0 deletions .github/workflows/governance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,22 @@ jobs:
- run: make check-vectors check-vectors-negative check-tests
# Byte determinism WITHIN the lane, across every interpreter present on the runner.
- run: bash scripts/vectors/crossversion.sh

dco:
# D-91, OpenSSF LE-01.01: every commit a pull request adds carries its author's DCO
# sign-off. Pull requests only: history before the policy is in no pull request range.
name: DCO
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
fetch-depth: 0
persist-credentials: false
- env:
DCO_BASE: ${{ github.event.pull_request.base.sha }}
DCO_HEAD: ${{ github.event.pull_request.head.sha }}
run: make check-dco-pr
8 changes: 4 additions & 4 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
# job would fail permanently. Until the public repository exists this has NEVER RUN,
# and no score may be displayed. A score is a measurement, not a decoration.
#
# `results.sarif` is uploaded to code scanning so findings are reviewable in place;
# it is NOT published to the public Scorecard API (`publish_results: false`), because
# publishing is an outward-facing act that is the owner's to authorise.
# `results.sarif` is uploaded to code scanning so findings are reviewable in place, and
# the results are published to the public Scorecard API (`publish_results: true`), which
# the owner authorised on 2026-09-29 so the README badge shows a real measurement.
name: Scorecard

on:
Expand Down Expand Up @@ -43,7 +43,7 @@ jobs:
with:
results_file: results.sarif
results_format: sarif
publish_results: false
publish_results: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: scorecard-results
Expand Down
44 changes: 41 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,51 @@ Status: IMPLEMENTED
Implements: D-88, §40

All notable changes to ISEDRAF are recorded here. Format follows Keep a Changelog; versioning follows
Semantic Versioning once a release exists. `VERSION` is the single source of the current version, and
`make check` verifies that `VERSION`, this file and the release date agree.
Semantic Versioning once a release exists. `VERSION` is the single source of the current version.

## [Unreleased]

## [0.1.0] - 2026-09-28

The first release of ISEDRAF.

### Added
- Nothing released. The project is in the planning phase; see `docs/CURRENT_STATE.md`.
- Unprivileged Linux host evidence collection with one command, `isedraf audit`, across
ten domains: host and platform inventory, local accounts and groups, NSS configuration,
hostname, sudo, SSH server configuration, PAM, login policy, mounts and SSH authorized
keys.
- A user-mode evidence store at `$XDG_STATE_HOME/isedraf` or `~/.local/state/isedraf`,
private to the user (mode 0700). A store on a network or unknown filesystem is refused
before anything is written.
- One committed evidence run per audit: canonical JSON, hash-bound, recorded in a
hash-chained ledger and verified after every commit.
- `isedraf report`: a report of the latest committed run - Markdown, JSON, or a static HTML
page - rendered from committed evidence only.
- DEB and RPM packages for the system Python (3.6 or later), with no compiled code and no
third-party runtime module.

### Upgrading from a pre-release
- The package upgrades 0.1.0-alpha1 in place and keeps the evidence in your store.
- Running `sudo isedraf` is refused, as before, and no longer leaves Python bytecode in
the installed package directory.
- Pre-release cleanup: If you previously ran 0.1.0-alpha1 with sudo, Python may have left
unowned bytecode under `/usr/lib/isedraf`. After uninstalling ISEDRAF and confirming the
package is no longer installed, the remaining ISEDRAF bytecode/directory may be removed
manually.

### Limitations
- This release does not run with elevated privilege. Facts that need root, such as
`/etc/shadow` and sudoers, are reported NOT_TESTED: not observed, never passed.
- PARTIAL means some evidence could not be collected. It does not mean the host is secure
or insecure.
- There are no pass/fail judgements and no comparison between runs. A run with no
observed problem does not show that a host is uncompromised.
- Evidence is protected by filesystem permissions. It is not protected against a local
administrator.

### Comparability
- First release: there is no earlier normalized state to compare with, and no baseline to
rebind.

<!-- Entry template:
## [0.1.0] - YYYY-MM-DD
Expand Down
153 changes: 79 additions & 74 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,116 +10,121 @@ Implements: D-63, D-69, D-90, D-91, D-93, §13
ISEDRAF is a trust tool. Its value is that what it reports is true and that its limits are stated. Most
of the rules below exist to protect that property, not to protect a coding style.

**The project is pre-release and not accepting external contributions yet.** This document is the contract
that will apply when it does.
## What contributions are welcome

## Before you write code
Pull requests are welcome for bug fixes, regression tests, parser fixtures from real systems (redacted),
platform compatibility evidence and documentation corrections. For a new feature or a change of
behaviour, open an issue first: scope is deliberately narrow (see below), and a feature outside it will
not be merged however well it is written.

Read, in order: `CLAUDE.md` (or `docs/development/LLM_PROTOCOL.md` if you are using any AI assistant) →
`docs/architecture/INTERNAL_RECORDS.md` → the relevant frozen architecture documents →
`docs/CURRENT_STATE.md` → `docs/REPOSITORY_MAP.md` → `docs/development/requirements-trace.md` *(PLANNED — not yet created)*.
Security vulnerabilities are **not** reported through issues or pull requests. See
[`SECURITY.md`](SECURITY.md).

Architecture is not inferred from the code. The code may be incomplete; the frozen requirements govern.
## Development prerequisites

## The invariants

**Host evidence boundary.** ISEDRAF assesses state directly observable on the local operating system.
Firewalls, AV, EDR, IDS/IPS, WAF, SIEM, cloud and network controls, CVE matching and whole-filesystem
integrity monitoring are permanently out of scope. The absence of a locally detectable agent is never the
absence of the control.
- Linux, `git`, `make`, `bash` and GNU coreutils.
- Python 3. The product runs on Python **3.6 or later**, including vendor-maintained 3.6 such as
`platform-python` on Enterprise Linux 8, so product code must stay within the 3.6 language and
standard library. `make check` enforces that floor.
- Optional, for packaging and some checks: `dpkg-deb`, `rpmbuild`, `rsync`, `shellcheck`.

**Read-only audit.** ISEDRAF never modifies host state. Collectors collect; the engine interprets.
Nothing in the runtime may change PAM, SSH, audit, sysctl, users, services, MAC or mounts.
There are no third-party Python packages to install, for the product or for development. See
[`docs/DEPENDENCIES.md`](docs/DEPENDENCIES.md).

**Clean room.** Never copy code, rules, tests, prose, tables, mappings or remediation from NFTBan,
ComplianceAsCode, OpenSCAP, Lynis, osquery, Wazuh, AIDE, CIS, ISO, PCI DSS or any commercial product.
Reference identifiers and authoritative sources; write ISEDRAF prose independently.
## Before you open a pull request

**Runtime dependencies.** Bash where a shell sequence is genuinely required, plus Python ≥ 3.9 **standard
library only**. No Go, no compiled components, no plugins, no third-party runtime modules. Development and
CI tools live in `requirements-dev.txt` and are never imported by runtime code. No network egress and no
embedded database — both are enforced by an import allowlist, not by convention.

**Traceability.** Every function that implements a requirement cites it: `Implements: SNAP-012`. The
generated `docs/development/requirements-trace.md` *(PLANNED — not yet created)* must stay fresh.
```sh
make check
```

**Negative tests.** Every parser needs normal, malformed, missing-data and permission-failure fixtures.
Every bug gets a regression test *before* the fix.
`make check` is the single local entry point, and CI runs the same targets. There is no warning tier: a
check either passes or fails. Install the repository hooks once, so `make check` runs before each
commit:

**Canonical serialization.** Only canonicalized state is hashed and diffed. Observations — last login,
PID, uptime, "days remaining" — are displayed but never cause a change.
```sh
hooks="$(git rev-parse --git-path hooks)"
cp git-hooks/* "$hooks"/ && chmod 0755 "$hooks"/*
```

**Snapshot immutability.** A completed snapshot is never modified and never contains findings. Findings
live in regenerable evaluations.
Never bypass them with `git commit --no-verify`.

**Collection vs evaluation.** Collection status (`COLLECTED`, `PARTIAL`, `NOT_TESTED`, `ERROR`) and
evaluation result (`PASS`, `FAIL`, `PARTIAL`, `MISMATCH`, `NOT_APPLICABLE`, `MANUAL_REVIEW`,
`NOT_EVALUATED`) are separate fields with separate counters. Never conflate them.
**Tests.** Every change comes with tests appropriate to it. Every bug gets a regression test that fails
before the fix. Every parser needs normal, malformed, missing-data and permission-failure fixtures.

**`NOT_TESTED` semantics.** `NOT_TESTED` is not `PASS`. It never becomes `REMOVED` and never becomes an
improvement. Where either side was not collected, the result is `NOT_COMPARABLE` with a reason.
**Required CI.** A pull request can be merged only when these checks pass on it: `make check`,
`make check-falsifiable`, `make check-gate-coverage`, the W1-A vector lanes, CodeQL and the DCO
check. The branch is protected: no force-push, no deletion, linear history.

**No scope expansion without an amendment.** Interesting is not in scope. Out-of-scope ideas go to
`docs/IMPLEMENTATION_QUESTIONS.md` as `FUTURE`. Architecture changes happen only through an owner-written
`docs/architecture/INTERNAL_RECORDS.md` entry and a regenerated manifest.
## Signing off your commits (DCO)

## Local validation
Every commit must carry a Developer Certificate of Origin sign-off. By adding it you certify the
statements at <https://developercertificate.org/>, in particular that you have the right to submit the
change under the project's licence (MPL-2.0).

```
make check
```sh
git commit -s # adds: Signed-off-by: Your Name <you@example.com>
```

This is the authoritative pre-commit entry point, and the `pre-commit` hook runs it. CI runs the same
targets. There is no warning tier: a gate either passes or fails.
The name and email must match the commit's author. To sign off commits you already made on your branch:

Install the repository hooks once:

```
cp git-hooks/* .git/hooks/ && chmod 0755 .git/hooks/*
```sh
git commit --amend -s --no-edit # the last commit
git rebase --signoff origin/main # every commit on the branch
```

Never use `git commit --no-verify`, never change `core.hooksPath`, never edit `.git/hooks/` directly.
The DCO check fails a pull request that contains any commit without a matching sign-off, and says which
commit. A sign-off is a personal legal statement: nobody may add one on another person's behalf, and an
AI tool cannot make one.

## Commits
## Disclosing AI assistance

Sign off every commit (DCO):

```
Signed-off-by: Your Name <you@example.com>
```

Disclose AI assistance on every commit — the `commit-msg` hook enforces it:
Separately from the DCO, every commit states whether AI tools assisted, with an `Assisted-by:` trailer.
The `commit-msg` hook enforces it:

```
Assisted-by: Claude (implementation via Claude Code)
Assisted-by: none
```

Keep any `Co-Authored-By:` trailers your tooling adds. Never remove or disable them.
`Assisted-by:` is disclosure. It does not replace your sign-off, and AI tools are never credited as
authors: a `Co-Authored-By:` trailer naming an AI tool is rejected. See
[`AI_ASSISTED_DEVELOPMENT.md`](AI_ASSISTED_DEVELOPMENT.md).

## Pull requests
## The invariants

**Host evidence boundary.** ISEDRAF assesses state directly observable on the local operating system.
Firewalls, AV, EDR, IDS/IPS, WAF, SIEM, cloud and network controls, CVE matching and whole-filesystem
integrity monitoring are out of scope. The absence of a locally detectable agent is never the absence
of the control.

**Read-only.** ISEDRAF never modifies host state. Collectors collect; the engine interprets.

The template asks for requirement IDs, the domain touched, behaviour changed, collector/parser/schema
changes, negative tests added, corpus fixtures, privilege and capability impact, data sensitivity impact,
documentation updated, and the AI tools and roles used.
**Runtime dependencies.** Python standard library only, with a checked import allowlist. No compiled
components, no plugins, no network access, no embedded database.

It also asks the question that matters most for a delta engine:
**Clean room.** Never copy code, rules, tests, prose, tables, mappings or remediation from another
product or standard. Reference identifiers and authoritative sources; write ISEDRAF text independently.

> **Does this change alter normalized state for an unchanged host?**
> If yes, describe the comparability and `baseline rebind` handling.
**`NOT_TESTED` is not a pass.** Evidence that was not collected never becomes a good result, a removal
or an improvement.

If the answer is yes and unhandled, the change silently invalidates every existing baseline.
**Committed evidence is immutable.** A committed snapshot is never modified.

## When you get stuck
**Traceability.** Code that implements a requirement cites it, for example `Implements: SNAP-012`.

Do not silently reinterpret a requirement. Record it in `docs/IMPLEMENTATION_QUESTIONS.md` with the
requirement IDs, the affected code, the safest behaviour you implemented and your proposed clarification;
mark the requirement `BLOCKED` in the trace; continue other in-scope work.
## Security-sensitive changes

Changes to evidence collection, hashing and verification, the evidence store, the launcher, packaging
or the CI workflows need extra care. Say so in the pull request, explain the effect on what ISEDRAF
reports, and add a test that fails if the protection is removed. A change that weakens a check to make
work pass is not accepted.

## Pull requests

**Never weaken a requirement, a test or a gate to make work pass.**
The template asks what the change does, which tests cover it, whether it changes what ISEDRAF reports
for an unchanged host, and which AI tools were used. Keep one concern per pull request.

## Documentation

Documentation is part of product correctness and is reviewed like code. `/docs` is canonical; the README is
the front door. Follow `docs/development/DOCUMENTATION_POLICY.md` and `docs/STYLE_GUIDE.md`. Do not
position ISEDRAF against another project, and do not describe a planned feature in the present tense.
Documentation is reviewed like code. Describe only what exists: a planned feature is never written in
the present tense. Follow [`docs/STYLE_GUIDE.md`](docs/STYLE_GUIDE.md).
Loading
Loading