Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
93198ed
fix: loan/reservation coherence audit — clocks, availability, soft-de…
fabiodalez-dev Aug 10, 2026
620636c
fix: loan lifecycle follow-ups — #301 modal path, real timezone setti…
fabiodalez-dev Aug 10, 2026
547f700
fix(loans): admin UI fixes for issues #333, #334, #336
claude Aug 11, 2026
59b06e2
refactor(loans): canonical status badge partial; show cancelled loans…
claude Aug 11, 2026
627ee2a
refactor(loans): route every stato label through the canonical helpers
claude Aug 11, 2026
b4f4fd8
feat(mobile-api): align /me/loans history with web; additive status_l…
claude Aug 11, 2026
fc5cb4a
fix(loans): address CodeRabbit review on PR #337
claude Aug 11, 2026
77a76d6
fix(loans): address second-round CodeRabbit review on PR #337
claude Aug 11, 2026
78ac15b
test(loans): align history-predicate consistency guard with PR #337 s…
claude Aug 11, 2026
2a7a3d7
fix(loans): third-round CodeRabbit review — NUL-safe date validation,…
claude Aug 11, 2026
8cb7901
fix(loans): guard assigned copy during date edits
fabiodalez-dev Aug 11, 2026
7b680b0
fix(loans): fourth-round CodeRabbit review — French wording, structur…
claude Aug 11, 2026
585a33f
fix(loans): address discussion 238 workflow feedback
fabiodalez-dev Aug 11, 2026
61c7569
fix(loans): address CodeRabbit review findings
fabiodalez-dev Aug 11, 2026
3f72a68
feat(series): add complete series indicator
fabiodalez-dev Aug 11, 2026
284afff
fix: address adamsreview findings on PR #335 (2 groups, F007/F009/F033)
fabiodalez-dev Aug 12, 2026
080e1f2
fix: walkthrough findings F043/F012/F040/F028 (availability subject, …
fabiodalez-dev Aug 12, 2026
6625d7a
test(#301): drive reservation modal path via real JSON body + restore…
fabiodalez-dev Aug 12, 2026
faf9e85
fix(book-detail): reveal related-books scroll affordance on narrow/ta…
fabiodalez-dev Aug 12, 2026
f65bdce
merge: #340 complete-series indicator into 0.7.59-rc.1 integration
fabiodalez-dev Aug 12, 2026
f099598
merge: #335 loan/reservation coherence audit into 0.7.59-rc.1 integra…
fabiodalez-dev Aug 12, 2026
c5ebe62
merge: #337 issues 333/334/336 + status badge helpers into 0.7.59-rc.…
fabiodalez-dev Aug 12, 2026
d015299
merge: #339 scanner focus + natural copy ordering into 0.7.59-rc.1 in…
fabiodalez-dev Aug 12, 2026
2ecb4f3
chore(release): 0.7.59-rc.1 + fix series migration versioning
fabiodalez-dev Aug 12, 2026
d5cee9b
fix(plugins): self-heal orphan hook rows instead of logging every req…
fabiodalez-dev Aug 12, 2026
c76ecc3
fix(plugins): disable orphan hooks reversibly instead of deleting them
fabiodalez-dev Aug 12, 2026
127fbc6
test(loans): update stale reject-behavior E2E assertions to #335's an…
fabiodalez-dev Aug 12, 2026
d82c6f4
test: fix CI (self-sufficient plugin fixture) + address review follow…
fabiodalez-dev Aug 12, 2026
123d2b9
chore(release): bump version to 0.7.59-rc.2
fabiodalez-dev Aug 12, 2026
8cfdbb7
fix: harden release loan regressions
fabiodalez-dev Aug 12, 2026
46a1578
ci: add exhaustive regression and strict quality gates
fabiodalez-dev Aug 12, 2026
833c675
ci: pin reproducible frontend builds to Node 22
fabiodalez-dev Aug 12, 2026
3105151
ci: eliminate hidden skips and flaky fixtures
fabiodalez-dev Aug 12, 2026
a267961
ci: close review gaps with strict release gates
fabiodalez-dev Aug 12, 2026
7dbbd51
test: make import and seed fixtures fail closed
fabiodalez-dev Aug 12, 2026
5e076b8
test: make browser imports and scraping deterministic
fabiodalez-dev Aug 12, 2026
f19e297
ci: harden release gates and action supply chain
fabiodalez-dev Aug 12, 2026
4c611e5
test: activate interop plugins through real lifecycle
fabiodalez-dev Aug 12, 2026
f4bf6a9
ci: route deep tests to the database service
fabiodalez-dev Aug 12, 2026
6a32fbc
ci: prevent checkout credential persistence
fabiodalez-dev Aug 12, 2026
71b8b80
test: use real settings lifecycle and shared runtime permissions
fabiodalez-dev Aug 12, 2026
b747729
ci: verify and retry release baselines
fabiodalez-dev Aug 12, 2026
ab7a093
ci: install pinned PHPStan with resilient transport
fabiodalez-dev Aug 12, 2026
428e2f4
test: pass isolated database endpoint to maintenance cron
fabiodalez-dev Aug 12, 2026
535105d
test: make deep regression matrix deterministic
fabiodalez-dev Aug 12, 2026
3ac0f41
test: exercise cached settings through production path
fabiodalez-dev Aug 12, 2026
81c1700
test: isolate deep regression fixtures
fabiodalez-dev Aug 12, 2026
7e0c882
fix: harden upload and lock regressions
fabiodalez-dev Aug 12, 2026
102bf0c
test: supervise shared runtime regressions
fabiodalez-dev Aug 12, 2026
e8def0d
ci: authenticate release asset downloads
fabiodalez-dev Aug 12, 2026
96ecf7e
test: preserve shared upload ownership
fabiodalez-dev Aug 12, 2026
a1f0a68
test: remove navigation and lock races
fabiodalez-dev Aug 12, 2026
6c79ae0
test: respect persistent lock ownership
fabiodalez-dev Aug 12, 2026
fe58c4a
test: use application timezone for loan dates
fabiodalez-dev Aug 12, 2026
f9e6a89
test: align browser dates and timeouts
fabiodalez-dev Aug 12, 2026
26db424
ci: upgrade cache action runtime
fabiodalez-dev Aug 12, 2026
5d04264
test: avoid delayed Mailpit subject indexing
fabiodalez-dev Aug 12, 2026
6093b77
ci: harden release verification pipeline
fabiodalez-dev Aug 12, 2026
088ebae
ci: fix database credentials and workflow lint
fabiodalez-dev Aug 12, 2026
c28fd10
build: remediate frontend security advisories
fabiodalez-dev Aug 13, 2026
74aeb5c
fix: enforce database and accessibility gates
fabiodalez-dev Aug 13, 2026
7e0d11a
build: regenerate accessibility styles
fabiodalez-dev Aug 13, 2026
22870d0
test: report every accessibility route independently
fabiodalez-dev Aug 13, 2026
6a119e9
ci: use engine-aware database health checks
fabiodalez-dev Aug 13, 2026
8823d7b
fix: address review and accessibility findings
fabiodalez-dev Aug 13, 2026
10e5ff8
ci: prevent stale standalone browser assets
fabiodalez-dev Aug 13, 2026
241593e
ci: preserve canonical ZAP reports
fabiodalez-dev Aug 13, 2026
5e374c1
fix: harden CSP and stabilize mail regression tests
fabiodalez-dev Aug 13, 2026
b2132e2
fix: nonce legacy HTML responses
fabiodalez-dev Aug 13, 2026
97ccfb2
fix: secure framework error responses
fabiodalez-dev Aug 13, 2026
7cf47ff
chore(release): prepare 0.7.59-rc.3
fabiodalez-dev Aug 13, 2026
7fc28c5
ci: allow verified prereleases before merge
fabiodalez-dev Aug 13, 2026
25f417c
chore(release): finalize 0.7.59 (README highlights + changelog bounds)
fabiodalez-dev Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions .github/workflows/ci-browser-security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: Browser Compatibility and DAST

on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: '29 2 * * 4'
workflow_dispatch:

permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
browser-security:
name: Packaged app · 3 browsers · axe-core · OWASP ZAP
runs-on: ubuntu-latest
timeout-minutes: 75
permissions:
contents: read # Build and scan the checked-out application.

services:
mysql:
image: mysql:8.0@sha256:7dcddc01f13bab2f15cde676d44d01f61fc9f99fe7785e86196dfc07d358ae2b
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: pinakes_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1"
--health-interval=10s
--health-timeout=5s
--health-retries=12

env:
E2E_BASE_URL: http://localhost:8081
E2E_ADMIN_EMAIL: admin@pinakes.test
E2E_ADMIN_PASS: Test1234!
E2E_DB_HOST: 127.0.0.1
E2E_DB_PORT: '3306'
E2E_DB_USER: root
E2E_DB_PASS: root
E2E_DB_NAME: pinakes_test

steps:
- name: Checkout without persisted credentials
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false

- name: Install Apache and packaging tools
run: |
sudo apt-get update -q
sudo apt-get install -y apache2 libapache2-mod-php jq rsync unzip zip
sudo a2enmod rewrite headers env
php_module=$(find /etc/apache2/mods-available -maxdepth 1 -name 'php*.load' -printf '%f\n' | sed 's/\.load$//' | sort -V | tail -n1)
if [ -n "$php_module" ]; then sudo a2enmod "$php_module"; fi
sudo a2dissite 000-default || true

- name: Setup PHP 8.2 tooling
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.2'
extensions: mysqli, pdo_mysql, mbstring, curl, intl, xml, zip, gd
coverage: none

- name: Setup Node 22
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: '22'
cache: npm
cache-dependency-path: |
package-lock.json
frontend/package-lock.json

- name: Build the exact release artifact under test
run: |
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
npm ci --silent
npm --prefix frontend ci --silent
npm --prefix frontend run build
bash bin/build-release.sh --skip-build
version=$(jq -r .version version.json)
mkdir -p "$RUNNER_TEMP/pinakes-package"
unzip -q "releases/pinakes-v${version}.zip" -d "$RUNNER_TEMP/pinakes-package"
package_root="$RUNNER_TEMP/pinakes-package/pinakes-v${version}"
test -f "$package_root/public/index.php"
echo "PACKAGE_ROOT=$package_root" >> "$GITHUB_ENV"
echo "E2E_INSTALL_ROOT=$package_root" >> "$GITHUB_ENV"

- name: Install all supported Playwright browser engines
run: npx playwright install chromium firefox webkit --with-deps

- name: Configure Apache for the packaged application
run: |
path="$PACKAGE_ROOT"
while [ "$path" != / ]; do sudo chmod o+x "$path"; path=$(dirname "$path"); done
sudo chmod 777 "$PACKAGE_ROOT"
sudo chmod -R 777 "$PACKAGE_ROOT/storage"
sudo mkdir -p "$PACKAGE_ROOT/public/uploads"
sudo chmod -R 777 "$PACKAGE_ROOT/public/uploads"
sudo tee /etc/apache2/sites-available/pinakes.conf >/dev/null <<EOF
Listen 8081
<VirtualHost *:8081>
ServerName localhost
DocumentRoot ${PACKAGE_ROOT}/public
SetEnv PINAKES_E2E_BYPASS_RATE_LIMIT 1
SetEnv PINAKES_E2E_SCRAPER_STUB 1
<Directory "${PACKAGE_ROOT}/public">
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog \${APACHE_LOG_DIR}/pinakes-error.log
CustomLog \${APACHE_LOG_DIR}/pinakes-access.log combined
</VirtualHost>
EOF
sudo a2ensite pinakes
sudo apachectl configtest
sudo systemctl start apache2
for attempt in $(seq 1 30); do
curl -sf -o /dev/null http://localhost:8081/installer/ && exit 0
echo "waiting for packaged installer ($attempt/30)"
sleep 1
done
exit 1

- name: Install and bootstrap the packaged application
env:
PLAYWRIGHT_JSON_OUTPUT_FILE: test-results/package-bootstrap.json
PLAYWRIGHT_HTML_OUTPUT_DIR: playwright-report-bootstrap
run: npx playwright test tests/full-test.spec.js --config=tests/playwright.ci.config.js --workers=1

- name: Audit installer failures, flakes and skips
if: always()
run: node scripts/ci-audit-playwright-results.js test-results/package-bootstrap.json

- name: Test WCAG and runtime behavior in Chromium, Firefox and WebKit
env:
PLAYWRIGHT_JSON_OUTPUT_FILE: test-results/cross-browser.json
PLAYWRIGHT_HTML_OUTPUT_DIR: playwright-report-cross-browser
run: npx playwright test --config=tests/playwright.cross-browser.config.js --workers=1

- name: Audit cross-browser failures, flakes and skips
if: always()
run: node scripts/ci-audit-playwright-results.js test-results/cross-browser.json

- name: Run OWASP ZAP passive baseline scan
uses: zaproxy/action-baseline@de8ad967d3548d44ef623df22cf95c3b0baf8b25 # v0.15.0
with:
target: http://localhost:8081
docker_name: ghcr.io/zaproxy/zaproxy:stable@sha256:781a2bdaea47324e7bab583e2263f21d257b0aee61ed51521a5be45f5f5081ef
cmd_options: -a -m 3
fail_action: false
allow_issue_writing: false
artifact_name: zap-baseline-${{ github.run_id }}

- name: Fail on medium or high ZAP alerts
run: |
test -s report_json.json || { echo "ZAP JSON report is missing"; exit 1; }
blocking=$(jq '[.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2)] | length' report_json.json)
if [ "$blocking" -gt 0 ]; then
jq -r '.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2) | "[\(.riskdesc)] \(.alert): \(.desc)"' report_json.json
exit 1
fi
echo "ZAP found no medium/high passive-scan alerts"

- name: Upload browser and server diagnostics
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: browser-security-evidence-${{ github.run_id }}
path: |
playwright-report-bootstrap/
playwright-report-cross-browser/
test-results/
report_json.json
report_md.md
report_html.html
/var/log/apache2/pinakes-error.log
/var/log/apache2/pinakes-access.log
if-no-files-found: warn
retention-days: 21
144 changes: 144 additions & 0 deletions .github/workflows/ci-database-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
name: Database Compatibility

on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: '43 4 * * 3'
workflow_dispatch:

permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
database:
name: ${{ matrix.database.name }} schema and behavior
runs-on: ubuntu-latest
timeout-minutes: 35
permissions:
contents: read # Read application, schema and test sources.
strategy:
fail-fast: false
matrix:
database:
- name: MySQL 8.0 LTS baseline
image: mysql:8.0@sha256:7dcddc01f13bab2f15cde676d44d01f61fc9f99fe7785e86196dfc07d358ae2b
health_cmd: mysqladmin ping -h 127.0.0.1 -proot --silent
- name: MySQL 8.4 LTS
image: mysql:8.4@sha256:b3b90af2a6552ae30c266fdb7d5dd55f3afb72404bb78d37fe8a23eb857fd3fb
health_cmd: mysqladmin ping -h 127.0.0.1 -proot --silent
- name: MariaDB 10.11 LTS
image: mariadb:10.11@sha256:de61fed4a40d3842f3ee09944ba52792156cfd9adf489b2cc670fc6ded28df8d
health_cmd: healthcheck.sh --connect --innodb_initialized
- name: MariaDB 11.4 LTS
image: mariadb:11.4@sha256:67873d30a17f6a9c331f06363b2fa15f38abca415529966d67c84f87f82439fe
health_cmd: healthcheck.sh --connect --innodb_initialized

services:
database:
image: ${{ matrix.database.image }}
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: pinakes_test
ports:
- 3306:3306
options: >-
--health-cmd="${{ matrix.database.health_cmd }}"
--health-interval=10s
--health-timeout=5s
--health-retries=20

env:
E2E_DB_HOST: 127.0.0.1
E2E_DB_PORT: '3306'
E2E_DB_USER: root
E2E_DB_PASS: root
E2E_DB_NAME: pinakes_test
DB_HOST: 127.0.0.1
DB_PORT: '3306'
DB_USER: root
DB_PASS: root
DB_NAME: pinakes_test
CI_STRICT_TESTS: '1'

steps:
- name: Checkout without persisted credentials
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false

- name: Setup PHP 8.2
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: '8.2'
extensions: mysqli, pdo_mysql, mbstring, curl, intl, xml
coverage: none

- name: Install locked production dependencies
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader

- name: Wait for the database over TCP
run: |
for attempt in $(seq 1 45); do
if MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root -e 'SELECT VERSION()' 2>/dev/null; then
exit 0
fi
echo "database not ready ($attempt/45)"
sleep 2
done
exit 1

- name: Import the complete install baseline
run: |
MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root pinakes_test < installer/database/schema.sql
MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root pinakes_test < installer/database/data_it_IT.sql
MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root pinakes_test < installer/database/triggers.sql
cat > .env <<'ENVEOF'
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASS=root
DB_NAME=pinakes_test
ENVEOF
# data_it_IT.sql provides bundled plugin metadata, but the runtime
# registry is populated by PluginManager. Exercise that real install
# step so self-healing tests cannot silently skip book-club.
# shellcheck disable=SC2016
php -r '
require "vendor/autoload.php";
$db = new mysqli("127.0.0.1", "root", "root", "pinakes_test", 3306);
$db->set_charset("utf8mb4");
$hooks = new App\Support\HookManager($db);
(new App\Support\PluginManager($db, $hooks))->autoRegisterBundledPlugins();
'
plugin_count=$(MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root -Nse \
"SELECT COUNT(*) FROM pinakes_test.plugins WHERE name='book-club'")
test "$plugin_count" = 1

- name: Exercise all standalone behavioral tests in strict mode
run: bash scripts/ci-run-unit-tests.sh

- name: Exercise migration and plugin self-healing contracts
run: bash scripts/verify-schema.sh

- name: Verify schema invariants after all tests
run: |
table_count=$(MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root -Nse \
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='pinakes_test'")
trigger_count=$(MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root -Nse \
"SELECT COUNT(*) FROM information_schema.triggers WHERE trigger_schema='pinakes_test'")
default_languages=$(MYSQL_PWD=root mysql -h 127.0.0.1 -P 3306 -u root -Nse \
"SELECT COUNT(*) FROM pinakes_test.languages WHERE is_default=1")
test "$table_count" -ge 40
test "$trigger_count" -ge 1
test "$default_languages" = 1
echo "verified $table_count tables, $trigger_count triggers and one default language"

- name: Remove temporary database credentials
if: always()
run: rm -f .env
Loading
Loading