chore(security): fail closed booking development configuration
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ client/dist/
|
||||
client/dist.zip
|
||||
client/.vite/
|
||||
.pnpm-store/
|
||||
.planning/
|
||||
|
||||
server/target/
|
||||
|
||||
|
||||
1
client/.node-version
Normal file
1
client/.node-version
Normal file
@@ -0,0 +1 @@
|
||||
24.14.0
|
||||
@@ -3,6 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@11.16.0",
|
||||
"scripts": {
|
||||
"dev": "vite --host 127.0.0.1 --port 5174",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
@@ -37,6 +38,7 @@
|
||||
"vue-tsc": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.13.0"
|
||||
"node": "24.x",
|
||||
"pnpm": "11.x"
|
||||
}
|
||||
}
|
||||
|
||||
23
client/pnpm-lock.yaml
generated
23
client/pnpm-lock.yaml
generated
@@ -4,6 +4,10 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
nanoid: 3.3.17
|
||||
postcss: 8.5.23
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -947,6 +951,7 @@ packages:
|
||||
|
||||
glob@10.4.5:
|
||||
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
|
||||
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
hasBin: true
|
||||
|
||||
hookable@5.5.3:
|
||||
@@ -1179,8 +1184,8 @@ packages:
|
||||
muggle-string@0.4.1:
|
||||
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||
|
||||
nanoid@3.3.15:
|
||||
resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==}
|
||||
nanoid@3.3.17:
|
||||
resolution: {integrity: sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
@@ -1274,8 +1279,8 @@ packages:
|
||||
resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
postcss@8.5.16:
|
||||
resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==}
|
||||
postcss@8.5.23:
|
||||
resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
@@ -2229,7 +2234,7 @@ snapshots:
|
||||
'@vue/shared': 3.5.39
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.21
|
||||
postcss: 8.5.16
|
||||
postcss: 8.5.23
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.5.39':
|
||||
@@ -2836,7 +2841,7 @@ snapshots:
|
||||
|
||||
muggle-string@0.4.1: {}
|
||||
|
||||
nanoid@3.3.15: {}
|
||||
nanoid@3.3.17: {}
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
@@ -2922,9 +2927,9 @@ snapshots:
|
||||
cssesc: 3.0.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
postcss@8.5.16:
|
||||
postcss@8.5.23:
|
||||
dependencies:
|
||||
nanoid: 3.3.15
|
||||
nanoid: 3.3.17
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
@@ -3120,7 +3125,7 @@ snapshots:
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
picomatch: 4.0.5
|
||||
postcss: 8.5.16
|
||||
postcss: 8.5.23
|
||||
rolldown: 1.1.4
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
packages:
|
||||
- .
|
||||
|
||||
overrides:
|
||||
nanoid: 3.3.17
|
||||
postcss: 8.5.23
|
||||
|
||||
allowBuilds:
|
||||
vue-demi: true
|
||||
|
||||
20
docs/project/operations/booking-v01-security-checkpoint.md
Normal file
20
docs/project/operations/booking-v01-security-checkpoint.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Booking V0.1 安全 checkpoint
|
||||
|
||||
最后更新:2026-08-08
|
||||
|
||||
## 当前已完成的仓库内处置
|
||||
|
||||
- 开发与生产数据库配置不再提交远程连接地址、用户名或密码;开发环境必须通过 `TH_HOTEL_DEV_DB_URL`、`TH_HOTEL_DEV_DB_USERNAME`、`TH_HOTEL_DEV_DB_PASSWORD` 注入,生产环境使用对应的 `TH_HOTEL_PROD_*` Secret。
|
||||
- 默认本地 profile 使用进程内 H2,且关闭 AgentBus 捕获、SuperAgent 分发、Debug EML、文档转换和演示数据入口。
|
||||
- Git 历史不做重写。历史中曾出现的数据库凭据不得继续使用,也不得从历史恢复到配置文件。
|
||||
- BR00 原型只在开发态且 `VITE_ENABLE_BR00_PROTOTYPE=true` 时注册;生产构建不暴露原型路由,原型不连接正式 API。
|
||||
|
||||
## G0 的外部前置条件
|
||||
|
||||
数据库管理员必须在部署 Secret 系统中废止/轮换历史泄露的数据库凭据,并记录轮换日期与工单号。此操作不由代码仓库或应用启动过程替代;在收到完成证明前,G0 不得标记为完全通过。
|
||||
|
||||
## 验证边界
|
||||
|
||||
- 本地验证使用 H2,不使用远程数据库默认值。
|
||||
- 远程测试环境只允许通过 Secret 注入连接信息。
|
||||
- 真实邮件样本、密码和 `.planning` 过程文件不进入产品提交。
|
||||
44
scripts/verify-no-secrets.sh
Executable file
44
scripts/verify-no-secrets.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# Lightweight fail-closed scan for the checked-out source tree. Historical Git
|
||||
# secrets are deliberately not rewritten here; this gate prevents new obvious
|
||||
# plaintext credentials from entering the current product tree.
|
||||
set -euo pipefail
|
||||
|
||||
if ! command -v rg >/dev/null 2>&1; then
|
||||
echo 'rg is required for the repository secret scan.' >&2
|
||||
exit 69
|
||||
fi
|
||||
|
||||
# Only configuration and executable wiring can introduce a usable credential.
|
||||
# Application code contains benign variables such as `rawPassword`; scanning it
|
||||
# creates noise and makes a security gate easy to ignore.
|
||||
scan_args=(--hidden --pcre2 --glob '!.git/**' --glob '!client/node_modules/**' --glob '!server/target/**' --glob '!client/dist/**' --glob '*.yml' --glob '*.yaml' --glob '*.properties' --glob '.env' --glob '.env.*' --glob '*.sh')
|
||||
|
||||
# Ignore environment placeholders and explicitly fake examples. Reject literal
|
||||
# values of at least 12 characters, which includes all deployable credentials.
|
||||
credential_pattern='(?i)(?<![A-Za-z0-9_])(password|passwd|secret|api[_-]?key|access[_-]?key)[[:space:]]*[:=][[:space:]]*["\x27]?(?!\$\{|<|your-|[A-Za-z0-9._-]*xxx)[A-Za-z0-9][A-Za-z0-9_+/.=-]{11,}'
|
||||
credential_matches="$(rg -n -I "${scan_args[@]}" "${credential_pattern}" . || true)"
|
||||
if [[ -n "${credential_matches}" ]]; then
|
||||
echo 'Potential plaintext credential(s) detected outside tests:' >&2
|
||||
printf '%s\n' "${credential_matches}" >&2
|
||||
exit 73
|
||||
fi
|
||||
|
||||
jdbc_userinfo_matches="$(rg -n -I "${scan_args[@]}" 'jdbc:(mysql|postgresql):[^[:space:]]*//[^[:space:]@/]+:[^[:space:]@/]+@' . || true)"
|
||||
if [[ -n "${jdbc_userinfo_matches}" ]]; then
|
||||
echo 'JDBC URL with embedded user information detected:' >&2
|
||||
printf '%s\n' "${jdbc_userinfo_matches}" >&2
|
||||
exit 74
|
||||
fi
|
||||
|
||||
for config_file in server/src/main/resources/application-dev.yml server/src/main/resources/application-prod.yml; do
|
||||
if [[ ! -f "${config_file}" ]]; then
|
||||
continue
|
||||
fi
|
||||
if rg --pcre2 -n '^\s*(url|username|password):\s*(?!\$\{)[^#\s].*$' "${config_file}" >/dev/null; then
|
||||
echo "${config_file} contains a non-environment datasource value." >&2
|
||||
exit 75
|
||||
fi
|
||||
done
|
||||
|
||||
echo 'Repository plaintext-secret scan passed.'
|
||||
1
server/.java-version
Normal file
1
server/.java-version
Normal file
@@ -0,0 +1 @@
|
||||
17
|
||||
@@ -56,11 +56,20 @@
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-database-postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
spring:
|
||||
datasource:
|
||||
# dev 使用可提交的开发测试库地址;密码仍通过环境变量注入。
|
||||
url: "jdbc:mysql://rm-7xv137v8f04vfk7a4ko.mysql.rds.aliyuncs.com:3306/th_hotel?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"
|
||||
username: th_hotel_user
|
||||
password: 'CxrLg6b6kw2zJjmcjHUj'
|
||||
# dev 连接信息只能由部署环境注入。缺失任一变量时 Spring 会在启动阶段失败,
|
||||
# 禁止使用可提交的远程默认值。
|
||||
url: ${TH_HOTEL_DEV_DB_URL}
|
||||
username: ${TH_HOTEL_DEV_DB_USERNAME}
|
||||
password: ${TH_HOTEL_DEV_DB_PASSWORD}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
flyway:
|
||||
enabled: true
|
||||
@@ -18,13 +19,12 @@ agentbus:
|
||||
# dev 默认不主动连接 AgentBus,避免本地启动误消费消息。
|
||||
enabled: false
|
||||
ws:
|
||||
# AgentBus dev/test 真实测试地址可以提交;Token 仍走环境变量。
|
||||
url: wss://mesh.nianxx.cn/ws
|
||||
token: ${AGENTBUS_DEV_WS_TOKEN:}
|
||||
url: ${AGENTBUS_DEV_WS_URL:${AGENTBUS_WS_URL:}}
|
||||
token: ${AGENTBUS_DEV_WS_TOKEN:${AGENTBUS_WS_TOKEN:}}
|
||||
reconnect-delay: 5s
|
||||
connect-timeout: 15s
|
||||
capture:
|
||||
enabled: true
|
||||
enabled: false
|
||||
superagent-dispatch:
|
||||
# dev 默认不自动调用 SuperAgent,需要联调时显式开启。
|
||||
enabled: ${AGENTBUS_DEV_SUPERAGENT_DISPATCH_ENABLED:${AGENTBUS_SUPERAGENT_DISPATCH_ENABLED:false}}
|
||||
@@ -99,10 +99,9 @@ auth:
|
||||
|
||||
reservation:
|
||||
demo-data:
|
||||
# dev 本地联调默认开启演示数据造数接口;生产环境不得开启。
|
||||
enabled: true
|
||||
# 本地演示数据接口访问口令,需和 X-TH-Hotel-Demo-Data-Key 请求头一致。
|
||||
# 本地演示数据接口必须显式开启。
|
||||
enabled: ${RESERVATION_DEV_DEMO_DATA_ENABLED:${RESERVATION_DEMO_DATA_ENABLED:false}}
|
||||
access-key: ${RESERVATION_DEV_DEMO_DATA_ACCESS_KEY:${RESERVATION_DEMO_DATA_ACCESS_KEY:}}
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
port: ${SERVER_PORT:8080}
|
||||
|
||||
42
server/src/main/resources/application-local.yml
Normal file
42
server/src/main/resources/application-local.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
# 默认本机 profile:只使用进程内 H2,所有外部入口和写入型调试能力保持关闭。
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:th_hotel_local;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1
|
||||
username: sa
|
||||
password: ""
|
||||
driver-class-name: org.h2.Driver
|
||||
flyway:
|
||||
enabled: true
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: false
|
||||
swagger-ui:
|
||||
enabled: false
|
||||
|
||||
agentbus:
|
||||
capture:
|
||||
enabled: false
|
||||
superagent-dispatch:
|
||||
enabled: false
|
||||
worker-enabled: false
|
||||
|
||||
superagent:
|
||||
open-api:
|
||||
enabled: false
|
||||
|
||||
debug:
|
||||
eml-upload:
|
||||
enabled: false
|
||||
|
||||
document-conversion:
|
||||
enabled: false
|
||||
worker-enabled: false
|
||||
|
||||
reservation:
|
||||
demo-data:
|
||||
enabled: false
|
||||
|
||||
server:
|
||||
address: 127.0.0.1
|
||||
port: ${SERVER_PORT:8080}
|
||||
@@ -1,10 +1,10 @@
|
||||
spring:
|
||||
datasource:
|
||||
# test 数据库连接信息通过测试机 env 文件统一注入,默认值仅用于本机快速启动。
|
||||
url: ${SPRING_DATASOURCE_URL:jdbc:mysql://127.0.0.1:3306/th_hotel_test?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC}
|
||||
username: ${SPRING_DATASOURCE_USERNAME:th_hotel_test}
|
||||
password: ${SPRING_DATASOURCE_PASSWORD:${TH_HOTEL_TEST_DB_PASSWORD:}}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 本机 test 一律使用 H2;测试机远程数据库必须由三项专属 Secret 同时注入,禁止任何远程默认值。
|
||||
url: ${TH_HOTEL_TEST_DB_URL:jdbc:h2:mem:th_hotel_test;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;DB_CLOSE_DELAY=-1}
|
||||
username: ${TH_HOTEL_TEST_DB_USERNAME:sa}
|
||||
password: ${TH_HOTEL_TEST_DB_PASSWORD:}
|
||||
driver-class-name: ${TH_HOTEL_TEST_DB_DRIVER:org.h2.Driver}
|
||||
flyway:
|
||||
enabled: true
|
||||
servlet:
|
||||
|
||||
@@ -2,8 +2,8 @@ spring:
|
||||
application:
|
||||
name: th-hotel-server
|
||||
profiles:
|
||||
# 未显式指定 profile 时默认使用 dev,避免本地启动误连 test/prod。
|
||||
default: dev
|
||||
# 未显式指定 profile 时只使用本机 H2,禁止本地启动隐式连接任何远程数据库。
|
||||
default: local
|
||||
flyway:
|
||||
enabled: true
|
||||
servlet:
|
||||
@@ -16,6 +16,28 @@ spring:
|
||||
# Spring MVC async timeout 是应用级全局值;当前主要用于避免 Debug EML SSE 先于 SuperAgent 调试调用关闭。
|
||||
request-timeout: ${DEBUG_EML_UPLOAD_SSE_REQUEST_TIMEOUT:${SPRING_MVC_ASYNC_REQUEST_TIMEOUT:1800s}}
|
||||
|
||||
# 新 Booking 主线 PostgreSQL runtime 默认关闭。启用时仅允许部署 Secret 注入连接信息,
|
||||
# 与旧 MySQL/H2 datasource 隔离,不建立跨库事务或未定义双写。
|
||||
booking:
|
||||
agent:
|
||||
# 真实 Booking Agent 必须显式启用;未启用时不确定材料只形成 Risk,不会伪造任务。
|
||||
enabled: ${TH_HOTEL_BOOKING_AGENT_ENABLED:false}
|
||||
# 外部 Agent 调用默认通过 processing-run worker 完成;入口立即返回 202 和 run ID。
|
||||
async-enabled: ${TH_HOTEL_BOOKING_AGENT_ASYNC_ENABLED:true}
|
||||
profile-version: ${TH_HOTEL_BOOKING_AGENT_PROFILE_VERSION:booking-agent-v1}
|
||||
external-subject-id: ${TH_HOTEL_BOOKING_AGENT_EXTERNAL_SUBJECT_ID:}
|
||||
postgres:
|
||||
enabled: ${TH_HOTEL_BOOKING_PG_ENABLED:false}
|
||||
url: ${TH_HOTEL_BOOKING_PG_URL:}
|
||||
username: ${TH_HOTEL_BOOKING_PG_USERNAME:}
|
||||
password: ${TH_HOTEL_BOOKING_PG_PASSWORD:}
|
||||
schema: th_hotel_booking
|
||||
maximum-pool-size: ${TH_HOTEL_BOOKING_PG_MAX_POOL_SIZE:4}
|
||||
connection-timeout-ms: ${TH_HOTEL_BOOKING_PG_CONNECTION_TIMEOUT_MS:10000}
|
||||
material-download-max-bytes: ${TH_HOTEL_BOOKING_MATERIAL_DOWNLOAD_MAX_BYTES:10485760}
|
||||
retention-enabled: ${TH_HOTEL_BOOKING_RETENTION_ENABLED:false}
|
||||
retention-cron: ${TH_HOTEL_BOOKING_RETENTION_CRON:0 15 3 * * *}
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
Reference in New Issue
Block a user