Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
aa43809
fix: harden directory containment checks
bbingz May 30, 2026
e2aab77
fix: harden default web security settings
bbingz May 30, 2026
8f89194
fix: require authorization for database sync
bbingz May 30, 2026
000f2f4
fix: prevent admin password reset enumeration
bbingz May 30, 2026
dbb4b92
fix: rate limit anonymous hits updates
bbingz May 30, 2026
7b78e09
fix: prevent admin login sms enumeration
bbingz May 30, 2026
bbee404
fix: invalidate password reset sms codes
bbingz May 30, 2026
bc6c02f
fix: restore TLS certificate validation
bbingz May 30, 2026
85033df
fix: update vulnerable package resolutions
bbingz May 30, 2026
b71fee5
fix: block private hosts in remote downloads
bbingz May 30, 2026
d03f26c
fix: harden v1 administrator login
bbingz May 30, 2026
c119fe0
fix: validate scrawl image payloads
bbingz May 30, 2026
8e62de9
fix: constrain form file deletion paths
bbingz May 30, 2026
40bcd67
fix: enforce captcha during forced admin login
bbingz May 30, 2026
7a15281
fix: rate limit v1 user login
bbingz May 30, 2026
f91a375
fix: sign stl trigger requests
bbingz May 30, 2026
c54c03f
fix: rate limit stl rendering endpoints
bbingz May 30, 2026
aa274d7
fix: sign stl page contents requests
bbingz May 30, 2026
1280e4c
fix: sign content download requests
bbingz May 30, 2026
2033839
fix: invalidate tokens on password change
bbingz May 30, 2026
0e77e17
fix: restrict stl sql to read only queries
bbingz May 30, 2026
190aa57
fix: reject malformed stl page tokens
bbingz May 30, 2026
abd0e7b
fix: validate uploaded image content
bbingz May 30, 2026
e6d5719
fix: validate direct image uploads
bbingz May 30, 2026
11e63f1
fix: cap upload request sizes
bbingz May 30, 2026
95f0ce3
fix: validate v1 content query fields
bbingz May 30, 2026
ec68c6a
fix: require permission for v1 user lookup
bbingz May 30, 2026
7a12ae7
fix: rate limit agent security key attempts
bbingz May 30, 2026
f4569e3
fix: require content check permission on v1 update
bbingz May 30, 2026
a0d296d
fix: enforce content check permissions on editor saves
bbingz May 30, 2026
69b0b83
fix: enforce check permissions on content imports
bbingz May 30, 2026
c4a7370
fix: require auth for error log details
bbingz May 30, 2026
39e0e9a
fix: sanitize admin login response
bbingz May 30, 2026
6f60f48
fix: reject jwt tokens in query strings
bbingz May 30, 2026
ab03864
fix: restrict administrator permission changes to super admins
bbingz May 30, 2026
f418434
fix: require super admin for v1 administrator management
bbingz May 30, 2026
d39995f
fix: restrict site admin permissions to assigned sites
bbingz May 30, 2026
42ab4ba
fix: enforce site access for site management
bbingz May 30, 2026
d384f7e
fix: require site access for editor uploads
bbingz May 30, 2026
32c55b2
fix: enforce site access for common editor uploads
bbingz May 30, 2026
a99a2f5
fix: validate rest downloads against ssrf
bbingz May 30, 2026
4c15caf
fix: sanitize channel group filters
bbingz May 30, 2026
59316f9
fix: escape search highlight regex
bbingz May 30, 2026
0bcc278
fix: rate limit public sms endpoints
bbingz May 30, 2026
c4e35ca
fix: require auth for preview routes
bbingz May 30, 2026
fe0e823
fix: avoid persistent cloud token storage
bbingz May 30, 2026
83eb289
fix: add baseline security headers
bbingz May 30, 2026
6fa87b8
fix: bound form value length
bbingz May 30, 2026
19497b7
fix: store passwords with one-way hashes
bbingz May 30, 2026
4ef219d
fix: reject unsafe zip entries
bbingz May 30, 2026
ada1c63
fix: install plugins from validated staging
bbingz May 30, 2026
b74f811
fix: consume sms codes after verification
bbingz May 30, 2026
c6a9139
fix: cap zip extracted size
bbingz May 30, 2026
3b17c9e
fix: rate limit signed trigger requests
bbingz May 30, 2026
23b0435
fix: validate cloud restore keys
bbingz May 30, 2026
f117dea
fix: cap stl include depth
bbingz May 30, 2026
5e02200
fix: enable ueditor xss filters
bbingz May 30, 2026
6956689
fix: remove docker example secrets
bbingz May 30, 2026
5075afd
fix: stop publishing source maps
bbingz May 30, 2026
93348ad
fix: remove public ueditor samples
bbingz May 30, 2026
4bde771
fix: validate raw stl where clauses
bbingz May 30, 2026
4d0c48b
fix: synchronize rate limit counters
bbingz May 30, 2026
7d694a3
fix: pin npm transitive security fixes
bbingz May 30, 2026
feac6a0
fix: lock npm security overrides
bbingz May 30, 2026
c41d311
fix: replace vulnerable npm build packages
bbingz May 30, 2026
a9e8e18
fix: skip win32 icons on linux builds
bbingz May 30, 2026
17e18c0
fix: use npm lockfile v2 for ci scans
bbingz May 30, 2026
f3237e1
fix: use supported dotnet container bases
bbingz May 30, 2026
74ae278
test: isolate web integration secrets
bbingz May 30, 2026
bafbfa9
fix: regenerate windows application icons
bbingz May 30, 2026
fb5e0b5
fix: disable broken win32 icon resources
bbingz May 30, 2026
471cedf
ci: skip release uploads in pull requests
bbingz May 30, 2026
c56b3d9
ci: only publish release artifacts on master
bbingz May 30, 2026
88fb6fd
ci: treat unresolved oss variables as unavailable
bbingz May 30, 2026
5ba0c98
fix: pin nginx image to stable slim base
bbingz May 30, 2026
f5f1deb
fix: align aspnet test packages with dotnet 8
bbingz May 30, 2026
ede0f79
fix: run dotnet containers as non-root
bbingz May 30, 2026
318667f
fix: pin docker compose service images
bbingz May 30, 2026
b6c376b
fix: add lockfile for web extension manifest
bbingz May 30, 2026
9ab5123
fix: declare non-root docker users
bbingz May 30, 2026
874688b
fix: refresh vulnerable nuget dependencies
bbingz May 30, 2026
4216af1
fix: add docker health checks
bbingz May 30, 2026
e3cbbaf
fix: pin container image references
bbingz May 30, 2026
42b9f05
fix: declare root package license
bbingz May 30, 2026
45b15be
fix: move tests to xunit v3
bbingz May 30, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ src/SSCMS.Web/.vs/
src/SSCMS.Web/.vscode/
src/SSCMS.Web/.config/
src/SSCMS.Web/version.txt
src/SSCMS.Web/key-*.xml
sscms-all.sln

