|
| 1 | +# Security Policy |
| 2 | + |
| 3 | +## Reporting a Vulnerability |
| 4 | + |
| 5 | +**This is a public repository. Do not open a public GitHub issue for a security |
| 6 | +vulnerability** — that discloses it to everyone before a fix is available. |
| 7 | + |
| 8 | +Instead, report privately through GitHub's **Private Vulnerability Reporting**: |
| 9 | + |
| 10 | +1. Go to the **Security** tab of this repository. |
| 11 | +2. Click **Report a vulnerability**. |
| 12 | +3. Describe the problem, including steps to reproduce, affected version(s), and |
| 13 | + the impact. |
| 14 | + |
| 15 | +We will acknowledge the report, keep you informed as we investigate, and |
| 16 | +coordinate the disclosure timeline and a fixed release with you. Please give us |
| 17 | +a reasonable window to ship a fix before any public disclosure. |
| 18 | + |
| 19 | +## Scope |
| 20 | + |
| 21 | +This repo is the **MarketData PHP SDK** — a client library published to |
| 22 | +Packagist as `marketdataapp/sdk-php` and installed into consumers' applications |
| 23 | +via Composer. It runs on the consumer's machine (or their servers), not on |
| 24 | +MarketData infrastructure. The security concerns that matter here are therefore |
| 25 | +about how the library treats *its consumers*: |
| 26 | + |
| 27 | +- **Credential handling** — the caller's API token must never be logged |
| 28 | + verbatim, leaked in exception messages, or written to disk. The token is sent |
| 29 | + only in the `Authorization` header (never in query strings), request logging |
| 30 | + emits URLs without credentials, and the `headers()` utility partially redacts |
| 31 | + sensitive headers. Token resolution also reads the `MARKETDATA_TOKEN` |
| 32 | + environment variable and `.env` files (phpdotenv) — mishandling of those |
| 33 | + sources is in scope. Regressions in any of this are in scope. |
| 34 | +- **Transport security** — TLS is validated by default via Guzzle and the SDK |
| 35 | + exposes no skip-verify option. Anything that weakens this is in scope. |
| 36 | +- **Injection into outbound requests** — request-building that lets caller |
| 37 | + input smuggle headers, path segments, or query parameters it shouldn't. |
| 38 | +- **Response parsing safety** — the JSON decoding and typed-object hydration |
| 39 | + path (and the CSV/HTML format paths) handling hostile or malformed API |
| 40 | + responses without code execution, resource exhaustion, or crashes that a |
| 41 | + consumer can't defend against. Anything that routes response data into |
| 42 | + `unserialize()`, `eval()`, or dynamic includes is in scope by definition. |
| 43 | +- **Supply-chain integrity of the published package** — the release pipeline |
| 44 | + (`prepare-release.yml`, tag creation, and the Packagist sync from GitHub |
| 45 | + tags), and the dependency tree declared in `composer.json`. |
| 46 | + |
| 47 | +Out of scope: |
| 48 | + |
| 49 | +- **The MarketData API backend** itself. Report API/server vulnerabilities |
| 50 | + through the API's own channel, not here. |
| 51 | +- **Third-party dependencies.** Vulnerabilities in Composer-resolved |
| 52 | + dependencies (Guzzle, Carbon, phpdotenv, etc.) are tracked by Dependabot |
| 53 | + (see `.github/dependabot.yml`); report them upstream. We will bump the |
| 54 | + affected dependency here once a fixed version exists. |
| 55 | + |
| 56 | +## Security Fix Policy |
| 57 | + |
| 58 | +This policy governs how security fixes are applied to this repository, including |
| 59 | +fixes made by automated agents (e.g. Claude Code) working in the repo. It sorts |
| 60 | +every security fix into one of two tiers. |
| 61 | + |
| 62 | +The dividing line for a **library** is *consumer compatibility*. A fix that any |
| 63 | +consumer can pick up with `composer update`, with no source or behavior change |
| 64 | +on their side, is low-risk. A fix that forces consumers to change their code or |
| 65 | +adapt to changed runtime behavior is a breaking change and follows SemVer — |
| 66 | +those get the maintainer gate. |
| 67 | + |
| 68 | +### Tier 1 — Fix immediately (no approval needed) |
| 69 | + |
| 70 | +Security fixes that are **API- and behavior-compatible for legitimate |
| 71 | +consumers**. Existing callers keep working the same way after upgrading; only |
| 72 | +the vulnerability is closed. |
| 73 | + |
| 74 | +These may be fixed, tested, and committed right away. Every Tier 1 fix must be |
| 75 | +called out in its commit message, in `CHANGELOG.md`, and in the summary reported |
| 76 | +to the maintainer, so nothing ships silently. |
| 77 | + |
| 78 | +Typical Tier 1 fixes: |
| 79 | + |
| 80 | +- Tightening credential redaction, or plugging a token/secret/PII leak into |
| 81 | + logs or exception messages |
| 82 | +- Fixing injection in request building (header/path/query smuggling) where |
| 83 | + valid caller input is unaffected |
| 84 | +- Hardening the response-parsing path against malformed or hostile API |
| 85 | + responses (bounds, resource limits, null/missing-field handling) |
| 86 | +- Correcting a logic flaw in an existing security check without changing its |
| 87 | + public contract |
| 88 | +- Patching a vulnerable dependency by bumping to a compatible version within |
| 89 | + the existing `composer.json` constraints — no public API or behavior change |
| 90 | + for consumers |
| 91 | +- Hardening internal, non-public code paths (protected/private infra: |
| 92 | + transport, retry, logging internals) that consumers cannot observe or depend |
| 93 | + on |
| 94 | +- Fixing the release pipeline (CI workflows, tag/release automation) |
| 95 | + |
| 96 | +### Tier 2 — Requires maintainer approval first |
| 97 | + |
| 98 | +Any security fix that **breaks consumer compatibility or changes observable |
| 99 | +runtime behavior**. These must NOT be applied unilaterally. The agent or |
| 100 | +contributor stops, writes up the issue, the proposed fix, and the specific |
| 101 | +consumer impact, and waits for the maintainer's approval before proceeding. |
| 102 | + |
| 103 | +A fix is **Tier 2** if it does any of the following: |
| 104 | + |
| 105 | +- Removes, renames, or changes the signature of any **public** class, method, |
| 106 | + property, or parameter (a source-incompatible change — SemVer major) |
| 107 | +- Tightens input validation so that requests the SDK previously accepted are |
| 108 | + now rejected (could break existing callers) |
| 109 | +- Changes a user-visible default (timeouts, retry counts or backoff, |
| 110 | + rate-limit behavior, base URL, API version, token validation on client |
| 111 | + construction) |
| 112 | +- Changes an API/response contract — the shape of response objects, the |
| 113 | + exception types thrown (`ApiException`, `BadStatusCodeError`, `RequestError`, |
| 114 | + `UnauthorizedException`), or which exception a given failure raises — that |
| 115 | + consumers type-hint against or `catch` specifically |
| 116 | +- Raises the minimum PHP version (currently `^8.2`), tightens an existing |
| 117 | + dependency constraint in a way that can break consumers' dependency |
| 118 | + resolution, changes the package name, or otherwise forces a consumer to |
| 119 | + change their project to keep using the SDK |
| 120 | +- Adds a new required dependency to `composer.json` |
| 121 | +- Changes how the token is resolved (parameter → `MARKETDATA_TOKEN` env var → |
| 122 | + `.env` file) in a way that alters which credential an existing setup picks up |
| 123 | + |
| 124 | +### Classification rules |
| 125 | + |
| 126 | +- **When in doubt, it's Tier 2.** If it is unclear which tier a fix falls into, |
| 127 | + treat it as Tier 2 and ask for approval. |
| 128 | +- **No urgency exception.** Even for a critical, actively-exploitable |
| 129 | + vulnerability, a compatibility-breaking (Tier 2) fix waits for maintainer |
| 130 | + approval. Flag the urgency loudly, propose the fix, and wait. The maintainer |
| 131 | + is always the gate for changes that break consumers. (If a break is genuinely |
| 132 | + unavoidable to close a critical hole, that's a maintainer decision about |
| 133 | + cutting a major version — not an agent's.) |
| 134 | + |
| 135 | +### Release of security fixes |
| 136 | + |
| 137 | +Tiering governs *what* may be changed; the repo's normal release rules govern |
| 138 | +*what ships to consumers*. A Tier 1 fix may be committed to a branch and merged |
| 139 | +via the usual PR flow. **Publishing a release** — completing the release |
| 140 | +readiness gates in `release-readiness/`, cutting the `vX.Y.Z` tag, and letting |
| 141 | +Packagist pick it up — requires explicit maintainer confirmation, exactly like |
| 142 | +every other release (see `.github/RELEASE_PROCESS.md`). Automated agents never |
| 143 | +cut or publish a release on their own. |
0 commit comments