Kaynağa Gözat

Runtime panel on / shows app version + creator; OIDC reads enabled/disabled

`views/home.twig`'s admin-only Runtime panel drops the PHP row; adds
"App version" and "Creator" sourced from a new `App\Meta::VERSION` /
`App\Meta::CREATOR` (keep in sync with the release tag + CHANGELOG
heading). OIDC row's value vocabulary changes from
configured/not-configured to enabled/disabled, matching the Local-admin
row so OIDC_ENABLED=false reads naturally. Gate (admin-only since
R01-N02) is unchanged; the leak-guard test now asserts neither the new
appVersion nor appCreator strings render for anonymous visitors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chiappa 2 gün önce
ebeveyn
işleme
5f6febf4c6
6 değiştirilmiş dosya ile 66 ekleme ve 7 silme
  1. 11 0
      CHANGELOG.md
  2. 7 1
      SPEC.md
  3. 3 0
      public/index.php
  4. 25 0
      src/Meta.php
  5. 14 3
      tests/Http/TwigViewTest.php
  6. 6 3
      views/home.twig

+ 11 - 0
CHANGELOG.md

@@ -25,6 +25,17 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
   `error_log` line when `APP_ENV=production` and neither OIDC nor
   `LOCAL_ADMIN_*` is enabled. Stops a fully unreachable instance from
   shipping silently after a misconfigured deploy.
+- **Admin-only Runtime panel on `/` swaps contents.** Drops the `PHP`
+  row; adds `App version` and `Creator` (sourced from new `App\Meta::
+  VERSION` / `App\Meta::CREATOR` constants — keep in sync with the
+  release tag and the latest CHANGELOG heading). The OIDC row's value
+  vocabulary changes from `configured` / `not configured` to `enabled` /
+  `disabled`, matching the Local admin row, so `OIDC_ENABLED=false`
+  reads naturally. No leak-surface change — the gate
+  (`currentUser is not null and currentUser.isAdmin` from R01-N02) is
+  unchanged, and `TwigViewTest::testHomeForAnonymousUserHidesRuntimePanel`
+  was updated to assert that neither the new `appVersion` nor
+  `appCreator` strings render for anonymous visitors.
 
 ## [0.22.0] — 2026-05-07
 

+ 7 - 1
SPEC.md

@@ -1197,7 +1197,13 @@ OIDC kill-switch (`OIDC_ENABLED=false`):
       stays public for liveness probes. New
       `TwigViewTest::testHomeForAnonymousUserHidesRuntimePanel` locks
       the behaviour in. Tests: 150 / 430 (was 149 / 424). First fix
-      from `doc/REVIEW_01.md`.
+      from `doc/REVIEW_01.md`. Subsequently the panel's contents shifted
+      (no behavioural / leak change): `PHP` was dropped, `App version`
+      + `Creator` (sourced from `App\Meta::VERSION` / `::CREATOR`) were
+      added, and the OIDC row's value vocabulary swapped from
+      `configured` / `not configured` to `enabled` / `disabled` — same
+      vocabulary the local-admin row uses, so `OIDC_ENABLED=false`
+      reads naturally.
 
 - [x] **R01-N11 — Whitelist column in `AuditRepository::distinctColumn`**
       (`4ae1817`). The private helper interpolated its `$col` argument

+ 3 - 0
public/index.php

@@ -24,6 +24,7 @@ use App\Controllers\UserController;
 use App\Controllers\WorkerController;
 use App\Db\Connection;
 use App\Db\Migrator;
+use App\Meta;
 use App\Http\FatalErrorHandler;
 use App\Http\Request;
 use App\Http\Response;
