From 942d7d9d03fe935e0ed3103fa16a05a59dbb47f2 Mon Sep 17 00:00:00 2001 From: Sean Molenaar Date: Wed, 11 Dec 2024 14:29:39 +0100 Subject: [PATCH] Corona: JSON validation --- src/Lunr/Corona/JsonView.php | 83 ++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/src/Lunr/Corona/JsonView.php b/src/Lunr/Corona/JsonView.php index c5640e48..c2877869 100644 --- a/src/Lunr/Corona/JsonView.php +++ b/src/Lunr/Corona/JsonView.php @@ -10,6 +10,10 @@ namespace Lunr\Corona; +use _PHPStan_c875e8309\Psr\Container\ContainerInterface; +use JsonSchema\RefResolver; +use JsonSchema\Uri\UriRetriever; +use JsonSchema\Validator; use Lunr\Core\Configuration; use stdClass; use Throwable; @@ -19,17 +23,50 @@ */ class JsonView extends View { + /** + * Shared instance of the JSON URI retriever + * @var UriRetriever + */ + protected $jsonretriever; + + /** + * Shared instance of the JSON resolver + * @var RefResolver + */ + protected $jsonresolver; + + /** + * Shared instance of the JSON validator + * @var Validator + */ + protected $jsonvalidator; + + /** + * The list of calls to check and what to check them against + * @var array + */ + protected array $allowlist = []; /** * Constructor. * - * @param Request $request Shared instance of the Request class - * @param Response $response Shared instance of the Response class - * @param Configuration $configuration Shared instance of the Configuration class + * @param Request $request Shared instance of the Request class + * @param Response $response Shared instance of the Response class + * @param Configuration $configuration Shared instance of the Configuration class + * @param ContainerInterface|null $locator Shared instance of the ContainerInterface class */ - public function __construct($request, $response, $configuration) + public function __construct($request, $response, $configuration, $locator = NULL) { parent::__construct($request, $response, $configuration); + + if($locator !== NULL) { + $this->jsonretriever = $locator->get('jsonretriever'); + $this->jsonresolver = $locator->get('jsonresolver'); + $this->jsonvalidator = $locator->get('jsonvalidator'); + + $this->configuration->load_file('validation'); + $this->allowlist = $this->configuration['validation']['schemalist']->toArray(); + } } /** @@ -81,6 +118,11 @@ public function print_page() header('Content-type: application/json'); http_response_code($code); + if(in_array($code, $this->configuration['validation']['http_codes']->toArray())) { + $this->get_json_validated($json); + } + + if ($this->request->sapi == 'cli') { echo json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"; @@ -162,6 +204,39 @@ public function print_exception($e) } } + + /** + * Check if the JSON needs to be validated and do so if needed. + * + * @param array $json the response data passed by reference + * + * @return void + */ + private function get_json_validated(array &$json): void + { + + $schema_name = $this->request->controller . '/' . $this->request->method; + if(!array_key_exists($schema_name, $this->allowlist)) + { + return; + } + + $schema_path = $this->request->application_path . str_replace($this->request->base_path, '', $this->statics($this->allowlist[$schema_name])); + if(!is_readable($schema_path)) { + $json['status']['json_message'] = 'JSON schema was not found, not validated'; + return; + } + + $schema_file = $this->jsonretriever->retrieve('file://' . $schema_path); + $this->jsonresolver->resolve($schema_file); + $this->jsonvalidator->check(json_decode(json_encode($json)), $schema_file); + + if (!$this->jsonvalidator->isValid()) + { + $json['status']['json_message'] = 'JSON failed to validate against the schema'; + $json['status']['json_code'] = HttpCode::INTERNAL_SERVER_ERROR; + } + } } ?>