feat(安全+品牌): 对外产物发布脱敏(3注入点/2465文件0泄露) + Kole Cup 定稿
Regression / regression (push) Canceled after 0s
Regression / regression (push) Canceled after 0s
安全:CHANGELOG.md 是仓库内部变更流水(含服务器目录、镜像回滚标签、内网网段、
部署时序、AI 工作流用语),此前被 build-site.ps1 / precompute.mjs / build-mobile.mjs
原样注入站点,而站点是公网可下载的静态文件 —— 抓一次 /site/m/changelog.html
即可拿到内网地址段与服务器目录布局。
- 新增 tools/lib/redact-publish.mjs(零依赖,构建期过滤,不改 CHANGELOG.md,
内部可追溯性完整保留)
- 接入 precompute.mjs(PC data.json/changelog.json)与 build-mobile.mjs
(移动端更新日志页);build-site.ps1 不碰(ASCII-only 铁律)
- 只处理 changelog 字段:components[].sources 是规范实现源码,逐字保真
(详情页代码区主动高亮注释,剥注释会破坏该功能)
- 实测消除:/opt/aurora-admin.prev-*、kole-ui-showcase:pre-*、docker compose、
192.168.5.7、16 位产物指纹、1531/1531、并发会话/本会话/派子 agent
- settings.html 演示占位 IP 192.168.5.0/24(= 真实网段)改为 RFC 5737 的 192.0.2.0/24
品牌:Kole Cup 饮料杯标记定稿(几何 K → 圆角杯盖 + 杯身负空间 K,无吸管),
brand-mark.json 升 schemaVersion 3(paths 支持 { d, evenodd }),
verify:brand 增至 25 条(新增 B9b:负空间必须带 fill-rule)。
验证:发布集 2465 文件全量扫描 0 泄露;PC 回归 100%(1464/1464) ·
移动端 100%(807/807);门禁品牌 25 / 隔离 31 / 移动文档 12 / 示例 9 / 版本 40 / i18n 17 全绿;
已按 AGENTS §九 发布公网,2461/2461 逐字节一致,五项验收全过。
已知未处理(既有缺口,ROADMAP S7-P28 已记录):data.mobile.json 的
meta.generated 为墙上时钟,会让 CI 的「生成物可复现」断言在重跑构建后永远非空;
该 CI 流水线本身亦从未通过(无 runner)。
This commit is contained in:
+23
-11
@@ -5,13 +5,23 @@ export const BRAND_SPEC_REL = '.design_library/kole-ui/brand/brand-mark.json';
|
||||
export const BRAND_DATA_REL = 'site/brand-mark.generated.json';
|
||||
export const BRAND_ASSET_REL = 'site/assets/kole-mark.svg';
|
||||
export const BRAND_MONO_REL = 'site/assets/kole-mark-mono.svg';
|
||||
export const BRAND_SCHEMA_VERSION = 2;
|
||||
export const BRAND_SCHEMA_VERSION = 3;
|
||||
|
||||
export function readBrandSpec(root) {
|
||||
const file = path.join(root, BRAND_SPEC_REL);
|
||||
return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
|
||||
}
|
||||
|
||||
/** 路径项归一化:允许写成字符串(简化)或对象 { d, evenodd }(负空间需要 evenodd)。 */
|
||||
function normalizePaths(raw) {
|
||||
if (!Array.isArray(raw)) return [];
|
||||
return raw.map((item) => {
|
||||
if (typeof item === 'string') return { d: item, evenodd: false };
|
||||
if (item && typeof item.d === 'string') return { d: item.d, evenodd: !!item.evenodd };
|
||||
return { d: '', evenodd: false };
|
||||
});
|
||||
}
|
||||
|
||||
export function validateBrandSpec(spec) {
|
||||
const errors = [];
|
||||
if (!spec || spec.schemaVersion !== BRAND_SCHEMA_VERSION) {
|
||||
@@ -20,9 +30,9 @@ export function validateBrandSpec(spec) {
|
||||
if (!spec || spec.viewBox !== '0 0 24 24') errors.push('viewBox 必须为 0 0 24 24');
|
||||
|
||||
const g = spec?.geometry || {};
|
||||
const paths = g.paths;
|
||||
if (!Array.isArray(paths) || !paths.length || paths.some((d) => typeof d !== 'string' || !d.trim())) {
|
||||
errors.push('geometry.paths 必须包含至少一条非空路径');
|
||||
const paths = normalizePaths(g.paths);
|
||||
if (!paths.length || paths.some((p) => !p.d.trim())) {
|
||||
errors.push('geometry.paths 必须包含至少一条非空路径(字符串或 { d, evenodd })');
|
||||
}
|
||||
if (g.mode !== 'fill' && g.mode !== 'stroke') errors.push('geometry.mode 必须为 fill 或 stroke');
|
||||
if (g.mode === 'stroke') {
|
||||
@@ -31,7 +41,7 @@ export function validateBrandSpec(spec) {
|
||||
if (g.linejoin !== 'round') errors.push('stroke 模式的 geometry.linejoin 必须为 round');
|
||||
}
|
||||
if (g.opacities !== undefined) {
|
||||
if (!Array.isArray(g.opacities) || g.opacities.length !== (paths || []).length) {
|
||||
if (!Array.isArray(g.opacities) || g.opacities.length !== paths.length) {
|
||||
errors.push('geometry.opacities 必须与 paths 等长');
|
||||
} else if (g.opacities.some((v) => typeof v !== 'number' || !(v > 0) || v > 1)) {
|
||||
errors.push('geometry.opacities 取值必须在 (0, 1] 区间');
|
||||
@@ -47,13 +57,14 @@ export function validateBrandSpec(spec) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
/** 逐路径生成 <path>:实心模式按 opacities 给 fill-opacity;描边模式交给外层 <g> 统一给描边属性。 */
|
||||
/** 逐路径生成 <path>:负空间路径必须带 fill-rule="evenodd",否则挖空不会生效。 */
|
||||
function pathsMarkup(spec) {
|
||||
const { paths, mode, opacities } = spec.geometry;
|
||||
return paths
|
||||
.map((d, i) => {
|
||||
const { mode, opacities } = spec.geometry;
|
||||
return normalizePaths(spec.geometry.paths)
|
||||
.map((p, i) => {
|
||||
const opacity = mode === 'fill' && opacities && opacities[i] != null ? ` fill-opacity="${opacities[i]}"` : '';
|
||||
return `<path d="${d}"${opacity}/>`;
|
||||
const rule = p.evenodd ? ' fill-rule="evenodd"' : '';
|
||||
return `<path d="${p.d}"${rule}${opacity}/>`;
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
@@ -93,7 +104,8 @@ export function generatedData(spec) {
|
||||
viewBox: spec.viewBox,
|
||||
geometry: {
|
||||
mode: spec.geometry.mode,
|
||||
paths: spec.geometry.paths,
|
||||
/* 归一化后再写出:字符串项补成 { d, evenodd } 对象,保证下游拿到的结构稳定一致。 */
|
||||
paths: normalizePaths(spec.geometry.paths),
|
||||
...(spec.geometry.opacities ? { opacities: spec.geometry.opacities } : {}),
|
||||
...(spec.geometry.mode === 'stroke'
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user