@@ -225,6 +226,8 @@ $router->get('/', function (Request $req) use ($view, $pdo, $users, $sprints, $a
         'schemaVersion'     => $schemaVersion,
         'dbPath'            => Connection::path(),
         'appEnv'            => $appEnv,
+        'appVersion'        => Meta::VERSION,
+        'appCreator'        => Meta::CREATOR,
         'oidcConfigured'    => OidcClient::isConfigured(),
         'localAdminEnabled' => LocalAdmin::isEnabled(),
         'authError'         => isset($req->query['auth_error']),

+ 25 - 0
src/Meta.php

@@ -0,0 +1,25 @@
+<?php
+/*
+ * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * See the LICENSE file in the project root for the full license text.
+ */
+
+declare(strict_types=1);
+
+namespace App;
+
+/**
+ * Single source of truth for human-readable app metadata surfaced in the
+ * UI (e.g. the admin-only Runtime panel on `/`). Bumped manually as part
+ * of the release commit — keep in sync with the matching `[x.y.z]` entry
+ * at the top of `CHANGELOG.md` and with the git tag.
+ */
+final class Meta
+{
+    public const VERSION = '0.22.0';
+    public const CREATOR = 'Alessandro Chiapparini';
+}

+ 14 - 3
tests/Http/TwigViewTest.php

@@ -51,6 +51,8 @@ final class TwigViewTest extends TestCase
             'schemaVersion'     => 3,
             'dbPath'            => '/tmp/x',
             'appEnv'            => 'production',
+            'appVersion'        => '0.0.0-test',
+            'appCreator'        => 'Test Creator',
             'oidcConfigured'    => false,
             'localAdminEnabled' => true,
             'authError'         => false,
@@ -80,6 +82,8 @@ final class TwigViewTest extends TestCase
             'schemaVersion'     => 3,
             'dbPath'            => '/tmp/x',
             'appEnv'            => 'production',
+            'appVersion'        => '0.0.0-test',
+            'appCreator'        => 'Test Creator',
             'oidcConfigured'    => false,
             'localAdminEnabled' => true,
             'authError'         => false,
@@ -105,6 +109,8 @@ final class TwigViewTest extends TestCase
             'schemaVersion'     => 3,
             'dbPath'            => '/tmp/x',
             'appEnv'            => 'production',
+            'appVersion'        => '0.0.0-test',
+            'appCreator'        => 'Test Creator',
             'oidcConfigured'    => false,
             'localAdminEnabled' => true,
             'authError'         => false,
@@ -124,6 +130,8 @@ final class TwigViewTest extends TestCase
             'schemaVersion'     => 3,
             'dbPath'            => '/var/data/app.sqlite',
             'appEnv'            => 'production',
+            'appVersion'        => '0.0.0-test',
+            'appCreator'        => 'Test Creator',
             'oidcConfigured'    => true,
             'localAdminEnabled' => true,
             'authError'         => false,
@@ -131,12 +139,15 @@ final class TwigViewTest extends TestCase
         ]);
 
         self::assertStringContainsString('Sign in with Microsoft', $html);
-        // R01-N02: the Runtime <details> panel must not leak PHP_VERSION,
-        // dbPath, schema version, OIDC/local-admin flags to anonymous visitors.
+        // R01-N02: the Runtime <details> panel must not leak app metadata,
+        // dbPath, schema version, or OIDC/local-admin flags to anonymous
+        // visitors. PHP version was removed from the panel entirely; the
+        // app-version + creator strings now live there in its place.
         self::assertStringNotContainsString('Runtime', $html);
         self::assertStringNotContainsString('Schema version', $html);
         self::assertStringNotContainsString('/var/data/app.sqlite', $html);
-        self::assertStringNotContainsString(PHP_VERSION, $html);
+        self::assertStringNotContainsString('0.0.0-test', $html);
+        self::assertStringNotContainsString('Test Creator', $html);
         // R01-N31 falls out of the same gate: no /healthz hint either.
         self::assertStringNotContainsString('/healthz', $html);
     }

+ 6 - 3
views/home.twig

@@ -114,8 +114,11 @@
     <details class="rounded-lg border bg-white p-4 dark:bg-slate-800 dark:border-slate-700">
         <summary class="text-sm font-semibold text-slate-700 uppercase tracking-wider cursor-pointer dark:text-slate-200">Runtime</summary>
         <dl class="mt-3 grid grid-cols-[max-content_1fr] gap-x-6 gap-y-1 text-sm">
-            <dt class="text-slate-500 dark:text-slate-400">PHP</dt>
-            <dd class="font-mono">{{ constant('PHP_VERSION') }}</dd>
+            <dt class="text-slate-500 dark:text-slate-400">App version</dt>
+            <dd class="font-mono">{{ appVersion }}</dd>
+
+            <dt class="text-slate-500 dark:text-slate-400">Creator</dt>
+            <dd class="font-mono">{{ appCreator }}</dd>
 
             <dt class="text-slate-500 dark:text-slate-400">APP_ENV</dt>
             <dd class="font-mono">{{ appEnv }}</dd>
@@ -127,7 +130,7 @@
             <dd class="font-mono">{{ schemaVersion }}</dd>
 
             <dt class="text-slate-500 dark:text-slate-400">OIDC</dt>
-            <dd class="font-mono">{{ oidcConfigured ? 'configured' : 'not configured' }}</dd>
+            <dd class="font-mono">{{ oidcConfigured ? 'enabled' : 'disabled' }}</dd>
 
             <dt class="text-slate-500 dark:text-slate-400">Local admin</dt>
             <dd class="font-mono">{{ localAdminEnabled ? 'enabled' : 'disabled' }}</dd>