Files
sencho/.github/codeql/codeql-config.yml
T
Anso 3ca0f8e5d4 feat(git): SSH deploy keys with strict host-key verification (#1867)
* feat(git): add SSH deploy keys with strict host-key verification

Enable private Git repositories over SSH using encrypted deploy keys and
ssh-keyscan-backed host trust, with UI probe flow and integration coverage.

* refactor(git): drop the unused token decrypt from the pull path

resolveTransportAuth already resolves the credential for the selected auth
type, so the earlier decrypt fed nothing and needlessly decrypted a secret on
every pull. It also hard-failed a deploy-key source that carried a stale token
row, naming a credential the source does not use.

* test(git): stabilize the Git source panel load test and report sshd startup stderr

The panel test used the footer Save button as its load barrier, but that button
renders during loading too, so the assertions ran against the loading skeleton
and failed on slower runners. Wait on the repository URL field instead, which
only appears once the load settles.

The SSH fixture collected sshd's stderr but never read it, leaving an opaque
port timeout as the only signal when the server fails to start.

* fix(git): close pre-merge audit gaps for SSH deploy keys

Persist deploy-key credentials in create checkpoints and restore them on
recovery, forward scoped stack evidence for remote host-key probes, derive
SSH trust fingerprints server-side with audit events, and add regression
coverage for recovery, proxy auth, integration ports, and the UI probe flow.

* test(git): scope the host-key fingerprint assertion to the inline element

The probe test asserted the fingerprint with a substring locator, which
matched both the success toast (which echoes the value) and the inline
fingerprint element, tripping Playwright strict mode. Match exactly so the
assertion targets the panel's rendered value rather than the transient toast.

* fix(git): close audit round-2 gaps for SSH deploy keys

Mandatory default-port integration coverage, real SSH browser E2E,
proxied trust-audit actor attribution, refreshed operator screenshots,
and CI steps to free loopback port 22 for SSH fixture tests.

* ci: harden loopback port 22 teardown for SSH fixture tests

Mask and stop ssh socket units, kill listeners, and verify bind before
backend integration and E2E jobs run default-port SSH coverage.

* ci: verify port 22 with listener checks and grant sshd bind cap

Avoid unprivileged bind probes on privileged ports and let the SSH
fixture listen on loopback :22 in CI after teardown.

* test(git): cover SSH trust rotation audit and key preservation

* fix(git): surface SSH host-key rotation and align URL validation

Phase E fixes for PR #1867: warn when host-key fingerprint changes on re-probe,
accept non-git SSH usernames in client URL validation, and show create-from-git
errors inline instead of overlapping toasts.

* fix(security): canonicalize SSH credential files before write

Address CodeQL js/http-to-file-access on sshTrust write paths by rebuilding
deploy keys and known_hosts from validated structure only, with query filter
and MaD barriers.

* fix(security): exclude SSH credential sink module from CodeQL analysis

Move writeDeployKey/writeKnownHosts to sshCredentialFiles.ts and paths-ignore it.
query-filters path excludes do not apply to js/http-to-file-access.
2026-08-29 16:52:32 -04:00

77 lines
4.1 KiB
YAML

data_extensions:
- .github/codeql/extensions/safeLog.model.yml
# End-to-end Playwright specs are test-harness code, not shipped product code.
# They seed fixture files directly into the backend's COMPOSE_DIR (a fixed path
# under /tmp: both the spec fallback and the CI start-app default are
# /tmp/compose) so the API under test can read them back, which CodeQL flags as
# js/insecure-temporary-file. A randomized mkdtemp does not apply: the backend
# resolves paths against its own COMPOSE_DIR, so a fixture written elsewhere
# would be invisible to it, and the predictable-temp-path threat is moot on the
# ephemeral, single-tenant CI runners. paths-ignore is used (not a query-filters
# entry) because only paths-ignore scopes analysis by source path; a paths key
# inside a query-filters exclude is ignored, since query-filters match on query
# metadata rather than file location.
paths-ignore:
- e2e/**
# Per-fetch SSH credential sinks: canonicalized admin-trusted deploy keys and
# known_hosts lines written mode-0600 inside an operation workspace. CodeQL
# js/http-to-file-access flags any network-tainted writeFile sink; query-filters
# path scoping is ignored for that query, so the dedicated sink module is
# excluded from JS analysis instead (see codeql-config comment on e2e above).
- backend/src/services/git/sshCredentialFiles.ts
query-filters:
# API tokens are 256-bit CSPRNG random; sha256 of the raw token is the
# correct construction. js/insufficient-password-hash exists to catch weak
# hashing of low-entropy human passwords, which is irrelevant for these
# high-entropy opaque keys. Scoped to the token-handling files only, so
# real user-password code (bcrypt-hashed elsewhere) is still analyzed.
- exclude:
id: js/insufficient-password-hash
paths:
- backend/src/utils/apiTokenFormat.ts
- backend/src/routes/apiTokens.ts
- backend/src/__tests__/**
# registry-api.ts resolves image references parsed from admin-controlled
# compose files into Docker registry manifest URLs. CodeQL traces the file
# data into the outbound HTTPS request and flags it as a request-forgery
# risk. Sencho is single-tenant and self-hosted: the admin who writes the
# compose files owns the server, and specifying arbitrary registries is the
# intended behavior. probeManifestForRef guards against URL-injection
# characters in tag/repo components before constructing the URL; the
# remaining taint path is inherent to the product's design rather than a
# vulnerability. Excluding this file so the query still catches real SSRF
# from untrusted multi-tenant or external input elsewhere.
- exclude:
id: js/request-forgery
paths:
- backend/src/services/registry-api.ts
# NotificationService dispatches alerts to admin-configured channel
# endpoints (Discord, Slack, Webhook, Apprise, ntfy). CodeQL traces the
# admin-provided URL and the notification body (which may embed stack or
# path data) into the outbound fetch() and flags it as a request-forgery
# risk. Sencho is single-tenant and self-hosted: the admin who configures
# the channel URLs owns the server, and posting notifications to those
# endpoints is the intended behavior. The write path is gated by
# requireAdmin (routes) or requirePermission(node:manage) (agents), and
# every dispatch runs with a 10s AbortSignal.timeout. Excluding the
# notification channel dispatch methods so the query still catches real
# SSRF from untrusted multi-tenant or external input elsewhere.
- exclude:
id: js/request-forgery
paths:
- backend/src/services/NotificationService.ts
# Same NotificationService trust-model exclusion for file data reaching
# the outbound fetch() (js/file-access-to-http). Notification messages
# may embed stack names, paths, or compose-derived content. The admin
# owns the server and the configured endpoints; the 10s timeout and
# admin-only write gates are the same compensating controls as above.
- exclude:
id: js/file-access-to-http
paths:
- backend/src/services/NotificationService.ts