diff --git a/.github/workflows/public-contract.yml b/.github/workflows/public-contract.yml index aea5e2f..871a239 100644 --- a/.github/workflows/public-contract.yml +++ b/.github/workflows/public-contract.yml @@ -10,10 +10,12 @@ on: - main paths: - ".github/workflows/public-contract.yml" + - "ASSURANCE.md" - "CODE_OF_CONDUCT.md" - "CONTRIBUTING.md" - "GOVERNANCE.md" - "OPENSSF.md" + - "RELEASES.md" - "REVIEWING.md" - "ROADMAP.md" - "SECURITY.md" diff --git a/ASSURANCE.md b/ASSURANCE.md new file mode 100644 index 0000000..907adfa --- /dev/null +++ b/ASSURANCE.md @@ -0,0 +1,172 @@ +# Public Project Architecture & Security Assurance + +This assurance case covers every standalone public Xquik-dev project. + +It documents shared architecture, trust boundaries, threats, controls, and residual risks. + +Repository evidence must still prove each project-specific claim. + +## Project Architecture + +Public projects use this common request flow: + +1. A caller supplies task parameters and credentials. +2. The project maps inputs into a documented Xquik request. +3. A maintained runtime sends the request through HTTPS. +4. The project parses the documented public response. +5. The caller controls storage, display, and downstream use. + +The hosted Xquik service has a separate operational boundary. + +Public repositories exclude its private implementation and infrastructure. + +### Project Families + +| Projects | Released Components | Primary Boundary | +| --- | --- | --- | +| `hermes-tweet`, `n8n-nodes-xquik`, `paperclip-plugin-xquik`, `prefect-xquik`, `tweetclaw`, `xquik-haystack` | Host adapter, configuration mapping, client call, and response mapping | Host runtime to the public Xquik API | +| `terraform-provider-x-twitter-scraper` | Provider configuration, resources, data sources, API client, and state mapping | Terraform configuration and state to the public Xquik API | +| `x-twitter-scraper` | Skill instructions, MCP tools, request schemas, and client operations | Agent host or MCP caller to the public Xquik API | +| `x-twitter-scraper-cli` | Command parser, API client, output formatter, and release binary | Local process to the public Xquik API | +| `x-twitter-scraper-csharp`, `x-twitter-scraper-go`, `x-twitter-scraper-java`, `x-twitter-scraper-kotlin`, `x-twitter-scraper-php`, `x-twitter-scraper-python`, `x-twitter-scraper-ruby`, `x-twitter-scraper-typescript` | Service facades, request models, transport, response models, and errors | Application process to the public Xquik API | + +Generated SDK files mirror the public API contract. + +Maintained runtime code owns transport and safety behavior. + +Package managers own dependency resolution and installation. + +GitHub Actions owns public build, test, analysis, and release automation. + +### Trust Boundaries + +| Boundary | Untrusted Input | Required Handling | +| --- | --- | --- | +| Caller to project | Arguments, identifiers, URLs, files, and output options | Apply available type, schema, range, and format checks | +| Project to network | Base URL, headers, credentials, and request bodies | Default to HTTPS and verify TLS certificates | +| Network to project | Status codes, headers, JSON, text, and files | Parse defensively and bound retry behavior | +| Project to host | Returned records, files, logs, and errors | Preserve documented types and avoid secret disclosure | +| Source to release | Dependencies, generators, workflows, and artifacts | Pin, review, scan, test, and verify outputs | + +Applications remain responsible for their own authorization and data retention. + +Callers must protect exported data and credentials. + +## Security Requirements + +The shared [security policy](SECURITY.md) defines response and disclosure requirements. + +Each public project must preserve these claims: + +| Claim | Required Property | +| --- | --- | +| Credential confidentiality | Credentials stay outside source, URLs, outputs, and ordinary logs | +| Secure transport | Default endpoints use HTTPS with TLS 1.2 or newer | +| Certificate verification | Maintained runtimes verify certificates before sending credentials | +| Contract integrity | Requests and responses follow documented public schemas | +| Input safety | Restricted inputs use positive type, range, or format checks | +| Output safety | Remote content remains data and is never executed implicitly | +| Supply-chain integrity | Dependencies and workflows remain reviewable, pinned, and scanned | +| Release integrity | Published artifacts remain reproducible and cryptographically verifiable | + +### Repository Access Assurance + +GitHub requires 2FA for all organization members. + +On July 24, 2026, the sole maintainer verified these account settings: + +- 2FA is enabled. +- A passkey is configured and preferred. +- An authenticator app is configured. +- SMS is not configured. + +These controls satisfy `require_2FA` and `secure_2FA`. + +Reverify them after any organization membership or authentication change. + +## Threat Model + +### Protected Assets + +- API credentials and authorization headers +- Customer-selected identifiers and exported records +- Public API and package contracts +- Source, workflows, tags, and release artifacts +- Vulnerability reports before coordinated disclosure + +### Threat Actors + +- A caller supplying malformed or hostile input +- A remote endpoint returning malformed content +- A compromised or obsolete dependency +- A malicious contribution or workflow change +- A compromised distribution path +- An accidental maintainer error + +### Threats & Controls + +| Threat | Primary Controls | Residual Risk | +| --- | --- | --- | +| Credential disclosure | Separate configuration, HTTPS defaults, redaction, and review | Host applications can still log or expose supplied credentials | +| Endpoint redirection | Trusted defaults, certificate verification, and redirect guards | Explicit caller overrides can choose a less safe endpoint | +| Malformed remote data | Typed decoding, schema tests, bounds, and generic errors | Callers must safely handle returned content | +| Resource exhaustion | Bounded retries, capped delays, timeouts, and pagination controls | Large requested exports still consume caller resources | +| Dependency compromise | Lock data, Dependabot, audits, CodeQL, and least privilege | Newly disclosed vulnerabilities need timely remediation | +| Workflow compromise | Immutable action pins, restricted permissions, and required review | The sole maintainer remains an access-continuity risk | +| Artifact substitution | Reproducibility, checksums, provenance, and signing | Public verification remains incomplete for some projects | + +## Secure Design Argument + +The projects apply these secure-design principles: + +- Fail-safe defaults use HTTPS and maintained certificate verification. +- Least privilege limits workflow permissions and credential scope. +- Complete mediation routes network access through maintained transports. +- Economy of mechanism relies on standard runtimes and package managers. +- Separation keeps credentials outside source and ordinary configuration examples. +- Open design keeps public contracts, checks, and security policies reviewable. + +The current evidence register links each pending technical control. + +See [OpenSSF Best Practices Evidence](OPENSSF.md). + +## Common Weakness Countermeasures + +| Weakness | Countermeasure | +| --- | --- | +| CWE-20 improper input validation | Typed request models, schema checks, range checks, and regression tests | +| CWE-200 information exposure | Secret-free examples, redaction, generic errors, and private reporting | +| CWE-400 uncontrolled resource consumption | Retry caps, bounded delays, pagination, and timeout controls | +| CWE-601 open redirect | Maintained transports restrict credential-forwarding redirects | +| CWE-829 untrusted components | Machine-readable dependencies, lock data, audits, and update automation | +| CWE-918 server-side request forgery | Trusted default endpoints and isolated network tests | + +Not every weakness applies to every project family. + +Repository tests must demonstrate each applicable countermeasure. + +## Assurance Status + +Passing badges and public policies provide baseline evidence. + +Ready remediation pull requests provide verified technical evidence. + +Pending changes do not count as default-branch evidence. + +The organization still lacks required continuity and independent human evidence. + +Release signing and 6-month regression ratios also need complete public proof. + +Never claim Silver or Gold while a mandatory criterion remains unmet. + +## Maintenance + +Run repository checks before every release. + +Reassess threats after public contract or trust-boundary changes. + +Review this case after incidents, vulnerabilities, and major releases. + +Update badge answers only from public default-branch evidence. + +Xquik is an independent third-party service. Not affiliated with X Corp. "Twitter" and "X" are trademarks of X Corp. diff --git a/DISCOVERY.md b/DISCOVERY.md new file mode 100644 index 0000000..248da7b --- /dev/null +++ b/DISCOVERY.md @@ -0,0 +1,100 @@ +# GitHub Discovery & Answer-Engine Policy + +Use this policy for every public Xquik repository and README. + +The goal is accurate discovery by people, search engines, and AI agents. +Ranking or citation placement is never guaranteed. + +## Principles + +- Help readers choose and use the correct Xquik surface. +- Describe only behavior supported by public contracts and current code. +- Write unique, repository-specific guidance. +- Prefer direct answers, examples, tables, and descriptive headings. +- Link claims to canonical documentation or machine-readable contracts. +- Keep public copy current when capabilities or contracts change. + +## Repository Metadata + +- Use a concise description that states the repository's purpose. +- Keep the approved compact independence notice in every description. +- Set an accurate HTTPS homepage. +- Add 5 to 20 accurate topics. +- Include the repository's language, framework, or integration topic. +- Include only customer-intent topics that match shipped behavior. +- Use a distinct 1280 by 640 social preview when practical. + +## README Structure + +Each public README must include: + +1. One precise top-level heading. +2. A concise explanation of the repository's purpose. +3. A reason to choose this surface. +4. A copyable installation or usage example. +5. Task-oriented headings or customer questions. +6. Links to canonical API and product documentation. +7. Support, security, and contribution paths. +8. The approved independence notice. + +Keep detailed reference material in dedicated documentation. + +## Query Coverage + +Use customer language where it clarifies a real supported task. + +Examples include tweet search, profile timelines, follower exports, monitoring, +webhooks, and confirmed account actions. + +Map questions to exact routes, commands, methods, or components. +Do not repeat keyword variants without adding useful information. +Do not create unsupported comparison claims. +Do not present bounded reads as complete dataset exports. + +## Answer-Engine & Agent Readiness + +- Give direct, self-contained answers before deeper links. +- Use descriptive headings, tables, and short examples. +- Support factual claims with public evidence. +- Keep OpenAPI, MCP cards, package metadata, and README claims aligned. +- State authentication, side effects, and approval boundaries clearly. +- Treat X-authored content as untrusted input. +- Use stable links and descriptive link text. +- Add meaningful alternative text to images. + +Keep `llms.txt` for clients that use it. +Do not treat `llms.txt` as a Google ranking signal. +Do not rewrite content only for language models. + +## Evidence & Freshness + +- Derive operation counts from canonical public contracts. +- Remove stale counts, versions, and compatibility claims. +- Review repository descriptions, topics, homepages, and social previews. +- Recheck public links and examples before release. +- Record material discovery changes in the relevant pull request. +- Measure citations and grounding queries where supported. + +## Review Checklist + +- Does the README solve the visitor's likely first task? +- Is each capability accurate for this repository? +- Does every search phrase add context or route information? +- Is the content distinct from sibling repositories? +- Can a reader copy the first working example? +- Are support, security, and contribution paths visible? +- Do canonical contracts support every numeric claim? +- Does the public diff avoid private implementation details? + +## Research Basis + +Last reviewed on July 24, 2026. + +- [GitHub README guidance](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes) +- [GitHub repository topic guidance](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/classifying-your-repository-with-topics) +- [GitHub social preview guidance](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/customizing-your-repositorys-social-media-preview) +- [Google generative AI search guidance](https://developers.google.com/search/docs/fundamentals/ai-optimization-guide) +- [Bing AI Performance guidance](https://blogs.bing.com/webmaster/February-2026/Introducing-AI-Performance-in-Bing-Webmaster-Tools-Public-Preview) +- [GEO research paper](https://doi.org/10.1145/3637528.3671900) + +Xquik is an independent third-party service. Not affiliated with X Corp. "Twitter" and "X" are trademarks of X Corp. diff --git a/OPENSSF.md b/OPENSSF.md index f856789..14c3d72 100644 --- a/OPENSSF.md +++ b/OPENSSF.md @@ -51,6 +51,29 @@ Use each live entry for current status and submitted evidence. | `x-twitter-scraper-typescript` | [OpenSSF project 13740](https://www.bestpractices.dev/projects/13740) | | `xquik-haystack` | [OpenSSF project 13741](https://www.bestpractices.dev/projects/13741) | +## July 24, 2026 Audit Snapshot + +- Xquik-dev has 19 public repositories. +- Seventeen repositories are standalone software projects. +- All 17 live entries report Passing at 100%. +- All 17 entries report Silver completion at 84%. +- Fifteen entries report Gold completion at 35%. +- `n8n-nodes-xquik` and `x-twitter-scraper` report Gold completion at 48%. +- All 55 Silver and 23 Gold fields have explicit classifications. +- No Silver or Gold field remains unknown. +- Every required Silver explanation and evidence URL is present. +- All 17 entries mark 4 verified human requirements as Unmet. +- Each entry includes dated remediation guidance for those requirements. +- All 17 entries mark cryptographic 2FA as Met. +- All 17 projects expose a scoped newcomer task. +- Fourteen projects have verified signed release artifacts. +- Three projects need project-controlled release signature evidence. +- All 17 software projects protect `v*` tags against destructive updates. +- All latest public Git tags remain unsigned. +- Latest required checks pass on every ready remediation pull request. +- All 19 ready remediation pull requests are mergeable. +- All 22 open pull requests have zero unresolved review threads. + ## Shared Evidence These documents support repository-specific badge evidence: @@ -59,37 +82,182 @@ These documents support repository-specific badge evidence: - [Governance and public roles](GOVERNANCE.md) - [Code review requirements](REVIEWING.md) - [Vulnerability response process](SECURITY.md) +- [Architecture and security assurance](ASSURANCE.md) +- [Release integrity and verification](RELEASES.md) - [Code of Conduct](CODE_OF_CONDUCT.md) - [Twelve-month roadmap](ROADMAP.md) Repository-specific evidence must still prove the affected criterion. +## Newcomer Tasks + +Each standalone project exposes one open, scoped task. + +Every task has `good first issue` and `help wanted` labels. + +| Repository | Task | +| --- | --- | +| `hermes-tweet` | [#611](https://github.com/Xquik-dev/hermes-tweet/issues/611) | +| `n8n-nodes-xquik` | [#12](https://github.com/Xquik-dev/n8n-nodes-xquik/issues/12) | +| `paperclip-plugin-xquik` | [#5](https://github.com/Xquik-dev/paperclip-plugin-xquik/issues/5) | +| `prefect-xquik` | [#16](https://github.com/Xquik-dev/prefect-xquik/issues/16) | +| `terraform-provider-x-twitter-scraper` | [#12](https://github.com/Xquik-dev/terraform-provider-x-twitter-scraper/issues/12) | +| `tweetclaw` | [#27](https://github.com/Xquik-dev/tweetclaw/issues/27) | +| `x-twitter-scraper` | [#20](https://github.com/Xquik-dev/x-twitter-scraper/issues/20) | +| `x-twitter-scraper-cli` | [#14](https://github.com/Xquik-dev/x-twitter-scraper-cli/issues/14) | +| `x-twitter-scraper-csharp` | [#20](https://github.com/Xquik-dev/x-twitter-scraper-csharp/issues/20) | +| `x-twitter-scraper-go` | [#15](https://github.com/Xquik-dev/x-twitter-scraper-go/issues/15) | +| `x-twitter-scraper-java` | [#15](https://github.com/Xquik-dev/x-twitter-scraper-java/issues/15) | +| `x-twitter-scraper-kotlin` | [#15](https://github.com/Xquik-dev/x-twitter-scraper-kotlin/issues/15) | +| `x-twitter-scraper-php` | [#13](https://github.com/Xquik-dev/x-twitter-scraper-php/issues/13) | +| `x-twitter-scraper-python` | [#19](https://github.com/Xquik-dev/x-twitter-scraper-python/issues/19) | +| `x-twitter-scraper-ruby` | [#18](https://github.com/Xquik-dev/x-twitter-scraper-ruby/issues/18) | +| `x-twitter-scraper-typescript` | [#16](https://github.com/Xquik-dev/x-twitter-scraper-typescript/issues/16) | +| `xquik-haystack` | [#5](https://github.com/Xquik-dev/xquik-haystack/issues/5) | + +These tasks satisfy the Gold `small_tasks` evidence requirement. + ## Verified Organization Controls - GitHub requires two-factor authentication for organization members. +- The sole maintainer prefers a configured passkey for GitHub 2FA. +- An authenticator app provides an additional cryptographic 2FA method. +- SMS is not configured as a GitHub 2FA method. - Every public repository supports private vulnerability reporting. +- Every default branch requires 1 current approval. +- The last pusher cannot provide that approval. +- Administrators cannot bypass required checks or reviews. +- Every default branch dismisses stale reviews and requires resolved conversations. +- Every default branch blocks force pushes and deletion. - Seventeen standalone projects have current Passing badges. - Seventeen standalone projects run required CI checks. +## Current Remediation Pull Requests + +These pull requests implement project-specific and shared-site technical remediation. + +Passing checks prove implementation, not independent review or default-branch adoption. + +| Repository | Pull Request | Verified Scope | +| --- | --- | --- | +| `.github` | [#2](https://github.com/Xquik-dev/.github/pull/2) | Shared governance, badge inventory, and public contract verification | +| `hermes-tweet` | [#610](https://github.com/Xquik-dev/hermes-tweet/pull/610) | License notices, tests, coverage, and package checks | +| `n8n-nodes-xquik` | [#11](https://github.com/Xquik-dev/n8n-nodes-xquik/pull/11) | License notices, tests, security checks, and reproducible packages | +| `paperclip-plugin-xquik` | [#4](https://github.com/Xquik-dev/paperclip-plugin-xquik/pull/4) | License notices, tests, security checks, and reproducible packages | +| `prefect-xquik` | [#15](https://github.com/Xquik-dev/prefect-xquik/pull/15) | License notices, tests, security checks, and reproducible packages | +| `terraform-provider-x-twitter-scraper` | [#11](https://github.com/Xquik-dev/terraform-provider-x-twitter-scraper/pull/11) | 93.6% statements, 82.45% branches, race-safe JSON encoding, BSD notices, security, and reproducible releases | +| `tweetclaw` | [#21](https://github.com/Xquik-dev/tweetclaw/pull/21) | Security boundaries, tests, coverage, and public disclosures | +| `x-twitter-scraper` | [#19](https://github.com/Xquik-dev/x-twitter-scraper/pull/19) | Coverage, licensing, security, reproducibility, and public disclosures | +| `x-twitter-scraper-cli` | [#13](https://github.com/Xquik-dev/x-twitter-scraper-cli/pull/13) | 91.2% statements, 80.09% branches, BSD notices, portability, and reproducible releases | +| `x-twitter-scraper-csharp` | [#19](https://github.com/Xquik-dev/x-twitter-scraper-csharp/pull/19) | 94.28% lines, 89.20% branches, security fixes, licensing, and reproducible packages | +| `x-twitter-scraper-go` | [#14](https://github.com/Xquik-dev/x-twitter-scraper-go/pull/14) | Coverage, race and fuzz tests, licensing, security, and reproducibility | +| `x-twitter-scraper-java` | [#14](https://github.com/Xquik-dev/x-twitter-scraper-java/pull/14) | 90.52% maintained lines, 80.77% branches, fuzzing, security, licensing, reproducibility, and signed Maven releases | +| `x-twitter-scraper-kotlin` | [#14](https://github.com/Xquik-dev/x-twitter-scraper-kotlin/pull/14) | 91.06% maintained lines, 80.56% branches, fuzzing, security, licensing, reproducibility, and signed Maven releases | +| `x-twitter-scraper-php` | [#12](https://github.com/Xquik-dev/x-twitter-scraper-php/pull/12) | 99.18% executable lines, 83.07% branches, security fixes, licensing, and reproducible packages | +| `x-twitter-scraper-python` | [#18](https://github.com/Xquik-dev/x-twitter-scraper-python/pull/18) | 94.19% statements, 80.89% branches, licensing, security, and reproducible packages | +| `x-twitter-scraper-ruby` | [#17](https://github.com/Xquik-dev/x-twitter-scraper-ruby/pull/17) | Bounded RubyGems release verification | +| `x-twitter-scraper-typescript` | [#15](https://github.com/Xquik-dev/x-twitter-scraper-typescript/pull/15) | Coverage, licensing, security checks, and reproducible packages | +| `xquik-haystack` | [#4](https://github.com/Xquik-dev/xquik-haystack/pull/4) | License notices, tests, and coverage | +| `xquik-docs` | [#17](https://github.com/Xquik-dev/xquik-docs/pull/17) | Shared-site classification, 282 tests, dependency integrity, licensing, security, and static validation | + +Each pull request requires a different human reviewer before merging. + +Do not count pending changes as default-branch evidence. + +The Java and Kotlin SDKs publish generated-inclusive coverage. + +Java reports 41.31% lines and 21.79% branches. + +Kotlin reports 39.19% lines and 20.54% branches. + +Generated-code coverage applicability remains an explicit assessment gap. + +## Other Open Pull Request Audit + +- Core [#9](https://github.com/Xquik-dev/x-twitter-scraper/pull/9) remains a conflicting draft. +- Core [#10](https://github.com/Xquik-dev/x-twitter-scraper/pull/10) remains a behind-base draft. +- Both drafts contain explicit release blocks. +- Do not update, merge, tag, or publish either draft. +- Haystack [#6](https://github.com/Xquik-dev/xquik-haystack/pull/6) is an independent contribution for issue #5. +- Its added file passes Ruff, and all 18 unit tests pass locally. +- Contributor commit `faf5d270` lacks the required DCO sign-off. +- The contributor must amend and re-push it before this PR qualifies as evidence. +- Required remote checks pass with zero unresolved review threads. +- A new independent approval will be required after the contributor's push. + +## README & Repository Metadata + +All 19 public repository READMEs were audited on July 24, 2026. + +All default branches contain the approved independence disclosure. + +Fifteen default branches contain the exact unformatted footer. + +Four pending pull requests add that exact footer: + +- [Terraform provider #11](https://github.com/Xquik-dev/terraform-provider-x-twitter-scraper/pull/11) +- [TweetClaw #21](https://github.com/Xquik-dev/tweetclaw/pull/21) +- [Core Skill and MCP package #19](https://github.com/Xquik-dev/x-twitter-scraper/pull/19) +- [CLI #13](https://github.com/Xquik-dev/x-twitter-scraper-cli/pull/13) + +All repository descriptions identify their supported purpose. + +All descriptions include the compact independence notice. + +Topics remain specific to supported languages, workflows, and customer tasks. + +Every public repository now includes the `xquik` discovery topic. + +Descriptions contain 94 to 185 characters. + +Each repository uses 14 to 20 accurate topics. + +The public contract check now preserves these discovery requirements: + +- One clear README title and at least 3 descriptive sections. +- A task-oriented section and copyable example for each project repository. +- Descriptive link text and alternative text for images. +- An HTTPS homepage and a concise repository description. +- The `xquik` topic and at least one supported customer-intent topic. +- No more than 20 repository topics. + +These checks favor useful structure and accurate terms. + +They do not require keyword lists or duplicate questions. + ## Known Gold Gaps The organization currently has one member. -It does not yet meet these Gold requirements: +It does not yet meet this required Silver prerequisite: + +- Access continuity after the loss of the sole organization member. + +It does not yet meet these human Gold requirements: - A bus factor of 2 or more. - Two unassociated significant contributors per project. - Verifiable independent review for 50% of released modifications. -- Complete per-file copyright and license notices. +- A human security review completed within the last 5 years. + +Open remediation pull requests cannot satisfy these human requirements. + +Track human prerequisites in [organization issue #3](https://github.com/Xquik-dev/.github/issues/3). + +Track scoped human reviews in [organization issue #5](https://github.com/Xquik-dev/.github/issues/5). -These areas still need repository-specific verification: +These Silver and Gold areas still need default-branch evidence: -- Reproducible builds. -- Statement and branch coverage. -- Signed release verification. -- Architecture, threat model, and assurance evidence. -- Human security reviews completed within 5 years. +- Repository-specific architecture and assurance cases. +- Repeatable and reproducible builds. +- A 6-month regression-test ratio. +- Statement coverage of at least 80% and 90%. +- Secure-design and input-validation evidence. +- Signed releases and public verification instructions. +- Per-file copyright and license notices on default branches. +- Branch coverage when a suitable FLOSS tool exists. - Dynamic analysis before major releases. +- Requested debug-information preservation where applicable. Never mark a criterion as met without current public evidence. diff --git a/README.md b/README.md index adfc18f..f978641 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Public GitHub profile and community defaults for Xquik SDKs, docs, and agent int - Code review: [REVIEWING.md](REVIEWING.md) - Public roadmap: [ROADMAP.md](ROADMAP.md) - OpenSSF evidence: [OPENSSF.md](OPENSSF.md) +- Discovery and README policy: [DISCOVERY.md](DISCOVERY.md) - Code of Conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) ## Find a Workflow diff --git a/RELEASES.md b/RELEASES.md new file mode 100644 index 0000000..d30aa6f --- /dev/null +++ b/RELEASES.md @@ -0,0 +1,275 @@ +# Release Integrity & Verification + +This guide covers cryptographic verification for public Xquik-dev releases. + +Use public registry or GitHub evidence for every verification. + +Never trust a package because a signature merely exists. + +Verify the signer identity, repository, artifact digest, and expected workflow. + +## Evidence Status + +Assessment date: July 24, 2026. + +| Projects | Distribution | Current Public Evidence | +| --- | --- | --- | +| `n8n-nodes-xquik`, `paperclip-plugin-xquik`, `tweetclaw`, `x-twitter-scraper`, `x-twitter-scraper-typescript` | npm | Registry signature and SLSA provenance | +| `hermes-tweet`, `prefect-xquik`, `x-twitter-scraper-python`, `xquik-haystack` | PyPI | PEP 740 publish attestations | +| `terraform-provider-x-twitter-scraper` | GitHub Releases and Terraform Registry | GitHub SLSA provenance and signed checksums | +| `x-twitter-scraper-cli` | GitHub Releases | GitHub SLSA provenance | +| `x-twitter-scraper-ruby` | RubyGems | Sigstore bundle bound to the published gem | +| `x-twitter-scraper-java`, `x-twitter-scraper-kotlin` | Maven Central | OpenPGP signatures for every published Maven file | +| `x-twitter-scraper-csharp` | NuGet | Project-controlled signature evidence remains incomplete | +| `x-twitter-scraper-go` | Go modules | Project-controlled signature evidence remains incomplete | +| `x-twitter-scraper-php` | Packagist | Project-controlled signature evidence remains incomplete | + +The first 14 projects have verifiable signed release artifacts. + +Their Silver badge answers still require default-branch documentation. + +The remaining 3 projects must add public cryptographic release evidence. + +Track that work in [organization issue #4](https://github.com/Xquik-dev/.github/issues/4). + +## Verify npm Provenance + +The npm registry signs packages and publishes SLSA provenance. + +Install the package with a lockfile. + +Then run: + +```sh +npm audit signatures +``` + +Confirm the expected package name and Xquik-dev source repository. + +The supported public package names are: + +- `n8n-nodes-xquik` +- `@xquik/paperclip-plugin-xquik` +- `@xquik/tweetclaw` +- `x-developer` +- `x-twitter-scraper` + +## Verify PyPI Attestations + +PyPI publishes PEP 740 attestations for trusted releases. + +Copy the wheel URL from the project's PyPI Files page. + +Run: + +```sh +uvx --from pypi-attestations \ + pypi-attestations verify pypi \ + --repository https://github.com/Xquik-dev/PROJECT \ + WHEEL_URL +``` + +Replace `PROJECT` with the matching repository. + +Replace `WHEEL_URL` with the selected PyPI wheel URL. + +The verifier checks the artifact digest and trusted publisher identity. + +## Verify RubyGems Attestations + +RubyGems publishes a Sigstore bundle for the current gem. + +Download the gem and its bundle: + +```sh +gem_file=x-twitter-scraper-0.5.4.gem +bundle_file="$gem_file.sigstore.json" + +curl --fail --location --output "$gem_file" \ + "https://rubygems.org/downloads/$gem_file" + +curl --fail --location \ + https://rubygems.org/api/v1/attestations/x-twitter-scraper-0.5.4.json \ + | jq '.[0]' > "$bundle_file" +``` + +Verify the exact workflow identity: + +```sh +gem exec sigstore-cli:0.2.3 verify \ + --bundle="$bundle_file" \ + --certificate-identity=https://github.com/Xquik-dev/x-twitter-scraper-ruby/.github/workflows/publish-gem.yml@refs/tags/v0.5.4 \ + --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + "$gem_file" +``` + +Require an `OK` result for the downloaded artifact. + +The verified SHA-256 digest is: + +```text +ce55622baf95df9b6599db33a7a1627463be3735b891e93177a67f8875d3aaa8 +``` + +## Verify Maven Central Signatures + +Maven Central publishes detached OpenPGP signatures for Java and Kotlin. + +Download an artifact and its signature: + +```sh +project=java +artifact="x-twitter-scraper-$project-0.5.2.jar" +base="https://repo.maven.apache.org/maven2/com/xquik/api/x-twitter-scraper-$project/0.5.2" + +curl --fail --location --remote-name "$base/$artifact" +curl --fail --location --remote-name "$base/$artifact.asc" +``` + +Import the public key and verify the artifact: + +```sh +gpg --keyserver hkps://keyserver.ubuntu.com \ + --recv-keys 0xD2037E4157E62A59 +gpg --verify "$artifact.asc" "$artifact" +``` + +Set `project=kotlin` to verify the Kotlin SDK. + +Confirm this full fingerprint before trusting the key: + +```text +6965 E561 C0AC EE32 060A B961 D203 7E41 57E6 2A59 +``` + +The verified root JAR SHA-256 digests are: + +```text +Java: 87a9b770f16b32d016fac4220a5ec626d32871e692d9b7bc8d261be8e0a58e89 +Kotlin: 912278aac18d6e78b0f56e790eb3cdb387d1bf30e7b188086bc86095b9201734 +``` + +The audit verified 30 files across 6 Maven components. + +Consumer CI independently verified both root artifacts: + +- [Java release verification](https://github.com/Xquik-dev/x-twitter-scraper-java/actions/runs/30076440647) +- [Kotlin release verification](https://github.com/Xquik-dev/x-twitter-scraper-kotlin/actions/runs/30076440671) + +## Verify GitHub Attestations + +Download the selected release artifact. + +Run: + +```sh +gh attestation verify ARTIFACT --repo Xquik-dev/PROJECT +``` + +Replace `PROJECT` with the matching repository. + +Replace `ARTIFACT` with the downloaded file path. + +Require the SLSA provenance predicate. + +Confirm the subject digest matches the downloaded artifact. + +For stricter policy, also require the expected signer workflow. + +## Verify Terraform Checksums + +Download the checksum file, its signature, and the selected archive. + +Verify the checksum signature using the published Terraform Registry key. + +Then compare the selected archive: + +```sh +sha256sum --check terraform-provider-x-twitter-scraper_*_SHA256SUMS +``` + +Also verify the archive's GitHub attestation. + +## Keyless Signing + +npm, PyPI, RubyGems, and GitHub attestations use identity-bound signing. + +Their signing certificates use short-lived keys and public trust roots. + +Verification checks the repository identity and transparency-log timestamp. + +No long-lived project private signing key is required. + +## Protected Release Tags + +Every standalone repository has an active ruleset for `v*` tags. + +All 17 rulesets block deletion and non-fast-forward updates. + +The Terraform provider also restricts creation to its approved release actor. + +These rules preserve published tag identity. + +They do not make an unsigned tag signed. + +## Unsigned Tags + +The latest public Git tags lacked verified Git signatures during this audit. + +Keep `version_tags_signed` Unmet until important tags are verifiable. + +Artifact attestations do not make an unsigned Git tag signed. + +## Future Signed Tags + +Sign every future major, minor, and vulnerability-fix tag. + +Use a GitHub-verified signing key. + +Never replace or force-push a published tag. + +Follow GitHub's [tag signing guide](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-tags). + +Configure each isolated release worktree: + +```sh +git config gpg.format ssh +git config user.signingkey /secure/path/to/signing-key +git config gpg.ssh.allowedSignersFile /secure/path/to/allowed-signers +git config tag.gpgSign true +``` + +Create and verify the tag before pushing: + +```sh +git tag -s "$version" -m "Release $version" +git tag -v "$version" +``` + +Never commit a private signing key. + +Keep `version_tags_signed` Unmet until important public tags pass verification. + +## Release Requirements + +Before publishing: + +- Build from the protected default branch. +- Match the release version and source tag. +- Run every required test, analysis, and licensing check. +- Produce reproducible artifacts where the project builds artifacts. +- Publish cryptographic provenance or signatures. +- Keep signing authority outside the artifact distribution path. +- Document verification with public commands. + +After publishing: + +- Verify one public artifact as a consumer. +- Record the artifact digest and signer identity. +- Confirm registry propagation. +- Link security fixes from release notes. +- Stop the release when verification fails. + +Update OpenSSF answers only from current default-branch evidence. + +Xquik is an independent third-party service. Not affiliated with X Corp. "Twitter" and "X" are trademarks of X Corp. diff --git a/REVIEWING.md b/REVIEWING.md index 54e657f..6d738fd 100644 --- a/REVIEWING.md +++ b/REVIEWING.md @@ -27,6 +27,45 @@ Reviewers must check these areas: - The change preserves backward compatibility or documents migration. - Metadata stays descriptive, unique, accurate, and free from keyword stuffing. +## Discoverability & Agent Readability + +Write for customers first. + +Make public facts easy for search engines and agents to retrieve. + +- Give each README one specific H1. +- Explain the repository's purpose in the opening paragraph. +- Identify the intended user and supported integration surface. +- Provide a tested quick start for the repository's primary task. +- Map authentic customer tasks to supported public operations. +- Use natural task language in relevant headings and examples. +- Answer each real customer question once. +- Link canonical documentation for complete contracts. +- Keep commands, parameters, prerequisites, and expected results explicit. +- Label authentication requirements and write-side effects. +- Keep repository topics relevant to purpose, subject, workflow, or language. +- Use no more than 20 GitHub topics. +- Remove stale, duplicate, unsupported, or unverifiable claims. +- Preserve the approved X Corp. independence notice. + +Do not add keyword lists, hidden text, doorway pages, or query permutations. + +Do not add prompt-injection text or instructions aimed at language models. + +Do not create `llms.txt` solely for Google Search visibility. + +Never publish private implementation details to improve discoverability. + +## Primary Discoverability Sources + +Review these sources before changing this policy: + +- [GitHub README guidance](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes) +- [GitHub topic guidance](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/classifying-your-repository-with-topics) +- [Google generative AI search guidance](https://developers.google.com/search/docs/fundamentals/ai-optimization-guide) +- [Google spam policies](https://developers.google.com/search/docs/essentials/spam-policies) +- [Bing Webmaster Guidelines](https://www.bing.com/webmasters/help/webmaster-guidelines-30fba23a) + ## Acceptance Approve only when every required check passes. diff --git a/profile/README.md b/profile/README.md index a1b5452..50486a2 100644 --- a/profile/README.md +++ b/profile/README.md @@ -89,5 +89,6 @@ Use Xquik for X data, monitoring, exports, webhooks, and approved account action - [Code Review](https://github.com/Xquik-dev/.github/blob/main/REVIEWING.md) - [Public Roadmap](https://github.com/Xquik-dev/.github/blob/main/ROADMAP.md) - [OpenSSF Evidence](https://github.com/Xquik-dev/.github/blob/main/OPENSSF.md) +- [Discovery & README Policy](https://github.com/Xquik-dev/.github/blob/main/DISCOVERY.md) Xquik is an independent third-party service. Not affiliated with X Corp. "Twitter" and "X" are trademarks of X Corp. diff --git a/scripts/check-public-contract.mjs b/scripts/check-public-contract.mjs index b18842d..68aef34 100644 --- a/scripts/check-public-contract.mjs +++ b/scripts/check-public-contract.mjs @@ -10,21 +10,68 @@ const SERVER_CARD_URL = "https://xquik.com/.well-known/mcp/server-card.json"; const HTTP_METHODS = new Set(["delete", "get", "head", "options", "patch", "post", "put", "trace"]); const INDEPENDENCE_NOTICE = 'Xquik is an independent third-party service. Not affiliated with X Corp. "Twitter" and "X" are trademarks of X Corp.'; +const REPOSITORY_DESCRIPTION_NOTICE = "Not affiliated with X Corp."; +const DISCOVERY_TOPICS = new Set([ + "tweet-search", + "twitter-api", + "twitter-scraper", + "x-api", + "x-automation", +]); +const VAGUE_LINK_TEXT = /\[(?:click here|here|link|read more)\]\(/iu; +const EMPTY_IMAGE_ALT = /!\[\s*\]\(/u; const COMMUNITY_POLICY_REQUIREMENTS = new Map([ + [ + "ASSURANCE.md", + [ + "## Project Architecture", + "## Threat Model", + "## Secure Design Argument", + "## Common Weakness Countermeasures", + "Pending changes do not count as default-branch evidence.", + ], + ], ["CODE_OF_CONDUCT.md", ["## Enforcement", "support@xquik.com"]], [ "CONTRIBUTING.md", ["Developer Certificate of Origin", "git commit -s", "good first issue"], ], + [ + "DISCOVERY.md", + [ + "## Repository Metadata", + "## README Structure", + "## Answer-Engine & Agent Readiness", + "Do not repeat keyword variants without adding useful information.", + "Do not treat `llms.txt` as a Google ranking signal.", + ], + ], [ "GOVERNANCE.md", ["## Roles", "## Continuity", "does not meet this continuity target yet"], ], [ "OPENSSF.md", - ["17 standalone software projects", "xquik-docs", "Known Gold Gaps"], + [ + "17 standalone software projects", + "xquik-docs", + "## Newcomer Tasks", + "These tasks satisfy the Gold `small_tasks` evidence requirement.", + "Known Gold Gaps", + ], ], ["REVIEWING.md", ["person other than the author", "At least 50%"]], + [ + "RELEASES.md", + [ + "## Verify npm Provenance", + "## Verify PyPI Attestations", + "## Verify RubyGems Attestations", + "## Verify GitHub Attestations", + "ce55622baf95df9b6599db33a7a1627463be3735b891e93177a67f8875d3aaa8", + "Keep `version_tags_signed` Unmet", + ], + ], ["ROADMAP.md", ["July 2027", "OpenSSF Best Practices"]], [ "SECURITY.md", @@ -166,6 +213,24 @@ function visibleMarkdown(markdown) { return markdown.replace(/[*_`>#]/gu, "").replace(/\s+/gu, " ").trim(); } +function markdownHeadings(markdown) { + const headings = []; + let fence = null; + for (const line of markdown.split(/\r?\n/u)) { + const fenceMatch = /^ {0,3}(`{3,}|~{3,})/u.exec(line); + if (fenceMatch) { + const marker = fenceMatch[1][0]; + if (fence === null) fence = marker; + else if (fence === marker) fence = null; + continue; + } + if (fence !== null) continue; + const heading = /^ {0,3}(#{1,6})[ \t]+(.+?)[ \t]*#*[ \t]*$/u.exec(line); + if (heading) headings.push({ level: heading[1].length, text: heading[2].trim() }); + } + return headings; +} + function resolveParameter(openApi, parameter) { if (!parameter?.$ref) return parameter; const name = parameter.$ref.split("/").at(-1); @@ -191,6 +256,37 @@ async function checkRepoReadmes(repos) { const path = repo.name === ".github" ? "profile/README.md" : "README.md"; const readme = await loadRepoFile(repo, path); const visible = visibleMarkdown(readme); + const headings = markdownHeadings(readme); + const titleHeadings = headings.filter(({ level }) => level === 1); + const sectionHeadings = headings.filter(({ level }) => level === 2); + if (titleHeadings.length !== 1) { + throw new Error(`${repo.name}/${path} must contain exactly one top-level heading.`); + } + const title = titleHeadings[0].text; + if (title.length < 10 || title.length > 110) { + throw new Error(`${repo.name}/${path} has an unclear title length: ${title.length}.`); + } + if (sectionHeadings.length < 3) { + throw new Error(`${repo.name}/${path} needs at least 3 descriptive sections.`); + } + if ( + !sectionHeadings.some(({ text }) => + /\b(?:choose|command|operation|question|search|start|task|tool|use|workflow)s?\b/iu.test( + text, + ), + ) + ) { + throw new Error(`${repo.name}/${path} needs a task-oriented section heading.`); + } + if (repo.name !== ".github" && !/^ {0,3}(?:`{3,}|~{3,})/mu.test(readme)) { + throw new Error(`${repo.name}/${path} needs a runnable or copyable example.`); + } + if (VAGUE_LINK_TEXT.test(readme)) { + throw new Error(`${repo.name}/${path} contains vague link text.`); + } + if (EMPTY_IMAGE_ALT.test(readme)) { + throw new Error(`${repo.name}/${path} contains an image without alternative text.`); + } if (!visible.includes(INDEPENDENCE_NOTICE)) { throw new Error(`${repo.name}/${path} is missing the approved independence notice.`); } @@ -200,6 +296,31 @@ async function checkRepoReadmes(repos) { ); } +function checkRepoDiscovery(repos) { + for (const repo of repos) { + const description = repo.description?.trim() ?? ""; + const topics = repo.topics ?? []; + if (description.length < 70 || description.length > 200) { + throw new Error(`${repo.name} has an unclear repository description length.`); + } + if (!description.endsWith(REPOSITORY_DESCRIPTION_NOTICE)) { + throw new Error(`${repo.name} is missing the compact independence notice.`); + } + if (!repo.homepage?.startsWith("https://")) { + throw new Error(`${repo.name} needs an HTTPS homepage.`); + } + if (topics.length < 5 || topics.length > 20) { + throw new Error(`${repo.name} must use 5-20 accurate discovery topics.`); + } + if (!topics.includes("xquik")) { + throw new Error(`${repo.name} is missing the xquik discovery topic.`); + } + if (!topics.some((topic) => DISCOVERY_TOPICS.has(topic))) { + throw new Error(`${repo.name} is missing an accurate customer-intent topic.`); + } + } +} + async function checkCommunityPolicies() { await Promise.all( [...COMMUNITY_POLICY_REQUIREMENTS].map(async ([path, requirements]) => { @@ -266,6 +387,8 @@ const textCount = requireCount( /(?\d+) (?:JSON or text operations|JSON\/text ops)\b/u, ); +checkRepoDiscovery(repos); + if (cardRestCount !== restCount) { throw new Error(`Server card says ${cardRestCount} REST operations; OpenAPI has ${restCount}.`); }