Skip to content

Security: shiro-0x/hersona

Security

SECURITY.md

Security

hersona is a small, low-dependency PyPI library (YAML attribute templates + a CLI). This document is deliberately modest in scope — it describes what protections actually exist, not aspirational ones. Written in response to an external review that (correctly) noted the project had no documented threat model. Detailed maintainer review notes are kept in the private hersona-internal repository.

Threat model — what this project protects against

In scope:

  • A malformed or oversized hersona update download corrupting the local data cache. hersona.core.update caps the downloaded archive at 64 MB, rejects path-traversal / non-regular archive members, and installs atomically (backup-then-swap; a failed install rolls back rather than leaving a half-written cache).
  • Transit-level tampering or corruption of a hersona update download (a compromised network path, a bad CDN edge, a truncated transfer). Since v1.7.x, hersona update fetches a SHA-256 manifest (checksums.json, generated by scripts/gen_checksums.py) from a second, independent GitHub delivery path (raw.githubusercontent.com, distinct from the codeload.github.com archive endpoint) at the same ref, and verifies every extracted file against it before installing. A mismatch aborts the update with no partial install. Skippable with --no-verify; if the manifest simply isn't available for a given ref (e.g. an old tag predating this feature), verification is skipped with a warning (fail-open) rather than blocking the update.
  • Untrusted attribute YAML content. Attributes are data (YAML validated against schema/attribute.schema.json), not executable code — loading one does not run arbitrary Python. scripts/validate.py and CI schema validation catch structurally invalid files before merge.

Out of scope (please don't rely on this document for these):

  • A compromised shiro-0x/hersona GitHub account or repository. The archive and its checksum manifest both originate from the same commit tree; an attacker who can rewrite the repository (or push malicious commits with valid credentials) can rewrite both consistently. This is not code signing, not Sigstore/SLSA build provenance, and does not protect against a supply-chain compromise at the source. It only detects tampering/corruption introduced after GitHub served the content — i.e., in transit or at a compromised intermediate cache/CDN edge, not a compromise of GitHub or the account itself.
  • A malicious PyPI release. hersona's PyPI publish workflow uses Trusted Publishing (OIDC, no long-lived API token in CI) — this removes the risk of a leaked PyPI token being used to publish a rogue release, but it does not itself attest that the published artifact matches a specific source commit. Build provenance attestation (actions/attest-build-provenance) is planned to close that gap; check .github/workflows/publish.yml for current status.
  • Prompt injection via attribute content at inference time. Attribute YAMLs are authored to be injected into an LLM system prompt. hersona does not sandbox or filter what an LLM does with that content at response time — that's the responsibility of whatever agent framework or application embeds hersona.
  • Model behavior, hallucination, or persona-driven misuse. See DISCLAIMER.md.

Reporting a vulnerability

Please open a GitHub issue for non-sensitive reports. For anything you'd rather not disclose publicly before a fix is available, use GitHub's private vulnerability reporting (repository Security tab → Report a vulnerability) if enabled, or reach the maintainer through the contact listed on their GitHub profile. There is no dedicated security email or bug bounty at this time — this is an individually maintained open-source project.

Verifying a hersona update download yourself

hersona update --ref v1.7.0   # pin to a tag rather than a moving branch

Pinning --ref to a tag (rather than the default main branch) avoids a theoretical race where the archive and checksums.json are fetched a moment apart while the branch is being pushed to. Tags are immutable, so both requests resolve to the same content regardless of timing.

To inspect the manifest yourself: it's a plain JSON file at checksums.json in this repository, one SHA-256 hash per file under attributes/ and schema/. Regenerate and diff it with python scripts/gen_checksums.py --check.

AI disclosure and companion-chatbot rules

persona_lock (on by default) tells the model to refuse tone, dialect and persona swaps and to treat the persona definition as outranking in-chat instructions. That is what makes a persona hold — but the same instruction can push a model toward not answering "are you a human or an AI?" straight, and several 2026 regimes require exactly that answer:

  • California SB 243 (companion chatbots, effective 2026-01-01): clear and conspicuous AI disclosure where a reasonable person could be misled; a disclosure that the service may not be suitable for some minors; for users the operator knows are minors, a reminder at least every three hours that they are talking to an AI; and published protocols for preventing self-harm content with crisis referrals.
  • Chatbot bills were moving in 27 US states as of April 2026 (Oregon, Washington and Tennessee have enacted their own versions).
  • EU AI Act transparency obligations apply from 2026-08-02 for anyone serving EU customers.
  • The FTC's deception analysis looks at the whole presentation: a human name and a human-sounding persona can themselves undercut a disclosure.

hersona ships an opt-in --disclosure flag (hersona.core.disclosure) that adds a directive stating the persona must answer honestly when asked whether it is an AI, and that this overrides persona lock. It also tells the model not to claim human experiences, a body, a real identity or credentials as fact, and to drop the persona styling and point to real human help if the user appears to be in crisis.

What this does not do. It is a prompt directive, not a control. The model may ignore it. And most of what the rules above actually require cannot be expressed in a prompt at all:

Requirement Who has to implement it
Conspicuous in-UI "you are talking to an AI" disclosure You (the operator)
Three-hourly break reminders for known minors You
Age assurance / knowing a user is a minor You
Self-harm prevention protocol, published, with crisis referrals You
Auditable records of the above You
A prompt directive asking the model to answer honestly hersona --disclosure

Treat --disclosure as one layer inside your own compliance design, never as the design. If you operate a companion-style product, get your own legal review; this section is a pointer, not advice.

There aren't any published security advisories