{"schema":"threatline.record/2","record":{"id":"GHSA-6xm9-p734-8fx6","aliases":["GHSA-6xm9-p734-8fx6"],"summary":"Public open-questions API returns hidden questions","details":"## Summary\r\n\r\nUnauthenticated `GET /api/v4.0/open-questions` returns questions marked `is_visible = N` when `api.onlyPublicQuestions` is **enabled** (the default). The HTML page `/open-questions.html` correctly hides those rows. The API does the opposite of the documented flag.\r\n\r\n## Description\r\n\r\n`OpenQuestionController::list()` reads `api.onlyPublicQuestions` and passes that boolean into `Question::getAll($showAll)`.\r\n\r\n`QuestionRepository::getAll()` treats `$showAll === true` as “do not filter on `is_visible`” and `$showAll === false` as `AND is_visible = 'Y'`.\r\n\r\nDefault config is `api.onlyPublicQuestions = true` (“API returns only public questions”). That becomes `getAll(true)`, which returns **all** rows, including hidden ones.\r\n\r\nThe public HTML helper queries `is_visible = 'Y'` only, so the website and the API disagree.\r\n\r\nThe existing unit test encodes the inverted call (`getAll` expected with `true` when the flag is `true`).\r\n\r\n## Root cause\r\n\r\n`phpmyfaq/src/phpMyFAQ/Controller/Api/OpenQuestionController.php`:\r\n\r\n```php\r\n$onlyPublic = (bool) $this->configuration->get('api.onlyPublicQuestions');\r\n// ...\r\n$this->question->getAll($onlyPublic);\r\n```\r\nphpmyfaq/src/phpMyFAQ/Question/QuestionRepository.php:\r\n```php\r\n$showAll === false ? \" AND is_visible = 'Y'\" : '',\r\n```\r\nShould be getAll(showAll: !$onlyPublic).\r\n\r\n## Proof of concept\r\n\r\nEnvironment: phpMyFAQ 4.2.0-beta, PHP 8.4, SQLite, API enabled (default).\r\n\r\n1. Insert one hidden and one public open question, language en.\r\n2. Leave api.onlyPublicQuestions=true (default).\r\n3. Unauthenticated:\r\n\r\nGET /api/v4.0/open-questions HTTP/1.1\r\nAccept-Language: en\r\n\r\nObserved: JSON data contains both rows, including isVisible: false and the hidden question text/username.\r\n\r\n4. GET /open-questions.html shows only the public question.\r\n5. Set api.onlyPublicQuestions=false and repeat step 3: API returns only the public row (flag is inverted).\r\n\r\n## Impact\r\n\r\nAny internet client can read open questions administrators marked non-public (moderation / internal asks) and the associated usernames. \r\n\r\n","categories":["Unclassified"],"product":"phpMyFAQ","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-6xm9-p734-8fx6","source_modified_at":"2026-10-03T11:36:16Z","fetched_at":"2026-10-24T14:26:37.433577Z","package":{"ecosystem":"","name":"phpMyFAQ"},"range":"< 4.2.0-alpha.2"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-6xm9-p734-8fx6","source_modified_at":"2026-10-03T11:36:16Z","fetched_at":"2026-10-24T14:26:37.433577Z","package":{"ecosystem":"","name":"phpMyFAQ"},"version":"4.2.0-beta"}],"cvss":5.3,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-03T11:36:16Z","modified_at":"2026-10-03T11:36:16Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-6xm9-p734-8fx6","fetched_at":"2026-10-24T14:26:37.433577Z","source_modified_at":"2026-10-03T11:36:16Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-03T11:36:16Z","content_hash":"e128606072a4284397cbeb1ae3bfc21133a143c4d27ff2bd85d69ee81844568e"}}