*.ps1
Expand Down
10 changes: 6 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/core/aspnet:8.0-buster-slim AS base
FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled-extra@sha256:a6df1767e8363f13963cc5da5c24d0403b630f66acbf5fd6bc414269e446c8fb AS base
WORKDIR /app
EXPOSE 8080
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:8.0-buster AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0-noble@sha256:6b0b7f73dc7cce85fe9eaf7cfcfd1dc109accc5b3782c8cba006fbe036da424e AS build
WORKDIR /src
COPY ["src/SSCMS.Web/SSCMS.Web.csproj", "src/SSCMS.Web/"]
COPY ["src/SSCMS.Core/SSCMS.Core.csproj", "src/SSCMS.Core/"]
Expand All @@ -23,7 +23,9 @@ RUN echo `date +%Y-%m-%d-%H-%M-%S` > /app/sscms/_wwwroot/sitefiles/version.txt

FROM base AS final
WORKDIR /app
COPY --from=publish /app/sscms .
COPY --from=publish --chown=1654:1654 /app/sscms .
USER 1654
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 CMD ["dotnet", "--list-runtimes"]
ENTRYPOINT ["dotnet", "SSCMS.Web.dll"]

# docker build -t sscms/core:dev .
# docker build -t sscms/core:dev .
3 changes: 2 additions & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,12 @@ jobs:
- script: docker build -f docker/Dockerfile.core --no-cache -t $(imageName):latest -t $(imageName):$(productVersion) .
- task: Docker@2
displayName: Push image
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
containerRegistry: |
docker
repository: $(imageName)
command: push
tags: |
latest
$(productVersion)
$(productVersion)
11 changes: 7 additions & 4 deletions docker/Dockerfile.core
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled-extra@sha256:a6df1767e8363f13963cc5da5c24d0403b630f66acbf5fd6bc414269e446c8fb AS base
WORKDIR /app
EXPOSE 8080
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0-noble@sha256:6b0b7f73dc7cce85fe9eaf7cfcfd1dc109accc5b3782c8cba006fbe036da424e AS build
WORKDIR /sscms
RUN wget https://dl.sscms.com/cms/7.4.0/sscms-7.4.0-linux-x64.tar.gz
RUN tar -xzf sscms-7.4.0-linux-x64.tar.gz
Expand All @@ -13,7 +14,9 @@ RUN echo `date +%Y-%m-%d-%H-%M-%S` > /sscms/_wwwroot/sitefiles/version.txt

