B-64: 安全修复——随机初始密码与跨公司重复上传隔离

- 公司账号创建改为随机一次性初始密码,只在创建响应中显示一次,
  密码保证不等于用户名;删除「用户名即初始密码」兼容分支,继续
  强制首次登录改密。
- 跨公司相同字节文件上传只返回通用重复状态:不再返回其他公司的
  原批次 ID、银行、模板、期间、交易数或诊断;同公司重复上传的
  幂等摘要保持可用。
- 补充服务端回归测试,覆盖同公司与跨公司两个分支及随机密码;
  完整测试 85 项全绿,node --check 通过。
This commit is contained in:
腾讯WorkBuddy
2026-08-16 01:50:42 +08:00
parent 545837446c
commit 7f1a93f6a6
11 changed files with 155 additions and 58 deletions
+8 -2
View File
@@ -53,8 +53,13 @@ def verify_password(password: str, stored: str) -> bool:
return hmac.compare_digest(digest, expected)
def generate_initial_password() -> str:
"""Generate a 12-char initial password with upper, lower and digit chars."""
def generate_initial_password(exclude: str | None = None) -> str:
"""Generate a 12-char initial password with upper, lower and digit chars.
When ``exclude`` is given, the result is guaranteed to differ from it
(case-insensitive) so a fresh account never starts with a password equal
to its own username.
"""
alphabet = string.ascii_letters + string.digits
while True:
password = "".join(
@@ -64,6 +69,7 @@ def generate_initial_password() -> str:
any(char.isupper() for char in password)
and any(char.islower() for char in password)
and any(char.isdigit() for char in password)
and (exclude is None or password.lower() != exclude.lower())
):
return password
+15 -4
View File
@@ -29,6 +29,7 @@ class ImportResult:
source_file_id: int
batches: tuple[StatementBatch, ...] = ()
message: str | None = None
duplicate_same_company: bool = False # only meaningful when status == 'duplicate'
def import_statement(
@@ -96,7 +97,7 @@ def _record_duplicate(
) -> ImportResult:
original = connection.execute(
"""
SELECT id FROM import_batches
SELECT id, company_id FROM import_batches
WHERE source_file_id = ? AND status = 'parsed'
ORDER BY id LIMIT 1
""",
@@ -105,7 +106,7 @@ def _record_duplicate(
if original is None:
original = connection.execute(
"""
SELECT id FROM import_batches
SELECT id, company_id FROM import_batches
WHERE source_file_id = ? AND status != 'duplicate'
ORDER BY id LIMIT 1
""",
@@ -113,7 +114,7 @@ def _record_duplicate(
).fetchone()
now = utc_now()
with connection:
connection.execute(
cursor = connection.execute(
"""
INSERT INTO import_batches
(source_file_id, status, duplicate_of_id, company_id, diagnostics, created_at, updated_at)
@@ -128,12 +129,22 @@ def _record_duplicate(
now,
),
)
# A duplicate is only "same company" when the uploader belongs to the
# same tenant that owns the original batch. Cross-company duplicates
# must stay opaque: the caller gets the new duplicate batch id (which
# belongs to its own company) but never the other company's batch id.
same_company = original["company_id"] == company_id
if same_company:
batch_id = original["id"]
else:
batch_id = int(cursor.lastrowid)
return ImportResult(
original["id"],
batch_id,
"duplicate",
sha256,
source_file_id,
message="相同内容的文件已导入,本次按重复上传处理。",
duplicate_same_company=same_company,
)