diff --git a/.drone.yml b/.drone.yml index 035b0d24..f18b01cc 100644 --- a/.drone.yml +++ b/.drone.yml @@ -22,55 +22,19 @@ steps: - yarn config set network-timeout 100000 - yarn --cwd web install - tar --transform="s@.@./happydomain-${DRONE_COMMIT}@" --exclude-vcs --exclude=./web/node_modules/.cache -czf /dev/shm/happydomain-src.tar.gz . - - mv /dev/shm/happydomain-src.tar.gz . + - mkdir deploy + - mv /dev/shm/happydomain-src.tar.gz deploy - yarn --cwd web --offline generate:api && sed -i "s/hey-api\.ts';/hey-api';/" web/src/lib/api-base/client.gen.ts - yarn --cwd web --offline build - yarn --cwd web-admin --offline generate:api && sed -i "s/hey-api\.ts';/hey-api';/" web/src/lib/api-admin/client.gen.ts - yarn --cwd web-admin --offline build - - name: deploy sources - image: plugins/s3 - settings: - endpoint: https://blob.nemunai.re - path_style: true - region: garage - bucket: happydomain-dl - access_key: - from_secret: s3_access_key - secret_key: - from_secret: s3_secret_key - source: happydomain-src.tar.gz - target: /${DRONE_BRANCH//\//-}/ - when: - event: - - push - branch: - exclude: - - renovate/* - - - name: deploy sources for release - image: plugins/s3 - settings: - endpoint: https://blob.nemunai.re - path_style: true - region: garage - bucket: happydomain-dl - access_key: - from_secret: s3_access_key - secret_key: - from_secret: s3_secret_key - source: happydomain-src.tar.gz - target: /${DRONE_TAG}/ - when: - event: - - tag - - name: backend-commit image: golang:1-alpine commands: - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - - ln happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain + - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + - ln deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain environment: CGO_ENABLED: 0 when: @@ -82,14 +46,48 @@ steps: image: golang:1-alpine commands: - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X main.Version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - - ln happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain + - go build -tags netgo,swagger,web -ldflags '-w -X main.Version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + - ln deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain environment: CGO_ENABLED: 0 when: event: - tag + - name: generate SBOM + image: nemunaire/drone-syft + settings: + select_catalogers: go,npm + output: spdx-json=deploy/happydomain-sbom.spdx.json + source_name: happyDomain + + - name: build-commit macOS + image: golang:1-alpine + commands: + - apk add --no-cache git + - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + environment: + CGO_ENABLED: 0 + GOOS: darwin + GOARCH: amd64 + when: + event: + exclude: + - tag + + - name: build-tag macOS + image: golang:1-alpine + commands: + - apk add --no-cache git + - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_TAG##v}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + environment: + CGO_ENABLED: 0 + GOOS: darwin + GOARCH: amd64 + when: + event: + - tag + - name: deploy image: plugins/s3 settings: @@ -101,8 +99,9 @@ steps: from_secret: s3_access_key secret_key: from_secret: s3_secret_key - source: happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + source: deploy/* target: /${DRONE_BRANCH//\//-}/ + strip_prefix: deploy/ when: event: - push @@ -121,72 +120,9 @@ steps: from_secret: s3_access_key secret_key: from_secret: s3_secret_key - source: happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - target: /${DRONE_TAG}/ - when: - event: - - tag - - - name: build-commit macOS - image: golang:1-alpine - commands: - - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - environment: - CGO_ENABLED: 0 - GOOS: darwin - GOARCH: amd64 - when: - event: - exclude: - - tag - - - name: build-tag macOS - image: golang:1-alpine - commands: - - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_TAG##v}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - environment: - CGO_ENABLED: 0 - GOOS: darwin - GOARCH: amd64 - when: - event: - - tag - - - name: deploy macOS - image: plugins/s3 - settings: - endpoint: https://blob.nemunai.re - path_style: true - region: garage - bucket: happydomain-dl - access_key: - from_secret: s3_access_key - secret_key: - from_secret: s3_secret_key - source: happydomain-darwin-${DRONE_STAGE_ARCH} - target: /${DRONE_BRANCH//\//-}/ - when: - event: - - push - branch: - exclude: - - renovate/* - - - name: deploy macOS release - image: plugins/s3 - settings: - endpoint: https://blob.nemunai.re - path_style: true - region: garage - bucket: happydomain-dl - access_key: - from_secret: s3_access_key - secret_key: - from_secret: s3_secret_key - source: happydomain-darwin-${DRONE_STAGE_ARCH} + source: deploy/* target: /${DRONE_TAG}/ + strip_prefix: deploy/ when: event: - tag @@ -210,11 +146,11 @@ steps: from_secret: git_nemunaire_token base_url: https://git.nemunai.re draft: true - prerelease: true files: - happydomain-src.tar.gz - happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - happydomain-darwin-${DRONE_STAGE_ARCH} + - happydomain-sbom.spdx.json when: event: - tag @@ -226,11 +162,11 @@ steps: from_secret: codeberg_token base_url: https://codeberg.org draft: true - prerelease: true files: - happydomain-src.tar.gz - happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - happydomain-darwin-${DRONE_STAGE_ARCH} + - happydomain-sbom.spdx.json when: event: - tag @@ -241,12 +177,12 @@ steps: api_key: from_secret: github_release_token draft: true - prerelease: true github_url: https://github.com files: - happydomain-src.tar.gz - happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - happydomain-darwin-${DRONE_STAGE_ARCH} + - happydomain-sbom.spdx.json when: event: - tag @@ -294,8 +230,8 @@ steps: image: golang:1-alpine commands: - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - - ln happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain + - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + - ln deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain environment: CGO_ENABLED: 0 when: @@ -307,8 +243,8 @@ steps: image: golang:1-alpine commands: - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X main.Version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - - ln happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain + - go build -tags netgo,swagger,web -ldflags '-w -X main.Version=${DRONE_TAG##v} -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + - ln deploy/happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} happydomain environment: CGO_ENABLED: 0 when: @@ -324,6 +260,33 @@ steps: environment: CGO_ENABLED: 0 + - name: build-commit macOS + image: golang:1-alpine + commands: + - apk add --no-cache git + - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + environment: + CGO_ENABLED: 0 + GOOS: darwin + GOARCH: arm64 + when: + event: + exclude: + - tag + + - name: build-tag macOS + image: golang:1-alpine + commands: + - apk add --no-cache git + - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_TAG##v}" -X main.build=${DRONE_BUILD_NUMBER}' -o deploy/happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ + environment: + CGO_ENABLED: 0 + GOOS: darwin + GOARCH: arm64 + when: + event: + - tag + - name: deploy image: plugins/s3 settings: @@ -335,8 +298,9 @@ steps: from_secret: s3_access_key secret_key: from_secret: s3_secret_key - source: happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + source: deploy/* target: /${DRONE_BRANCH//\//-}/ + strip_prefix: deploy/ when: event: - push @@ -355,72 +319,9 @@ steps: from_secret: s3_access_key secret_key: from_secret: s3_secret_key - source: happydomain-${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} - target: /${DRONE_TAG}/ - when: - event: - - tag - - - name: build-commit macOS - image: golang:1-alpine - commands: - - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_BRANCH}-${DRONE_COMMIT}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - environment: - CGO_ENABLED: 0 - GOOS: darwin - GOARCH: arm64 - when: - event: - exclude: - - tag - - - name: build-tag macOS - image: golang:1-alpine - commands: - - apk add --no-cache git - - go build -tags netgo,swagger,web -ldflags '-w -X "main.Version=${DRONE_TAG##v}" -X main.build=${DRONE_BUILD_NUMBER}' -o happydomain-darwin-${DRONE_STAGE_ARCH} ./cmd/happyDomain/ - environment: - CGO_ENABLED: 0 - GOOS: darwin - GOARCH: arm64 - when: - event: - - tag - - - name: deploy macOS - image: plugins/s3 - settings: - endpoint: https://blob.nemunai.re - path_style: true - region: garage - bucket: happydomain-dl - access_key: - from_secret: s3_access_key - secret_key: - from_secret: s3_secret_key - source: happydomain-darwin-${DRONE_STAGE_ARCH} - target: /${DRONE_BRANCH//\//-}/ - when: - event: - - push - branch: - exclude: - - renovate/* - - - name: deploy macOS release - image: plugins/s3 - settings: - endpoint: https://blob.nemunai.re - path_style: true - region: garage - bucket: happydomain-dl - access_key: - from_secret: s3_access_key - secret_key: - from_secret: s3_secret_key - source: happydomain-darwin-${DRONE_STAGE_ARCH} + source: deploy/* target: /${DRONE_TAG}/ + strip_prefix: deploy/ when: event: - tag diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..061dcb37 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,64 @@ +# Security Policy + +## Supported Versions + +Only the latest version of happyDomain is supported with security fixes. + +| Version | Supported | +| ------- | --------- | +| latest | ✓ | +| < latest| ✗ | + + +## Scope + +### In scope + +- happyDomain application code (API/backend and web frontend) +- Other websites directly operated by the happyDomain team: documentation, main website, blog, git redirection, downloads website, demo instance, insights + +### Out of scope + +- Vulnerabilities in third-party dependencies that are not directly exploitable in happyDomain +- Social engineering attacks +- Denial-of-service attacks requiring significant resources + + +## Reporting a Vulnerability + +If you discover a security vulnerability in happyDomain, please report it privately. + +By email: security@happydomain.org +On GitHub: https://github.com/happydomain/happydomain/security/advisories +On Gitlab: https://gitlab.com/happyDomain/happyDomain/-/issues/new (check Confidential issue before submitting) +On Framagit: https://framagit.org/happyDomain/happyDomain/-/issues/new (check Confidential issue before submitting) + +Please include: +- description of the vulnerability +- steps to reproduce +- potential impact + + +## Disclosure policy + +We follow a responsible disclosure process. + +After receiving a report we will: +1. acknowledge within 72 hours +2. investigate the issue +3. prepare a fix +4. publish a security advisory when the fix is available + + +## Safe Harbor + +We consider security research conducted in good faith to be authorized. We will not pursue legal action against researchers who: +- Report vulnerabilities through the channels listed above +- Avoid accessing, modifying, or deleting data that doesn't belong to them +- Avoid degrading the availability of our services +- Do not publicly disclose the vulnerability before a fix is available + + +## Credits + +We are happy to credit security researchers who responsibly disclose vulnerabilities. diff --git a/web/package.json b/web/package.json index ad91be29..ef6ead22 100644 --- a/web/package.json +++ b/web/package.json @@ -35,6 +35,9 @@ "vite": "^7.0.0", "vitest": "^4.0.0" }, + "resolutions": { + "vite": "^7.0.0" + }, "type": "module", "dependencies": { "@hey-api/openapi-ts": "^0.90.6", diff --git a/web/src/lib/components/forms/DomainGroupList.svelte b/web/src/lib/components/forms/DomainGroupList.svelte index 046a594e..b408b40f 100644 --- a/web/src/lib/components/forms/DomainGroupList.svelte +++ b/web/src/lib/components/forms/DomainGroupList.svelte @@ -25,7 +25,7 @@ import { createEventDispatcher } from "svelte"; import HListGroup from "$lib/components/ListGroup.svelte"; - import { groups } from "$lib/stores/domains"; + import { groups, newlyGroups } from "$lib/stores/domains"; import { t } from "$lib/translations"; const dispatch = createEventDispatcher(); @@ -49,7 +49,7 @@ !$groups.includes(g))]} {flush} isActive={(item) => selectedGroup != null && item === selectedGroup} on:click={selectGroup} diff --git a/web/src/lib/components/modals/DomainGroup.svelte b/web/src/lib/components/modals/DomainGroup.svelte index 3341dd75..dbf0444b 100644 --- a/web/src/lib/components/modals/DomainGroup.svelte +++ b/web/src/lib/components/modals/DomainGroup.svelte @@ -44,7 +44,7 @@ import ZoneList from "$lib/components/zones/ZoneList.svelte"; import { updateDomain } from "$lib/api/domains"; import type { Domain } from "$lib/model/domain"; - import { groups, domains, refreshDomains } from "$lib/stores/domains"; + import { groups, domains, newlyGroups, refreshDomains } from "$lib/stores/domains"; import { t } from "$lib/translations"; interface Props { @@ -66,6 +66,7 @@ if (newgroup.length && mygroups.indexOf(newgroup) < 0) { mygroups.push(newgroup); mygroups = mygroups; + newlyGroups.update((gs) => gs.includes(newgroup) ? gs : [...gs, newgroup]); } newgroup = ""; } diff --git a/web/src/lib/components/modals/NewDomainGroup.svelte b/web/src/lib/components/modals/NewDomainGroup.svelte new file mode 100644 index 00000000..0e9add64 --- /dev/null +++ b/web/src/lib/components/modals/NewDomainGroup.svelte @@ -0,0 +1,95 @@ + + + + + + + + + {$t("domaingroups.new")} + + +
+ + + + +
+
+
diff --git a/web/src/lib/components/pages/home/DomainGroupCard.svelte b/web/src/lib/components/pages/home/DomainGroupCard.svelte index 40226d01..0054f6c7 100644 --- a/web/src/lib/components/pages/home/DomainGroupCard.svelte +++ b/web/src/lib/components/pages/home/DomainGroupCard.svelte @@ -22,18 +22,14 @@ -->