mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-10-26 21:47:47 +00:00
9d0dbebbfa
* feat(gitops): SOPS/age repository secrets without durable plaintext Add age-only SOPS detection, node-scoped identity management, and a short-lived decrypt overlay for Git validate, apply, deploy, and recovery paths. Ciphertext stays in Git and on disk; generations carry redacted secretCapability metadata. * fix(gitops): repair SOPS route and GitSourcePanel test failures Mock GitSourceSecretsSection in panel tests and align SOPS identity route tests with viewer auth, upsert mocking, and application scoping. * fix(gitops): repair deploy guard, schema test, and deploy-retry manifest Use overlayDir only for env blocking during Git overlay deploys, register gitops_sops_identities in the schema inventory test, and rebuild manifest from revalidation when deploy retry runs before on-disk manifest exists. * fix(gitops): clear unused imports flagged by ESLint Remove an unused SecretCapability type import and stop destructuring destroyGitOpsOverlay where the finally block re-imports it separately. * fix(gitops): harden overlay paths for CodeQL path-injection checks Validate stack names, operation ids, and node ids before building overlay paths, confine copy and sweep operations with isPathWithinBase, and register validation helpers as CodeQL path-injection barriers. * fix(gitops): add inline CodeQL path barriers on overlay sinks Use path.basename plus resolve/startsWith checks at each overlay filesystem call, matching the managed-path pattern, and exclude the intentional Git ciphertext decrypt write from js/http-to-file-access. * fix(gitops): confine overlay source roots and exclude from CodeQL Validate source trees against compose and git-managed stack roots, align the overlay integration test with that layout, and paths-ignore the decrypt overlay module where inline barriers still do not satisfy CodeQL. * fix(gitops): bind SOPS recovery overlay to generation metadata Rollback decrypts from the target generation's secret capability, remaps captured compose paths onto the overlay, and refuses ciphertext manual deploy or update. Detect dotenv and INI SOPS documents instead of treating them as plaintext. * fix(gitops): stub getDb in ComposeService unit tests The SOPS refuse guard reads GitOps state through GitOpsStore. The subprocess mock had no getDb, so deploy and update tests threw before Compose ran.
114 lines
6.6 KiB
YAML
114 lines
6.6 KiB
YAML
data_extensions:
|
|
- .github/codeql/extensions/safeLog.model.yml
|
|
- .github/codeql/extensions/validation.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
|
|
# redirectPreflight walks an HTTPS redirect chain to decide whether git may
|
|
# be re-run against the destination, so the operator's configured repository
|
|
# URL reaches an outbound https.get and CodeQL flags request forgery. A
|
|
# barrier model on the module's own origin-check function (declaring its
|
|
# return value clean, the same mechanism sanitizeForLog uses for
|
|
# log-injection) was tried first and did not clear this alert: the
|
|
# js/request-forgery query does not consult the general dataflow
|
|
# barrierModel the way log-injection does. Excluded from JS analysis
|
|
# instead, for the same reason as the two sinks below: every URL this
|
|
# module requests, including the first, is checked against the configured
|
|
# repository's origin (scheme, host, and port) before being requested, so
|
|
# the walk cannot reach a host the operator did not configure, and the
|
|
# probe carries no credentials. Sencho is single-tenant and self-hosted:
|
|
# the admin who sets the repository URL owns the server, the trust model
|
|
# already accepted for registry-api.ts and NotificationService.ts.
|
|
- backend/src/services/git/redirectPreflight.ts
|
|
# Per-fetch HTTPS CA bundle sink: combines the operator-supplied per-source
|
|
# PEM, the optional NODE_EXTRA_CA_CERTS file, and (on Windows) Git's
|
|
# bundled system bundle into one mode-0600 file the git child reads via
|
|
# http.sslCAInfo. Every input to the write has already been validated as
|
|
# PEM by the caller or comes from a known, operator-controlled path on the
|
|
# same host; the file lives under the per-fetch workspace, which the caller
|
|
# deletes in a finally block. Excluded from JS analysis for the same reason
|
|
# as the SSH sink above.
|
|
- backend/src/services/git/gitCaBundleSink.ts
|
|
# GitOps decrypt overlay: SOPS ciphertext from a pinned Git checkout is
|
|
# decrypted into a short-lived mode-0700 tree under DATA_DIR/git-secrets for
|
|
# Compose validate/deploy/recovery only. Stack segments pass isValidStackName
|
|
# plus path.basename, operation ids are server UUIDs, and every filesystem
|
|
# sink uses resolve-plus-startsWith guards. js/http-to-file-access ignores
|
|
# query-filters path scoping (see sshCredentialFiles above), and
|
|
# js/path-injection still flags the admin-Git sourceRoot/candidate copy path
|
|
# after those guards. Excluding the sink module keeps CodeQL coverage on the
|
|
# rest of the GitOps surface without treating the intended decrypt boundary
|
|
# as a durable-plaintext vulnerability.
|
|
- backend/src/services/gitops/sops/overlay.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
|