{
  "name": "Rightshold Safety Ratings — methodology v2 (npm, PyPI, skills, MCP servers)",
  "program": "ratings-at-scale",
  "check_version": "rs-safety-checks-2.0",
  "disclaimer": "Automated checks of public signals as of the date shown. Not a security audit, certification or endorsement, and not a statement about the people who maintain the project. Signals can be incomplete or out of date; each check shows its evidence and source so anyone can verify it.",
  "github_program": {
    "check_version": "rs-safety-checks-1.0",
    "methodology": "https://www.rightshold.com/v1/safety-ratings/methodology",
    "note": "The GitHub repository program keeps its 1.0 rulebook verbatim; v2 adds the kinds below without changing any GitHub rule."
  },
  "passive_only_boundary": "Third-party facts come only from public registry metadata, public documentation, TLS facts and response headers. No Rightshold code path sends an MCP initialize, tools-list or authentication attempt to a third-party server; active probing exists only for endpoints the owner registered under endpoint health.",
  "mcp_demand_evidence": "The NSA Artificial Intelligence Security Center guidance U/OO/6030316-26 (May 2026) lists MCP servers among the systems that need assessing, under the controls of access enforcement, vulnerability scanning and monitoring.",
  "data_sources": [
    "registry.npmjs.org (public package metadata)",
    "pypi.org JSON API (public project metadata)",
    "api.osv.dev (open vulnerability database)",
    "registry.modelcontextprotocol.io (public MCP registry metadata) and the servers' public documentation",
    "Rightshold resource-scan receipts (skills: the artifact's latest sealed receipt, scored — never a second scan)"
  ],
  "kinds": [
    {
      "kind": "npm",
      "check_version": "rs-safety-checks-2.0",
      "disclaimer": "Automated checks of public signals as of the date shown. Not a security audit, certification or endorsement, and not a statement about the people who maintain the project. Signals can be incomplete or out of date; each check shows its evidence and source so anyone can verify it.",
      "ai_in_scoring": false,
      "deterministic": true,
      "weights": {
        "license": 10,
        "known_vulnerabilities": 15,
        "install_scripts": 12,
        "dependency_pinning": 9,
        "release_provenance": 8,
        "maintainer_activity": 10,
        "registry_state": 8,
        "metadata_completeness": 8,
        "maintainer_breadth": 12
      },
      "checks": [
        {
          "id": "license",
          "title": "License recorded (OSI-approved)",
          "max_points": 10,
          "rule": "OSI-approved SPDX id in the registry metadata for the latest version: full points. A declared license not matched to an OSI id: half. None recorded: zero."
        },
        {
          "id": "known_vulnerabilities",
          "title": "Known advisories for the latest version (OSV)",
          "max_points": 15,
          "rule": "The latest published version is queried against OSV. 0 advisories: full; 1–2: two thirds; 3–9: one third; 10+: zero."
        },
        {
          "id": "install_scripts",
          "title": "Install lifecycle scripts in the latest version",
          "max_points": 12,
          "rule": "The latest version declares no preinstall/install/postinstall scripts: full points. Any declared: zero, and the script names are listed as evidence. Script names only — script contents are never fetched or stored."
        },
        {
          "id": "dependency_pinning",
          "title": "Runtime dependencies pinned to exact versions",
          "max_points": 9,
          "rule": "Points are proportional to the share of declared runtime dependencies whose version specifier is an exact version (no range operators). A package with no runtime dependencies scores full."
        },
        {
          "id": "release_provenance",
          "title": "Build provenance / trusted publishing",
          "max_points": 8,
          "rule": "The registry records build attestations for the latest version, or a trusted-publishing configuration: full points."
        },
        {
          "id": "maintainer_activity",
          "title": "Maintainer activity (publish recency and cadence)",
          "max_points": 10,
          "rule": "Latest publish ≤30 days before the check: 6, ≤90: 4, ≤365: 2. Releases in the prior 365 days ≥12: 4, ≥4: 2, ≥1: 1."
        },
        {
          "id": "registry_state",
          "title": "Registry state (not deprecated)",
          "max_points": 8,
          "rule": "The latest version is not marked deprecated in the registry: full points. Deprecated: zero, with the registry's deprecation note quoted as evidence."
        },
        {
          "id": "metadata_completeness",
          "title": "Registry metadata completeness",
          "max_points": 8,
          "rule": "Four recorded facts — description, repository, homepage, README — worth 2 points each."
        },
        {
          "id": "maintainer_breadth",
          "title": "Maintainer breadth (bus factor signal)",
          "max_points": 12,
          "rule": "Maintainers recorded on the registry entry: ≥3: full; 2: two thirds; 1: one third. Counts only — maintainer names and addresses are never republished."
        }
      ],
      "score_formula": "score = round(100 × points earned ÷ points available). Checks marked not_applicable are removed from both sides, so an entry is never marked down for a signal that does not apply to it."
    },
    {
      "kind": "pypi",
      "check_version": "rs-safety-checks-2.0",
      "disclaimer": "Automated checks of public signals as of the date shown. Not a security audit, certification or endorsement, and not a statement about the people who maintain the project. Signals can be incomplete or out of date; each check shows its evidence and source so anyone can verify it.",
      "ai_in_scoring": false,
      "deterministic": true,
      "weights": {
        "license": 10,
        "known_vulnerabilities": 15,
        "dependency_pinning": 9,
        "release_provenance": 8,
        "maintainer_activity": 10,
        "release_files": 8,
        "yanked_state": 8,
        "project_urls": 8,
        "metadata_completeness": 8
      },
      "checks": [
        {
          "id": "license",
          "title": "License recorded (OSI-approved)",
          "max_points": 10,
          "rule": "An OSI-approved License classifier, or a license expression matched to an OSI-approved SPDX id: full points. A license statement that cannot be matched: half. None: zero."
        },
        {
          "id": "known_vulnerabilities",
          "title": "Known advisories for the latest version (OSV)",
          "max_points": 15,
          "rule": "The latest published version is queried against OSV. 0 advisories: full; 1–2: two thirds; 3–9: one third; 10+: zero."
        },
        {
          "id": "dependency_pinning",
          "title": "Runtime dependencies constrained (exact or bounded)",
          "max_points": 9,
          "rule": "Share of declared runtime dependencies (requires_dist, environment markers excluded from the count where the collector can tell) that are pinned exactly (==) or bounded above (<, ~=): exact counts 1, bounded counts one half. No runtime dependencies: full."
        },
        {
          "id": "release_provenance",
          "title": "Release attestations (PEP 740)",
          "max_points": 8,
          "rule": "At least one file of the latest release carries a PEP 740 attestation object as exposed by the PyPI JSON API: full points."
        },
        {
          "id": "maintainer_activity",
          "title": "Maintainer activity (release recency and cadence)",
          "max_points": 10,
          "rule": "Latest release upload ≤30 days before the check: 6, ≤90: 4, ≤365: 2. Releases in the prior 365 days ≥12: 4, ≥4: 2, ≥1: 1."
        },
        {
          "id": "release_files",
          "title": "Release files (wheel and source distribution)",
          "max_points": 8,
          "rule": "The latest release ships both a wheel and a source distribution: full points. One of the two: half."
        },
        {
          "id": "yanked_state",
          "title": "Latest release not yanked",
          "max_points": 8,
          "rule": "No file of the latest release is marked yanked (PEP 592): full points. Some files yanked: half, with the count as evidence. All files yanked: zero."
        },
        {
          "id": "project_urls",
          "title": "Project URLs recorded",
          "max_points": 8,
          "rule": "Four recorded slots — source repository, issue tracker, documentation, homepage — worth 2 points each."
        },
        {
          "id": "metadata_completeness",
          "title": "Metadata completeness (PEP 621 / core metadata)",
          "max_points": 8,
          "rule": "Four recorded facts — requires-python, summary, long description, at least five classifiers — worth 2 points each."
        }
      ],
      "score_formula": "score = round(100 × points earned ÷ points available). Checks marked not_applicable are removed from both sides, so an entry is never marked down for a signal that does not apply to it."
    },
    {
      "kind": "skill",
      "check_version": "rs-safety-checks-2.0",
      "disclaimer": "Automated checks of public signals as of the date shown. Not a security audit, certification or endorsement, and not a statement about the people who maintain the project. Signals can be incomplete or out of date; each check shows its evidence and source so anyone can verify it.",
      "ai_in_scoring": false,
      "deterministic": true,
      "weights": {
        "credentials": 15,
        "known_bad": 15,
        "shell_and_download": 12,
        "install_hooks": 12,
        "obfuscation": 10,
        "network": 10,
        "permission_vs_behavior": 10,
        "provenance": 6
      },
      "checks": [
        {
          "id": "credentials",
          "title": "Credential and environment access",
          "max_points": 15,
          "rule": "The resource-scan check \"Credential and environment access\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        },
        {
          "id": "known_bad",
          "title": "Known-bad indicators",
          "max_points": 15,
          "rule": "The resource-scan check \"Known-bad indicators\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        },
        {
          "id": "shell_and_download",
          "title": "Shell-outs and download-and-execute",
          "max_points": 12,
          "rule": "The resource-scan check \"Shell-outs and download-and-execute\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        },
        {
          "id": "install_hooks",
          "title": "Install and postinstall hooks",
          "max_points": 12,
          "rule": "The resource-scan check \"Install and postinstall hooks\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        },
        {
          "id": "obfuscation",
          "title": "Obfuscation and encoded payloads",
          "max_points": 10,
          "rule": "The resource-scan check \"Obfuscation and encoded payloads\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        },
        {
          "id": "network",
          "title": "Outbound network references",
          "max_points": 10,
          "rule": "The resource-scan check \"Outbound network references\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        },
        {
          "id": "permission_vs_behavior",
          "title": "Permission declarations vs behavior",
          "max_points": 10,
          "rule": "The resource-scan check \"Permission declarations vs behavior\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        },
        {
          "id": "provenance",
          "title": "Provenance facts (weak signals)",
          "max_points": 6,
          "rule": "The resource-scan check \"Provenance facts (weak signals)\" (published list: rightshold-resource-scan-v1) recorded a pass on the scanned bytes: full points. A flag: zero, with the scan's findings quoted as evidence. The rating is the artifact's latest scan receipt scored — the bytes are named by SHA-256 on the receipt, and anyone can re-run the same checks on the same bytes."
        }
      ],
      "score_formula": "score = round(100 × points earned ÷ points available). Checks marked not_applicable are removed from both sides, so an entry is never marked down for a signal that does not apply to it."
    },
    {
      "kind": "mcp",
      "check_version": "rs-safety-checks-2.0",
      "disclaimer": "Automated checks of public signals as of the date shown. Not a security audit, certification or endorsement, and not a statement about the people who maintain the project. Signals can be incomplete or out of date; each check shows its evidence and source so anyone can verify it.",
      "ai_in_scoring": false,
      "deterministic": true,
      "weights": {
        "auth_posture": 15,
        "mcp_safety_docs": 12,
        "license": 10,
        "known_vulnerabilities": 15,
        "maintainer_activity": 10,
        "tls_posture": 8,
        "documentation_completeness": 8
      },
      "checks": [
        {
          "id": "auth_posture",
          "title": "Authentication posture (as documented publicly)",
          "max_points": 15,
          "rule": "Read from the server's PUBLIC registry entry and PUBLIC documentation only — Rightshold never sends a protocol request to a third-party server to test this. OAuth 2.1 documented: full points. Another documented authentication requirement (for example an API key): half. No authentication requirement documented in the public record: zero — a documentation finding, not a test of the live endpoint."
        },
        {
          "id": "mcp_safety_docs",
          "title": "Safety documentation: tool annotations, access scope, injection notes",
          "max_points": 12,
          "rule": "Public documentation names tool annotations or read-only markers: 4. Public documentation describes access scope (allowed directories, allowlist, read-only, sandbox, permissions): 4. Public documentation addresses prompt injection or untrusted input: 4."
        },
        {
          "id": "license",
          "title": "License recorded (OSI-approved)",
          "max_points": 10,
          "rule": "OSI-approved SPDX id recorded for the server's code in the registry entry or its public repository metadata: full points. Declared but unmatched: half. None recorded in the sources checked: zero."
        },
        {
          "id": "known_vulnerabilities",
          "title": "Known advisories for the backing package (OSV)",
          "max_points": 15,
          "rule": "Applies only when the registry entry names a backing package (npm or PyPI): that package version is queried against OSV (0 advisories: full; 1–2: two thirds; 3–9: one third; 10+: zero). No backing package recorded: not applicable."
        },
        {
          "id": "maintainer_activity",
          "title": "Listing activity (version recency and count)",
          "max_points": 10,
          "rule": "Most recent listed version ≤30 days before the check: 6, ≤90: 4, ≤365: 2. Listed versions ≥5: 4, ≥2: 2."
        },
        {
          "id": "tls_posture",
          "title": "TLS posture of the remote endpoint (passive)",
          "max_points": 8,
          "rule": "For a remote server URL, a passive TLS handshake (no protocol traffic): certificate valid and more than 30 days from expiry: full; valid but expiring within 30 days: half; handshake or validation failure: zero. Local-only servers (no remote URL): not applicable."
        },
        {
          "id": "documentation_completeness",
          "title": "Registry documentation completeness",
          "max_points": 8,
          "rule": "Four recorded facts — registry description, a linked repository, a website or documentation link, a named transport — worth 2 points each."
        }
      ],
      "score_formula": "score = round(100 × points earned ÷ points available). Checks marked not_applicable are removed from both sides, so an entry is never marked down for a signal that does not apply to it."
    }
  ],
  "ledger": "Every rating is sealed on the Rightshold ledger as entry type safety.rating with subject \"<kind>:<id>\", its as_of date, check_version, score and rating_sha256. Re-checks add new entries; old ones are never edited. Verify at https://www.rightshold.com/v1/ledger.",
  "maintainers": {
    "recheck": "POST https://www.rightshold.com/v1/ratings/{kind}/{id}/recheck  {\"contact\":\"optional\",\"message\":\"optional\"}",
    "price": "free"
  },
  "rulebook_sha256": "94a03849e5e185eb20779bf45168431471482e3e52730e859a7bdf39f90fc57a"
}