FROM base AS final
WORKDIR /app
COPY --from=build /sscms .
COPY --from=build --chown=1654:1654 /sscms .
USER 1654
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 CMD ["dotnet", "--list-runtimes"]
ENTRYPOINT ["dotnet", "SSCMS.Web.dll"]

# docker build -f Dockerfile.core --no-cache -t sscms/core:latest -t sscms/core:7.4.0 .
# docker build -f Dockerfile.core --no-cache -t sscms/core:7.4.0 .
10 changes: 8 additions & 2 deletions docker/Dockerfile.nginx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
FROM nginx:latest
FROM nginx:stable-alpine-slim@sha256:2c605dbeab79a6b2a63340474fe58119d0ef95bdc4b1f41df0aa689659b3d13b

COPY nginx.conf /etc/nginx/nginx.conf

# docker build -f Dockerfile.nginx -t sscms/nginx .
RUN chown -R nginx:nginx /var/cache/nginx /var/run

USER nginx
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD ["nginx", "-t"]

# docker build -f docker/Dockerfile.nginx -t sscms/nginx:7.4.0 docker
20 changes: 12 additions & 8 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ SSCMS 官方镜像,跟随 SSCMS 版本同步更新。
拉取最新版本的 [SSCMS 镜像](https://hub.docker.com/r/sscms/core),运行命令:

``` bash
docker pull sscms/core:latest
docker pull sscms/core:7.4.0
```

如果需要获取指定版本的 [SSCMS 镜像](https://hub.docker.com/r/sscms/core),可以运行命令:
Expand All @@ -27,19 +27,21 @@ mkdir wwwroot
接下来,我们使用 SQLite 本地数据库运行 SSCMS:

```bash
export SSCMS_SECURITY_KEY="$(uuidgen)"

docker run -d \
--name my-sscms \
-p 80:80 \
-p 80:8080 \
--restart=always \
-v "$(pwd)"/wwwroot:/app/wwwroot \
-e SSCMS_SECURITY_KEY=e2a3d303-ac9b-41ff-9154-930710af0845 \
-e SSCMS_SECURITY_KEY="$SSCMS_SECURITY_KEY" \
-e SSCMS_DATABASE_TYPE=SQLite \
sscms/core:latest
sscms/core:7.4.0
```

- `-d` 参数让容器以后台任务形式运行
- `-name` 参数将容器实例命名为 my-sscms,可以更换为其他名称
- `-p` 参数映射容器的80端口到宿主机的80端口,如果希望使用8080端口访问可以设置 `-p 8080:80`
- `-p` 参数映射容器的8080端口到宿主机的80端口,如果希望使用8080端口访问可以设置 `-p 8080:8080`
- `-restart` 参数使得容器能够自动重启,必须使用 `always` 选项,否则容器将无法安装及升级插件
- `-v` 参数将当前文件夹下的 `wwwroot` 目录作为网站跟目录,从而保存 SSCMS 站点数据,其中 `$(pwd)` 代表当前文件夹
- `-e` 参数设置容器运行环境变量,SSCMS 系统将读取环境变量,作为容器运行的参数,在此我们设置 `SecurityKey` 为随机的 GUID 值,数据库类型为 SQLite
Expand All @@ -50,14 +52,16 @@ docker run -d \
除了将当前文件夹下的 `wwwroot` 目录作为站点根目录存储数据,我们也可以将镜像数据持久化存储在 Volume 中:

```bash
export SSCMS_SECURITY_KEY="$(uuidgen)"

docker run -d \
--name my-sscms \
-p 80:80 \
-p 80:8080 \
--restart=always \
-v volume-sscms:/app/wwwroot \
-e SSCMS_SECURITY_KEY=e2a3d303-ac9b-41ff-9154-930710af0845 \
-e SSCMS_SECURITY_KEY="$SSCMS_SECURITY_KEY" \
-e SSCMS_DATABASE_TYPE=SQLite \
sscms/core:latest
sscms/core:7.4.0
```

此命令将自动创建名称为 `volume-sscms` 的 Docker Volume。
Expand Down
20 changes: 8 additions & 12 deletions docker/cluster/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.7"

volumes:

volume-sscms:
Expand All @@ -8,29 +6,27 @@ volumes:
services:

reverse-proxy:
image: "sscms/nginx"
image: "sscms/nginx:7.4.0"
ports:
- "80:80"
- "443:443"
- "80:8080"
restart: always

cache:
image: "redis"
ports:
- "6379:6379"
image: "redis:8-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1"
command: ["redis-server", "--requirepass", "${SSCMS_REDIS_PASSWORD:?set SSCMS_REDIS_PASSWORD}"]
restart: always

sscms:
depends_on:
- reverse-proxy
- cache
image: "sscms/core"
image: "sscms/core:7.4.0"
expose:
- "80"
- "8080"
environment:
SSCMS_SECURITY_KEY: e2a3d303-ac9b-41ff-9154-930710af0845
SSCMS_SECURITY_KEY: ${SSCMS_SECURITY_KEY:?set SSCMS_SECURITY_KEY}
SSCMS_DATABASE_TYPE: SQLite
SSCMS_REDIS_HOST: redis://cache
SSCMS_REDIS_HOST: redis://:${SSCMS_REDIS_PASSWORD:?set SSCMS_REDIS_PASSWORD}@cache:6379
volumes:
- volume-sscms:/app/wwwroot
restart: always
Expand Down
24 changes: 11 additions & 13 deletions docker/mysql/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.7"

volumes:

volume-mysql:
Expand All @@ -11,31 +9,31 @@ volumes:
services:

sscms-mysql:
image: mysql
image: mysql:8.4@sha256:c36050afdca850f23cef85703f84c7531a5ae155a11b5ee1c60acb09937c4084
command: --default-authentication-plugin=mysql_native_password
restart: always
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: mysql-password
MYSQL_DATABASE: mysql-db
MYSQL_ROOT_PASSWORD: ${SSCMS_MYSQL_ROOT_PASSWORD:?set SSCMS_MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: sscms
MYSQL_USER: sscms
MYSQL_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD}
volumes:
- volume-mysql:/var/lib/mysql

sscms-core:
depends_on:
- sscms-mysql
image: "sscms/core"
image: "sscms/core:7.4.0"
restart: always
ports:
- "80:80"
- "80:8080"
environment:
SSCMS_SECURITY_KEY: e2a3d303-ac9b-41ff-9154-930710af0845
SSCMS_SECURITY_KEY: ${SSCMS_SECURITY_KEY:?set SSCMS_SECURITY_KEY}
SSCMS_DATABASE_TYPE: MySQL
SSCMS_DATABASE_HOST: sscms-mysql
SSCMS_DATABASE_USER: root
SSCMS_DATABASE_PASSWORD: mysql-password
SSCMS_DATABASE_NAME: mysql-db
SSCMS_DATABASE_USER: sscms
SSCMS_DATABASE_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD}
SSCMS_DATABASE_NAME: sscms
volumes:
- volume-sscms:/app/wwwroot

Expand Down
12 changes: 9 additions & 3 deletions docker/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
worker_processes 1;
pid /tmp/nginx.pid;

events { worker_connections 1024; }

http {

sendfile on;
client_max_body_size 500m;
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;

upstream sscms {
server sscms;
server sscms:8080;
}

server {
listen 80;
listen 8080;
server_name _;

location / {
Expand All @@ -29,4 +35,4 @@ http {
proxy_set_header X-Forwarded-Host $server_name;
}
}
}
}
23 changes: 10 additions & 13 deletions docker/postgres/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.7"

volumes:

volume-postgres:
Expand All @@ -11,30 +9,29 @@ volumes:
services:

sscms-postgres:
image: postgres
image: postgres:17-alpine@sha256:979c4379dd698aba0b890599a6104e082035f98ef31d9b9291ec22f2b13059ca
restart: always
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres-password
POSTGRES_USER: sscms
POSTGRES_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD}
POSTGRES_DB: sscms
volumes:
- volume-postgres:/var/lib/postgresql/data

sscms-core:
depends_on:
- sscms-postgres
image: "sscms/core"
image: "sscms/core:7.4.0"
restart: always
ports:
- "80:80"
- "80:8080"
environment:
SSCMS_SECURITY_KEY: e2a3d303-ac9b-41ff-9154-930710af0845
SSCMS_SECURITY_KEY: ${SSCMS_SECURITY_KEY:?set SSCMS_SECURITY_KEY}
SSCMS_DATABASE_TYPE: PostgreSQL
SSCMS_DATABASE_HOST: sscms-postgres
SSCMS_DATABASE_USER: postgres
SSCMS_DATABASE_PASSWORD: postgres-password
SSCMS_DATABASE_NAME: postgres
SSCMS_DATABASE_USER: sscms
SSCMS_DATABASE_PASSWORD: ${SSCMS_DATABASE_PASSWORD:?set SSCMS_DATABASE_PASSWORD}
SSCMS_DATABASE_NAME: sscms
volumes:
- volume-sscms:/app/wwwroot

Expand Down
Loading