Repository navigation
Release: merge development into beta - #834
Merged
Merged
Conversation
) * feat(generator): generate keys and passphrases in the browser, never on the server * feat(generator): passphrases, an admin switch for them, spec, docs and translations The generator dialog offers Password or Passphrase. A passphrase is 4 to 12 words from the EFF large word list, with a separator, capitals and a digit, and is extended to meet the org policy. Administrators can switch passphrases off. The change supersedes health-passphrase-generator, as that change's own D1 asked once generation moved to the browser, and row health-08 is built.
Both sides kept: the fill sends the tab host (#934) and the use-only flag, and a use-only copy is checked against that same host; the notifier runs its renderers as a list, so the emergency renderer (#931) and the access-end renderer both stay and prepare() stays simple; SecretService keeps the spec tag and #931's suppression; l10n is the union; info.xml keeps 20261002161000.
…t or face unlock (#921) * feat(ca): renew the root from the CA health section, behind a confirmation The renew-root endpoint had no caller, so an admin warned about the root's expiry could not act on it. The CA health section now has a renew root button that opens a confirmation dialog; only the confirm posts, and the result names how many suites were signed again. A controller test pins the admin-only posture of every CA write. Fixes #741 * fix(extension): apply the org password policy before saving a login The extension saved and updated logins without the strength floor or the breach block that the web app applies. The rules now live in src/policy/rules.js and src/health/hibpMatch.js, free of Nextcloud page imports, and the extension bundles the same code. The worker reads the same policy endpoint, refuses before encrypting, and the in-page bar explains the refusal instead of offering a save. Refs #746 * feat(leases): view and set an application's lease policy on its detail page The per-application lease-policy override had a PUT and no screen. A new GET /api/v1/applications/{id}/lease-policy answers the policy in force, the stored override and the instance values, for an admin or the application's registrant (404 for anyone else). The leases panel now shows a form: an admin saves the override, an empty field inherits the instance value, and the registrant sees it read-only. Refs #753 * fix(extension): post the breach-check prefix in the body, as the route now expects Development moved the range route to POST with the prefix in the body (keepiq#866). The extension's new lookup follows it, with a test that the prefix never appears in the URL. Refs #746 * feat(extension): idle lock maximum endpoint and per-client passkey credentials GET /api/v1/extension/policy returns the administrator's maximum idle lock delay for the browser extension (default 240 minutes). Passkey credentials gain client_kind and rp_id, and the login options are scoped to the asking client, so the web app never gets an extension credential and the reverse. Refs #784 * feat(extension): several accounts, per-account locks, and a sender check on worker messages The extension holds up to five paired accounts, each with its own key, idle timer and match cache. The old single pairing becomes the first account. Matching, filling, the one-time code and saving use the active account, and the worker refuses a fill whose id did not come from the active account's own match for the current site. Only the four content-script messages are accepted from a tab; everything else needs an extension page as sender. The idle delay is per account, capped at the organisation maximum read at each unlock. Refs #784 * feat(extension): fingerprint or face unlock, account switcher and idle delay settings The unlock window enrols a platform passkey with PRF for one account (the extension's own origin as relying party, user verification required) and unlocks with it: the raw unlock key is unwrapped in the window and handed to the worker, which imports the key and drops the bytes. The server gets only the wrapped key, the PRF salt and the credential metadata. The option shows only where a user-verifying platform authenticator exists. The popup header switches between accounts and shows each one's lock state; a settings view picks the idle delay, with delays above the organisation's maximum shown as unavailable. Refs #784 * feat(extension): admin idle maximum field, extension passkeys in the web list; archive the change The admin settings get a Browser extension section for the longest idle delay users may pick. The web app's passkey list labels extension credentials, which the owner revokes there as any other. The change clients-extension-unlock-lock-and-accounts is archived: its spec deltas are synced into openspec/specs and the parity rows clients-06, clients-21 and crypto-09 are set to built. Closes #784 * chore(quality): reasoned complexity suppression on the admin settings service, @SPEC on the extension section
…nt-libraries-and-ci
Keeps both notifier families (emergency and device approval) in the renderer list, both App.vue mounts, info.xml at 163000, and adds device_approvals to the backup table registry.
…and-ci feat: client libraries for Go, Python and TypeScript, GitHub Action and GitLab CI template
…w-device-approval # Conflicts: # appinfo/info.xml # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json # src/views/settings/Settings.vue
…w-device-approval
The extension's handlers moved to router.js (#921), so the use-only match filter, fill refusal, owner report and save-prompt block move there too. info.xml goes to 181000, past development's 180000.
…ackend)
Register five concrete admin settings classes (General, Policies,
Applications and machine access, People and offboarding, Audit and
compliance) in Keepiq's own container, so a Nextcloud delegation of a
class satisfies the #[AuthorizedAdminSetting] guard that names it.
Split the admin settings endpoint per area (decision of 2 Oct): one GET
and one PUT per area under /api/settings/admin/{general,policies,
applications,audit}, each guarded by its own area class and writing only
that area's keys; a key of another area answers 400. Version and trash
retention belong to Policies. Move the attribute guards and the inline
isAdmin checks to AdminAreaAuthorizer::holds(), and route admin handover
and team offboarding through the People area (vault_admin stays an alias).
Refs #774
…export (#936) * feat(siem): connector columns on SIEM sinks Version001004Date20261002170000 adds format (default json), credential_enc and connector_options to keepiq_siem_sinks; app version 0.3.4-unstable.20261002170000. SiemSink serializes format, connectorOptions and hasCredential, never the credential. Refs #782 * feat(siem): accept Splunk HEC and Sentinel sinks and CEF syslog SiemSinkService accepts splunk_hec and sentinel, requires https:// endpoints and a credential for them, validates connector options per type (Sentinel tenant, client, data collection endpoint and rule required; stream and authority host defaulted; https authority), and allows format cef on syslog only. The credential is encrypted with ICrypto and write-only (blank keeps it). The sink update audit names the connector type. The controller passes format, credential and connectorOptions. Red before, green after: SiemSinkServiceConnectorTest. Refs #782 * feat(siem): JSON, CEF, Splunk HEC and Sentinel formatters Pure formatters under lib/Service/Siem read the payload only through PayloadView, which returns the eight known fields and drops every forbidden metadata key again. CEF escapes header and extension values and takes severity from a fixed category map. A guard test plants every forbidden key and an extra top-level field and fails if any formatter output carries them or any value that is not from the payload or a fixed vendor constant. Refs #782 * style(siem): named arguments and spec anchors in the formatters Refs #782 * feat(siem): Splunk HEC and Sentinel delivery, CEF over syslog SiemTransport sends a cef syslog sink's CEF line in the existing RFC 5424 frame and a json one byte for byte as before. Splunk HEC posts one event with Authorization: Splunk <token> and accepts only HTTP 200 with code 0. Sentinel gets a client-credentials token for scope https://monitor.azure.com//.default, holds it in the process for the run, posts one row to the data collection rule stream, accepts 204 and retries once with a new token after a 401. Every failure goes through the existing retry and dead-letter path; test-fire works for every connector. Tested over a real TCP socket and a mocked HTTP client. Refs #782 * feat(siem): connector picker on the SIEM section The admin form picks Splunk HTTP Event Collector, Microsoft Sentinel, CEF over syslog, Syslog JSON or Webhook JSON and shows only the fields that connector needs. The credential field is write-only: never prefilled, blank keeps the stored one. siemConnectors.js holds the mapping, validation and request body; vitest covers the field set per connector, the body, and the empty credential on edit. New strings in every catalogue. Refs #782 * test(siem): Playwright flow for a Splunk HEC sink and its test-fire Refs #782 * test(siem): do not depend on translated quote marks in the e2e Refs #782 * feat(siem): Sentinel template, Splunk sourcetype and connector docs integrations/siem/sentinel/keepiq-dcr.json creates KeepiqAudit_CL and the data collection rule with stream Custom-KeepiqAudit; a test keeps its columns equal to SentinelRowFormatter. integrations/siem/splunk holds props.conf for keepiq:audit. Both READMEs name the least privilege each credential needs; docs/siem-connectors.md covers every connector. Refs #782 * docs(openspec): archive audit-siem-vendor-connectors Ticks every task with its test, syncs the siem-vendor-connectors spec, points the @SPEC and @e2e anchors at it, and sets parity row audit-14 to built. Closes #782 * refactor(siem): request object and connector settings class SiemSinkController create() and update() read the sink fields through SiemSinkRequest instead of eleven router-bound arguments; the HTTP contract and defaults are unchanged (SiemSinkRequestTest). Connector validation moves from SiemSinkService into SinkConnectorSettings, with the Sentinel rules in their own method, and updateSink() delegates the generic fields to applyGeneralChanges(). SiemService::updateSink() declares the InvalidArgumentException the controller catches. Spec tags on the picker binding; prettier and eslint on the SIEM files. Refs #782
…t stopped the agent
…949) Four EFF words carry a hyphen (drop-down, felt-tip, t-shirt, yo-yo), so splitting a random default passphrase on '-' failed now and then. The default is checked with a fixed random source; the content checks use a space as separator.
Version001008Date20261002181000, so it sorts after #921's migration.
…on of 2 Oct) Design, spec and tasks now describe one settings route pair per area, the concrete area classes registered by Keepiq itself, one initial-state key and mount element per area, and version and trash retention under Policies. Tasks 1.1, 1.3, 2.1 to 2.5 and 3.1 to 3.3 are done; 1.2 and 3.4 need a live instance, 4.1 is due one minor release later. Refs #774
…se-only-and-expiring-shares # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json
Version001009Date20261002182000 and 0.3.4-unstable.20261002182000; the previous merge had kept development's 180000, which would skip the upgrade.
…ped-roles # Conflicts: # appinfo/info.xml # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json # lib/Controller/SiemSinkController.php # lib/Service/AdminSettingsService.php # src/views/settings/Settings.vue
* feat(offline): baseUpdatedAt precondition on secret update and delete, offline edits setting
PUT and DELETE /api/v1/secrets/{id} accept an optional baseUpdatedAt. When
the secret changed since, nothing is written and the answer is 409 with the
current row. The admin setting offline_edits_enabled (default off) travels in
the offline manifest as offlineEditsEnabled.
Refs #785
* style(offline): named argument for StaleWriteException
* feat(offline): sealed edit queue store with coalescing
A queue store in the offline IndexedDB database (schema version 2). Each
entry's values and index fields are sealed to the owner's own certificate
with the hybrid envelope; only ids, the operation, the base version, the
queue time, the suite and the status are plain. Changes to one secret
coalesce. The snapshot now carries each secret's updatedAt and the
administrator's offline edits rule.
Refs #785
* feat(offline): queue offline edits and replay them with a fresh fan-out
While served from the cache with offline edits on, the secret store queues
creates, edits, moves and deletes instead of calling the server, and the
cached view shows them at once. Back online and unlocked, the queue replays
oldest first: owner rows through POST, PUT and DELETE with the base version,
then the recipient fan-out with shares and certificates fetched at that
moment. A 409 waits for the user's choice, 403 and 404 go to a failed list,
423 and network errors stop and keep the rest, and a failed recipient sync
retries only that step. A compromise recovery waits until the queue is empty.
Refs #785
* style(offline): lint
* feat(offline): offline edit screens, sync panel, conflict dialog and admin switch
Offline with offline edits on, the secret sidebar offers edit, move and
delete, New secret stays available, and queued changes show as not synced
yet. Sharing and attachments stay online-only and say so. A sync panel
under the banner shows how many changes wait, lets the user settle a
conflict (both versions decrypted in the browser), copy or discard a
refused change, and reopen changes made before a key change elsewhere with
the previous master password. Leaving the page with pending changes asks
first. The admin offline cache section gets the offline edits switch.
Refs #785
* docs(openspec): archive clients-offline-edits
Spec deltas synced: the new offline-edit-queue capability, and the
strictly read-only requirement removed from offline-readonly-cache. Parity
row clients-19 set to built. Code tags that named the removed requirement
point at the online-only requirement that replaces it.
Closes #785
* refactor(offline): OfflineEditGuard, a separate update controller, @SPEC on the queue methods
The baseUpdatedAt precondition moves out of SecretService into a small
OfflineEditGuard, used by update() and by findOwned() for the trash path,
so SecretService keeps its method count and the trash service needs no
static call. PUT /api/v1/secrets/{id} moves to SecretUpdateController,
which reads the changed fields from the request, so SecretController's
coupling and update()'s parameter list stay within the limits. The offline
switches leave getAdminSettings() for their own reader. Seven queue methods
get their @SPEC tag.
Refs #785
…w-device-approval # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json # lib/Service/AdminSettingsService.php
feat: Kubernetes operator for Keepiq application secrets (stacked on #914)
…et-sync-and-rotation-runner
…red by their own registrar Move the per-area settings routes to AdminAreaSettingsController (with a user rate limit on each write), report adminAreas from SettingsController instead of SettingsService, register the area classes in AdminAreaRegistrar, and name areas through AdminAreaAuthorizer constants where a controller was over the coupling limit. Place development's new extension idle setting in the Policies area. Refs #774
…ped-roles # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json
* feat(mobile): fdroid and play flavours, free-software check and reproducible build in CI Tasks 6.1 and 6.2 of clients-mobile-apps. The flavours differ only in the update check, and neither has one: Play Core is not free software, and both stores update the app themselves. mobile-fdroid.yml fails on any non-free artifact in the fdroid or play release runtime classpath and builds the unsigned fdroid release APK twice, in two clean checkouts at different paths, and compares them byte for byte. Build tools pinned, vcsInfo off. The preview, e2e and unit test workflows use the flavoured task names. * feat(mobile): store texts and screenshots for F-Droid and Google Play (draft) Task 6.3. English and Dutch title, short and full description and the first changelog in mobile/fastlane/metadata/android, with eight screenshots from docs/static/media/mobile. Draft for the product owner's review. * docs(mobile): privacy policy for the mobile apps, FEATURES.md to current state Task 7.3. docs/mobile/privacy.md says what the app sends where (your Nextcloud, the server of a Send link you open, a website's public assetlinks.json when another app asks for a login) and what it stores. using.md links it. * docs(mobile): developer README Task 7.2. Layout, building the shared core, Android and iOS, the crypto vectors, the e2e harness, the flavours and the F-Droid rules. * test(mobile): control, a Google Play Services dependency in the fdroid flavour Must make the free-software job of mobile-fdroid.yml fail and name the artifact. Reverted in the next commit. * Revert "test(mobile): control, a Google Play Services dependency in the fdroid flavour" The control has run (mobile-fdroid.yml run 37354683074); the fdroid flavour is free software again. * docs(openspec): tick mobile tasks 6.1, 6.2, 6.3 (draft), 7.2 and 7.3 with their CI runs
Contributor
Author
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 114/114 | |||
| npm | ✅ | ✅ 660/660 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-05 22:19 UTC
Download the full PDF report from the workflow artifacts.
- AdminAreaSettingsTest::testInfoXmlRegistersEveryArea read info.xml
with simplexml_load_file(). Under the Nextcloud bootstrap in CI,
lib/base.php blocks libxml's external entity loader, which
simplexml_load_file() also uses to open the file, so it got false and
failed on iterator_to_array(null). It now parses the file contents,
as NextcloudFloorMatrixTest and ConnectionsDeclarationTest already do.
- admin-api.postman_collection.json declared only its auditor variables,
so all 23 requests went to the literal `{{baseUrl}}` (getaddrinfo
ENOTFOUND). It now declares baseUrl, noAuthBase, adminUser and
adminPass with the same values as the other two collections.
Both failures predate this branch (#1173 shows the same).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
phpunit.xml sets beStrictAboutCoverageMetadata and failOnRisky, so a test that runs a class its @Covers does not declare is risky, and a risky test fails the run. Ten tests did (the same ten on development), which was hidden while AdminAreaSettingsTest errored first: - ExtensionControllerTest uses AdminSettingsService and PasswordPolicyService. - SettingsControllerConnectionRefreshTest uses AdminAreaSettingsController. - SiemConnectionReportCallersTest uses Siem\SinkConnectorSettings. Each @uses names exactly the classes CI reported for that test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(app-shell): Keepiq runs without any other app
Contributor
Author
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 114/114 | |||
| npm | ✅ | ✅ 660/660 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-06 08:03 UTC
Download the full PDF report from the workflow artifacts.
…nment Closes the gaps of #863/#864 left on development (keepiq#1189): - A share-target lookup that throws while resolving a copy's source is counted as a containment failure; the source owner was not warned but the cascade reported complete. - When revoking the other end of a migration throws, the named suite is already revoked: its containment now runs in a finally on the radius collected before any revoke. Before, it was skipped, and a retry collected after the revoke and lost the source owners. - The containment tally is audited as suite.compromise_contained, flagged incomplete when a step failed, instead of reaching only the response. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PARAM_DATE is deprecated since NC 31 and is defined as Types::DATETIME_MUTABLE, exactly what PARAM_DATETIME_MUTABLE is, so this is a pure rename. All bound values are \DateTime and the compared columns (keepiq_audit_log.occurred_at, keepiq_ca_certs.expires_at) are Types::DATETIME. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M7DxSg7d7wqqZsUppLWf7J
…not refused Self-review of #1190: - A failure to revoke the other end or end the migration, after the named suite was revoked and contained, fell into forceRevoke's RuntimeException branch: a 403, a suite.revoke_refused (forbidden) audit for a revoked suite, and the containment tally dropped. It is now caught, counted as a failed step, audited as migrationEndFailed and answered with a 500 `migration_end_failed` that carries the tally. - A retry on a suite left revoked with its migration open only finishes the migration. Only a compromise force-revoke revokes a migrating suite, so its containment already ran; running it again warned everyone twice. - A failed source lookup no longer also attempts the outbound lookup, so it counts as one failure. - The admin force-revoke moved into AdminSuiteRevocationService; the controller had outgrown phpmd's class-length and parameter limits. - Tests pin the exact failure status, body and audit, assert no refusal is recorded, and cover a failed named revoke and a retry that fails again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
refactor(nc35): replace deprecated IQueryBuilder::PARAM_DATE
…te containment Re-review of #1190: - The retry shortcut took "revoked with the migration still open" as proof that containment ran. An attempt can stop between the revoke and contain() (a throw after the status update, a killed worker, a failed audit write), and every later retry then skipped containment and answered success. The shortcut now requires a suite.compromise_contained audit entry for the suite since the migration started, with no failed containment step. No entry, a failed step or an unreadable trail runs the whole path again, the revoke included, so a revoke cascade that stopped is redone too. - The migration_end_failed message no longer claims containment when containment steps failed; the admin UI renders the result and cascade cards for that 500 instead of only the message. - A retry whose migration end fails again is recorded as suite.revoke_refused with reason code migration_end_failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(mobile): give the apps Keepiq's lock as launcher and store icon Android: an adaptive icon (brand blue background, the lock from img/pwa-icon-maskable.svg as a VectorDrawable foreground inside the 66 dp safe zone, the same layer as the monochrome icon for themed icons), legacy PNG mipmaps, and android:icon/roundIcon on the application. Store: a 512 px icon.png for en-US and nl-NL in the fastlane metadata. iOS: an AppIcon asset catalog with one 1024 px icon. * fix(mobile): pad the phone screenshots to Google Play's 2:1 limit The screenshots were 2.22:1 and Play takes at most 2:1. Each one is padded evenly left and right, each padding row filled with that row's edge pixel, to 557x1100 (1-7) and 1214x2400 (8), 1.98:1. No content is cut, and the one set in the fastlane layout serves F-Droid and Play alike. * docs(openspec): note the store icon and Play-sized screenshots under task 6.3 * fix(mobile): draw the legacy launcher PNGs straight from img/pwa-icon.svg
…mpt after Keepiq's forms (tasks 1.6.1, 3.2.1) (#1184) * feat(mobile): encrypted offline store on iOS (task 1.6.1) SQLCipher Community Edition 4.19.0, built from the pinned release source (SHA-256 checked) into the KeepiqShared klib per iOS target, linked by the SQLDelight native driver instead of the system SQLite. The database key is 32 random bytes per account in a non-biometric Keychain item (AfterFirstUnlockThisDeviceOnly, through KeychainStorage). The store refuses to open when SQLCipher is missing (PRAGMA cipher_version), and a file whose key is gone starts again empty. Unpair deletes file and key; suite change and offline caching off empty it through VaultSync, as on Android. The iOS app opens its sessions through IosVaultSession with the store and sync; the lock and wrap deletion of a sync reach AppModel. Tests: the store on the iOS simulator (IosEncryptedStoreTest) and OfflineUITests, which reads the stored vault after the replay drops every connection (POST /__e2e/offline). * fix(mobile): no "Save Password?" after Keepiq's own iOS forms (task 3.2.1) iOS reads the user name and password fields when they leave the view hierarchy and offers to save what it finds (WWDC 2018 session 204: "Make sure to only clear the username and password fields after they've been removed from the view hierarchy. This way, we can read out the data and save it into credential."). The item form, the Send form and the AutoFill sheet's add-login form now empty their secret fields once the save went through, wait a moment for the fields to update, and only then leave. The fields stay SecureFields, so VoiceOver announces them as secure as before. VaultFlowsUITests.expectNoSavePasswordPrompt fails again when the prompt comes; AutofillUITests records whether it came after the add-login form. * test(mobile): opt in to the foreign API in the iOS store test * fix(mobile): MobileSession keeps its store to close it on lock * test(mobile): answer the save prompt before the offline test types * build(mobile): name each iOS simulator test in the log * test(mobile): the offline test taps until the field has focus, five tries
…proven records Third review pass of #1190: - The audit fake now asserts the exact lookup (this event, object type, suite and the migration's start, limit 1), and the migration fixture has a start, so dropping or changing a filter fails the tests. - A record without an integer failed count no longer passes as complete: {"migrationEndFailed": true} computed 1 - 1 = 0 and skipped containment. Found by the new data provider (no metadata, empty, a failed step, no count); all of them now contain again. - A migration without a start contains again instead of querying with no time bound. - AuditEntryMapperTest runs the real findFiltered query: an entry before the migration, another event type and another suite are excluded, and the newest match is returned. It skips without a database, like SecretRequestMapperTest, and runs in CI. - The store only treats a 500 as migration_end_failed; the store and controller docblocks no longer say the suite was contained. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nment fix(encryption-suites): count, always run and audit compromise containment
The 17 alerts GitHub Advanced Security reports on development (and on the development -> beta release PR #834): - actions/missing-workflow-permissions (12): the repository's default token is write-all. Every flagged workflow now declares its permissions: contents: read for the self-contained ones, contents: write only on the cli-release jobs that attach to a release, and for callers of the shared ConductionNL/.github workflows exactly the union the called jobs declare (release, sync-to-beta: contents + pull-requests write; documentation: contents + packages write; issue-triage, openspec-sync: issues write). - js/polynomial-redos (sdk/js): the trailing-slash strip of the caller's address backtracked quadratically on a long run of slashes; replaced by a loop, with a test for an address with trailing slashes. - js/incomplete-url-substring-sanitization (3, extension tests): the assertions compared URL prefixes, which also matched other hosts; they now compare the parsed origin. - js/insecure-randomness (e2e helper): the run-unique user name suffix uses crypto.randomInt instead of Math.random. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 114/114 | |||
| npm | ✅ | ✅ 660/660 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-06 14:05 UTC
Download the full PDF report from the workflow artifacts.
fix(ci): resolve the open code-scanning alerts on development
Contributor
Author
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 114/114 | |||
| npm | ✅ | ✅ 660/660 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-06 14:42 UTC
Download the full PDF report from the workflow artifacts.
The generic Reports entry leaves the footer for the settings foldout, which nextcloud-vue now labels Advanced.
…enable The push runs on development test Nextcloud stable32-34; pull requests only run stable35, so these failures never showed on a PR. - FederatedSharesMigrationTest and SiemConnectorColumnsMigrationTest mocked stdClass when ISchemaWrapper::getTable() declares no return type (Nextcloud 34 and older), and stdClass has no hasColumn(). They now use a plain recording table there, as ConsolidatedSchemaMigrationTest does. The migrations were fine: getTable() returns a Doctrine Table there. - The federation tests that use the OCM APIs of Nextcloud 33 skip on 32, where FederationRootService::isSupported() is false and federation is off by design. The skip uses the same check. - The standalone and two-instances setups hit a transient 404 on the first API call right after app:enable, once each on 4934a64 (and green on the other run of the same commit). They now wait for the API first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 114/114 | |||
| npm | ✅ | ✅ 660/660 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-07 08:57 UTC
Download the full PDF report from the workflow artifacts.
The same three failed, retry included, on every E2E run since at least 4f942f7: - l10n-browser-catalogue picked the first entry whose value differs from its key as the t() probe. Plural entries (`_singular_::_plural_`) hold an array of forms, so they always qualified, and t() cannot resolve them. The probe now takes a plain string entry. - member-overview-offboarding expected "1 team folders."; the summary is pluralised now and says "1 team folder.". - vault-unlock walks every manifest route while locked. Each goto is a full page load, about 4.6 s a route in CI (measured from the trace), so the 17 routes ran out of the default timeout at route 15. The timeout now scales with the number of routes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With its timeout fixed, the locked-vault leak test reaches its assertion for the first time since device approval came to the lock screen (#1024), and finds two requests the lock screen itself makes: - device-approvals/status: checkSuite() asks, whenever online, whether to offer "Approve from another device". Answers {enabled}. - recovery/enrolment: with device approval on, DeviceApprovalRequest reads the user's own enrolment to decide whether to offer the administrator path. Answers {enrolled, policy}. Neither carries vault material. Run locally with Playwright against an instance with device approval enabled: all three specs fixed on this branch pass (10 passed, 1 skipped), and the development versions of the l10n and offboarding specs fail exactly as in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(ci): green PHPUnit on Nextcloud 32-34 and Playwright on development
Contributor
Author
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 114/114 | |||
| npm | ✅ | ✅ 660/660 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-07 11:34 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated PR to sync development changes to beta for beta release.
Merging this PR will trigger the beta release workflow.