30 KiB
Security And Privacy
Never Store In Repo / Trellis / Chat
- Passwords, token, Secret, private keys, EncodingAESKey.
- Full personal accounts, full phone numbers, full email addresses where not required for local config.
- Raw invoice images, contract bodies, bank statement originals, payment credentials, sensitive quotes, cost details.
- Complete local paths to confidential company directories.
- Enterprise WeChat raw payloads, full
sp_no, full template IDs, full userid, form values, attachments or comments.
Trellis-Specific Rules
.trellis/workspace/is local-only by default..trellis/tasks/may contain PRD and acceptance context, but only in redacted form.scripts/qa/check-trellis-governance.mjsmust pass before commit.
Scenario: AP Full-Ledger Reading Is Not Payment Review
1. Scope / Trigger
AP financial summary and reimbursement ledger reads, not general entity visibility.
2. Signatures
- Permission:
finance.ledger.viewin both server and client role maps. - Reads:
GET /api/ap-operationsandGET /api/ap-operations/reimbursement-ledger. - Separate export:
POST /api/exports/ap-operations.
3. Contracts
Grant ledger reading to finance, finance_mgr, not ceo, manager or
system_admin. Preserve emergency admin wildcard semantics. Both reads require
the dedicated permission before data queries; query filters cannot confer access.
Frontend route and navigation projections use the same permission. Keep manager
payment review and multi-role permission unions intact; never grant CEO payment
management to preserve read access. Export keeps its existing independent
payment.batch.manage / risk.review.manage rule.
4. Validation & Error Matrix
Feature enabled: missing ledger permission returns 403 / PERM_001, with no
financial query. Feature disabled: the existing 409 refusal may occur first.
Finance/finance manager and manager+finance or ceo+finance retain reading. CEO alone is refused. Old payment or
risk permissions alone no longer imply ledger reading.
5. Good / Base / Bad Cases
- Base: manager keeps payment review but cannot enter AP.
- Good: CEO retains approval and payment review but cannot read AP without a separate finance role.
- Bad: hiding a menu while leaving either backend read unrestricted.
6. Tests Required
Route injection checks both reads, filters and zero queries on refusal; role and navigation tests cover CEO and mixed roles. Browser checks cover manager direct refusal and preserved payment entry, plus allowed AP readers. Export and payment review regressions remain separate from ledger authorization.
7. Wrong vs Correct
Wrong: payment.batch.review implies all-company financial visibility, or a CEO
role exception is added to AP handlers. Correct: use finance.ledger.view in role
maps and read guards. This AP-only change does not alter the manager's six
*.view_all permissions: reimbursement-list all-record visibility remains an
explicit deferred decision, not a completed scope restriction.
Scenario: Bank Account Snapshots And Payment-Order Export
1. Scope / Trigger
- Trigger: company payment-account maintenance, employee payee-bank maintenance, payment-batch creation, full-account reveal or bank payment-order export.
- OA prepares payment evidence and a bank-import file; it does not call a bank payment API or treat the generated file as proof of payment.
2. Signatures
- DB:
BankInstitution,PaymentAccount,Employee.bank_institution_id,PaymentBatch.payment_account_snapshot_json, itemsnapshot_json.payee, andpayment_order_export_*fields. - Reveal APIs use
POSTwith JSON{ "reason": "2-500 chars" }; ordinary read APIs return only bank name and masked account. - Payment-order API:
POST /api/payment-batches/:id/payment-orderwith{}; success is an in-memory BIFF8.xlsdownload withCache-Control: no-store. - Payment-order download confirms sensitive-file handling, but does not require
a typed reason. Re-download warns about duplicate bank import/payment.
Audit records system purpose
reimbursement_bank_transferwithpurposeSource: system; optional legacy user reasons remain genuine input, never fabricated. Account reveal, cancellation and receipt policies stay unchanged. - Filename is
报销制单_<batchNo>.xls, sent with RFC 5987 UTF-8 encoding and ASCII fallback. No names, amounts or account numbers enter the filename. - Runtime: Node passes validated row JSON through stdin to the fixed
fill-payment-order-template.py; the dedicated Python runtime returns BIFF8 bytes on stdout.PAYMENT_ORDER_PYTHON_BINmay select the fixed interpreter, but requests cannot select the script or template path.
3. Contracts
- Employee account editing remains HR/admin-owned. Never grant
employee.bank.manageto finance, finance manager or system administrator. - Company account editing is finance-manager/admin-owned; ordinary finance is masked read plus reason-audited full reveal.
- A new bank batch freezes the exact existing ciphertext for payer and payees. Copy ciphertext directly; decrypt only to calculate the masked suffix. Never decrypt and re-encrypt while building a snapshot.
- Review UI reads masked values from the frozen snapshot. Finance manual-payment reveal also reads the frozen item snapshot, not the employee's current bank profile. An employee profile change after review must not change the payment target for that batch.
- Ordinary batch, AP and export DTOs must explicitly select safe fields and must
never return raw snapshot JSON,
v1:ciphertext or a full account. - Payment-order preflight returns a safe blocker array containing fixed codes and, when useful, the reimbursement business number. Collect every batch and item blocker before returning; do not restore fail-fast "fix one, find one" behavior. The UI maps those fixed codes to natural Chinese and keeps manual payment visible for legacy or unsupported batches.
- Legacy batches without ciphertext snapshots may still complete through manual payment plus receipt, but they can neither export a payment order nor reveal a supposed frozen account through a live-profile fallback.
- A payment-order file is an efficiency artifact. A bank receipt remains the hard evidence required before registering bank payment complete.
- Generate bank files by filling the controlled, blank BIFF8 template asset. Rebuilding a workbook with matching headers is forbidden when it loses the bank template's column widths, row heights, fonts, fills, borders or alignment. The asset contains headers and formatted blank rows only, and its SHA-256 is verified before every fill.
- BIFF8 rich-text runs are part of the template contract. Open the template
with
formatting_info=True, copy every source font property, and explicitly callwrite_rich_text;xlutils.filterdoes not preserve those runs by default. Never replace this with a hard-coded "red" font approximation. - Node owns authorization, snapshot decryption, blockers, ordering, serials, amounts, audit and export counters. Python only copies the fixed template and fills the already validated 15-cell rows. Full accounts travel only on child stdin; never place them in argv, environment variables, logs, stderr or a temporary workbook file.
- CI and the production image install
xlrd,xlwtandxlutilsfrom the same--require-hashesrequirements. Do not fall back to a style-dropping generator when Python, a dependency or the template is unavailable.
4. Validation & Error Matrix
- Missing/mixed legal entity, no unique active payer, missing bank institution,
missing account or decrypt failure -> reject batch creation with
409and no batch/item writes. - First release bank code is not
CCBfor any payer/payee -> reject the whole export; do not emit02, skip a row or create a partial file. - Batch not
reviewed, legacy snapshot or malformed snapshot -> reject export with409; do not increment export count or send file bytes. - Reveal without permission or approved finance business role ->
403. - Reveal/export without a 2-500 character reason ->
400 VALID_001and no full account/file response. - Successful export audit/update failure -> no download response.
- Template hash/structure mismatch ->
409 PAYMENT_ORDER_EXPORT_BLOCKED, no export increment and no file bytes; manual payment plus receipt remains available. - Python executable/dependency missing, timeout, oversized output, non-zero exit or invalid OLE magic -> fixed safe generation error; never copy child stderr into the API error or application log.
5. Good/Base/Bad Cases
- Good: review shows a masked frozen CCB account; finance supplies a reason and temporarily reveals that same frozen account for manual transfer.
- Base: a historical batch displays compatibility metadata, proceeds by manual payment and receipt, and clearly has no payment-order capability.
- Bad: export or manual-transfer reveal re-queries the employee profile after review, silently uses a changed account, or returns raw snapshot JSON.
- Good: a fourth synthetic row copies the first template data-row style and
height while the amount cell changes only to numeric
0.00formatting. - Bad: create a fresh workbook from the 15 labels, or invoke Python with an account in argv/env, even if the resulting values look correct in a parser.
6. Tests Required
- Permission matrix for employee/company account manage, masked read and full reveal; include an explicit system-admin denial.
- Privacy-negative assertions over batch/list/detail/AP responses for raw
accounts,
v1:and raw snapshots. - Batch creation tests for single legal entity, direct ciphertext copy, CCB-only, decrypt failure and legacy behavior.
- BIFF8 template-fill tests plus independent readback for three sheets, 15
headers, leading-zero
01, long account text, Chinese, numeric0.00amounts and deterministic row order. Compare visual signatures for column widths, row heights, font/bold, fill, border, alignment and wrapped text; include a row beyond the three preformatted template rows. - Compare rich-text segments semantically, including start offsets and complete
font properties. Required headers must assert the first
*uses the source red font and all remaining text uses the source black font; whole-cell style equality is insufficient. - Negative tests cover missing Python/dependencies, template hash drift, child failure and a child that echoes the synthetic input to stderr; exposed errors and logs must not contain the account or original row payload.
- Build one synthetic batch with at least two independent payer/payee blockers; assert the DTO/error contains both safe blocker entries and emits no file, export counter or success audit.
7. Wrong vs Correct
Wrong
const liveEmployee = await prisma.employee.findUnique({ where: { id: ownerId } });
return decryptStringOrThrow(config, liveEmployee.bank_account_encrypted);
Correct
const encrypted = paymentBatchItem.snapshot_json?.payee?.bankAccountEncrypted;
assertReviewedFinanceReveal({ batch, actor, reason, encrypted });
await writeRevealAudit();
return decryptStringOrThrow(config, encrypted);
Scenario: Defensive Security Finding Summaries
1. Scope / Trigger
- Trigger: a security review or remediation task writes findings into ordinary repository-visible Trellis material, specs, reports or management summaries.
- This contract governs the shareable summary. It does not replace focused automated tests or the archived remediation task that carries engineering proof.
2. Signatures
- Allowed summary fields:
findingId,riskCategory,discoverySeverity, repo-relativecodeLocations,businessImpact,controlObjective,safeVerification,remediationStatus,deploymentStatus,externalVerificationStatus,evidenceRef. - Forbidden summary fields: exploit procedure, payload, bypass sequence, probability/throughput calculation, raw request/response, real-environment identifier or secret-bearing value.
3. Contracts
- Keep the finding ID, risk category, discovery severity, repo-relative code locations, business impact, control objective, safe verification result and the separate remediation/deployment/external states.
- Do not store attack procedures, working payloads, bypass instructions, probability calculations, composable endpoint sequences, raw provider or browser payloads, or identifying details from a real environment.
- Phrase verification as a harmless control assertion, such as an unauthorized request being rejected, active content being forced to a non-executable download, or a negative-path test passing with synthetic data.
- Detailed engineering proof belongs in focused automated tests and the archived remediation task. Ordinary finding ledgers reference that evidence instead of copying sensitive mechanics.
- If evidence cannot be safely summarized, keep only metadata and a restricted evidence reference; do not place the raw material in the repository or chat.
4. Validation & Error Matrix
- Safe code location + defensive result -> allowed.
- Real account, token, attachment, contract body, business payload or complete environment detail -> remove and regenerate the evidence.
- Reproduction text that enables misuse -> replace with the control objective and a synthetic negative-path assertion.
- Local remediation pass without deployment/public evidence -> keep deployment and external behavior pending.
5. Good/Base/Bad Cases
- Good: identify the affected module, state the business impact and assert that a synthetic unauthorized or active-content negative path is rejected.
- Base: an unresolved static risk keeps a safe code reference and control objective while external behavior remains gated.
- Bad: copy a working request sequence, payload, real endpoint context or real data fragment into a PRD or finding ledger.
6. Tests Required
node scripts/qa/check-sensitive-content.mjs.- Manual review of security summaries for procedural or identifying detail.
- Focused positive/negative automated tests using synthetic fixtures.
7. Wrong vs Correct
Wrong
{
"findingId": "SEC-001",
"reproduction": "step-by-step operational procedure",
"rawPayload": "real or executable content"
}
Correct
{
"findingId": "SEC-001",
"riskCategory": "authorization boundary",
"businessImpact": "data outside the actor's scope may be exposed",
"controlObjective": "deny by default and enforce object scope",
"safeVerification": "synthetic unauthorized request is rejected"
}
Scenario: Business Attachment Governance
1. Scope / Trigger
- Trigger: adding or changing OA business attachments for reimbursement, purchase, payment, contract, project or company-document workflows.
- Applies when an attachment is stored by the OA and can later be previewed, downloaded, reviewed or archived.
- Do not introduce a generic file-center or free directory upload unless a Trellis task explicitly reopens that architecture.
2. Signatures
- DB fields on business attachment models:
governance_status,sensitivity_level,attachment_type,reviewed_by,reviewed_at,review_note,archived_by,archived_at,archive_note,download_policy. - Upload multipart fields may include:
governanceStatus,sensitivityLevel,attachmentType,downloadPolicy. - API response fields must use camelCase:
governanceStatus,sensitivityLevel,attachmentType,downloadPolicy,reviewedBy,reviewedAt,reviewNote,archivedBy,archivedAt,archiveNote. - Download APIs may accept
reason; sensitive or reason-required attachments must reject downloads without a non-empty reason. - Upload content-policy inputs are
{ buffer, fileName, declaredMimeType, maxBytes, normalizeImages }; accepted output must contain canonicalbuffer,extension,mimeType,fileSizeandfileSha256. - Browser downloads of OA-stored attachments must use a currently
authenticated request, preferably an API-local
POSTwith optional JSON{ "reason": "..." }, rather than a query bearer URL. The only approved query-grant exception is the at-most-120-second, purpose/object/actor-bound grant used by the official WeCompreviewFilenative layer; followwecom-boundary.mdand do not reuse that grant for ordinary browser downloads or other integrations. Payment-batch receipt preview is a separate, authenticated domain route described below; it never uses a bearer query URL.
3. Contracts
- Default status is
approvedunless the business workflow has a stricter state such aspending_review. - Default sensitivity is
internal; payment receipts and similarly sensitive financial proof should useconfidential. index_onlymeans OA stores the index/metadata, not the original file.- Frontend labels are informational only; backend permission and download reason checks remain authoritative.
- Audit logs for downloads must include actor, business target, attachment ID, attachment type, sensitivity level, governance status and sanitized reason when provided.
- New uploads must use an allowlist and jointly verify the final extension, declared MIME and actual bytes before storage. Original names are display metadata only; storage keys use a server-generated random identifier and a canonical extension.
- PDF basic validation requires a strict header and terminal EOF boundary. Safe
image formats must first match fixed signature bytes, then decode with Sharp
using
failOn: "warning", a fixed pixel ceiling and a one-page constraint; new images are re-encoded without metadata before size/hash/MIME persistence. - A governance state, existing hash, extension or stored MIME never proves historical content safe. Every download must re-authorize the current actor and business object, read under a hard byte limit and re-run the actual-byte policy before any response bytes are sent.
- Successful download audit is part of authorization: write it before setting attachment headers or sending bytes. Audit failure must return a non-success response with no file body.
- Successful ordinary browser downloads use
application/octet-stream,attachment,nosniff, private no-store caching, no-referrer and a route-level sandbox CSP. Do not restore general same-origin inline preview or query-token download routes. The WeCom-native reimbursement invoice/attachment exception retains the domain-specificinlineresponse and must still audit before bytes, use no-store/no-referrer and reject expired or mismatched grants. - The only ordinary-browser inline exception is
GET /api/payment-batches/:id/receipt-attachments/:attachmentId/file?mode=preview. It remains batch- and department-authorized, revalidates bounded actual bytes, accepts only PDF/JPEG/PNG/WebP, writesPAYMENT_BATCH_RECEIPT_PREVIEWEDwith fixed reason付款批次内在线预览before sending bytes, and returns the validated MIME withinline,nosniff, private no-store, no-referrer, sandbox CSP and same-origin resource policy. Default/download mode keeps forced binary download and reason-required audit behavior. Do not generalize this exception into a reusable file-center preview route.
4. Validation & Error Matrix
- Unknown governance enum in upload -> normalize to the module default, do not persist arbitrary strings.
- Sensitive or
reason_requiredattachment download without reason ->400 VALID_001with{ reason: "required" }. - Missing attachment or missing stored object ->
404. - User lacks business read permission ->
403 PERM_001. - Download reason longer than the server limit -> trim before writing audit metadata.
- Unsupported/active bytes, extension-MIME-content mismatch, dangerous file name, corrupt/truncated content, animation/multiple pages or pixel/byte limit breach -> reject before object-storage and database writes.
- Explicitly quarantined, missing, oversized, corrupt or unverifiable stored object -> do not return file bytes; write a fixed blocked-reason audit where possible.
- Payment receipt preview with a MIME outside PDF/JPEG/PNG/WebP -> blocked audit with fixed reason code; no inline bytes.
- Successful-download audit write fails -> return 5xx before download headers or file bytes.
5. Good/Base/Bad Cases
- Good: a bank receipt upload returns
confidential,receipt,reason_required; download first asks for用途 and writes an audit log. - Good: an authorized payment-detail preview uses the fixed server purpose and writes its preview audit; downloading the same receipt still asks for用途.
- Base: a reimbursement supporting material upload defaults to
internal,supporting_material,standardand remains downloadable by authorized participants. - Good: an allowed image is signature-checked, strictly decoded, normalized, stored under a random canonical key, then later revalidated and returned only as a forced binary download.
- Base: an allowed historical PDF remains byte-for-byte unchanged in storage; every access still revalidates it and forces attachment download.
- Bad: a page hides the download button but the backend route still streams the file without permission or reason checks.
- Bad: Enterprise WeChat messages or reports include original attachments instead of OA links and desensitized summaries.
- Bad: trust
multipart.mimetype, reuse an original basename in the storage path, or return a stored MIME withinlinefrom the OA origin.
6. Tests Required
- Prisma validate after schema or migration changes.
- Backend route tests for default governance fields, custom multipart fields
when exposed, sensitive-download reason rejection and audit
extrametadata. - Content-policy tests for every allowed type plus active/unknown content, mismatch, truncation, warning-only JPEG corruption, animation/multiple pages, pixel/byte limits, metadata removal, random keys and safe disposition names.
- Route tests must prove immediate bounded multipart consumption, rejection before storage/DB writes, current authentication and object authorization, historical revalidation, blocked-audit reasons, audit-before-send and the complete forced-download header contract.
- Payment receipt tests additionally assert preview MIME/disposition/security headers, fixed-purpose audit-before-send, PDF/image allowlist, blocked files, and that default download still rejects a missing reason.
- Frontend typecheck/build after API type changes.
- Focused UI verification for pages that add attachment governance labels or download-reason interactions.
- Frontend ordinary-download tests must assert authenticated JSON request + Blob
browser download. WeCom-native preview tests instead assert the official
bridge receives the exact short URL/name/size and that
window.openis not used. Use only synthetic in-memory fixtures; production attachments and historical bulk scanning require a separate approval.
7. Wrong vs Correct
Wrong
const storageKey = `${businessId}/${originalFileName}`;
await storage.writeBuffer({ key: storageKey, buffer, contentType: clientMime });
return reply.type(clientMime).header("content-disposition", "inline").send(buffer);
Correct
const validated = await validateActualAttachmentBytes({
buffer,
fileName,
declaredMimeType,
normalizeImages: false,
});
const reason = sanitizeDownloadReason(request.body?.reason);
if (requiresDownloadReason(attachment) && !reason) {
throw buildValidationError({ reason: "required" });
}
await prisma.auditLog.create({ data: buildDownloadAudit({ attachment, reason }) });
return reply
.type("application/octet-stream")
.header("content-disposition", buildSafeAttachmentDisposition(validated.originalName))
.header("x-content-type-options", "nosniff")
.header("cache-control", "private, no-store")
.send(validated.buffer);
Scenario: Company Documents And OA Collaboration
1. Scope / Trigger
- Trigger: mapping company electronic documents, policy files, operations guidance, employee collaboration tasks or external-review materials into OA.
- Applies to docs, specs, evidence, tasks, OA attachments, Enterprise WeChat messages, AI/Codex-assisted processing and storage decisions.
2. Contracts
- OA is the operations control layer, not the full original-document library.
- The controlled company document master keeps full originals, historical packages and high-sensitivity materials.
- OA may store indexes, business object links, status, responsibility, hash or checksum summaries, archive-location summaries, review results and audit evidence.
- Necessary business originals may enter OA only through scoped business attachment flows such as contract, reimbursement, purchase, payment, project or policy workflows.
- Employee collaboration, external review, generated reports, Enterprise WeChat messages and AI/Codex work use redacted copies, aliases or summaries.
- Do not build or describe a generic file center unless a Trellis task explicitly reopens that architecture and defines permissions, audit, storage, backup and retention rules.
3. Storage Decision Defaults
- Current V0 may keep authorized business attachments on the deployment server, but it must not become a shared drive or full document mirror.
- If storage is insufficient, prefer index-only records plus controlled offline retrieval before moving high-sensitivity originals online.
- Cloud object storage or controlled NAS/MinIO must be private, accessed by the OA service account, and governed by OA permissions and download audit.
- A home NAS can be cold archive, secondary backup or low-risk S3-compatible trial storage; it is not the default production attachment store unless VPN, private bucket, UPS, alerts and restore drills are in place.
4. Forbidden Content
- Full local paths to confidential directories.
- Passwords, token, Secret, private keys, source packages and complete configuration files.
- ID-card, bank-card, payroll, social-security, tax and full bank-statement originals in normal OA attachment areas.
- Customer original material, sensitive quotes, contract bodies or signed packages outside authorized business flows.
Scenario: External Delivery Error Logging
1. Scope / Trigger
- Trigger: logging SMTP, webhook, object-storage or other provider delivery failures whose native error objects may contain recipients or payloads.
2. Signatures
- Email log metadata:
errorCode, optionalproviderCode, optionalsmtpCommand, optional numericsmtpResponseCode, request/business IDs and recipient count. - Forbidden input: raw provider
Errorobjects in structured logs.
3. Contracts
- Log only explicitly allowlisted scalar metadata.
- Do not log
response,rejected,rejectedErrors,envelope, addresses, raw messages, headers, credentials or payloads. - Provider codes must match a bounded token format; SMTP commands must come from a fixed safe set; response codes must be bounded integers.
- Delivery failure counts remain observable without exposing recipient data.
4. Validation & Error Matrix
- Known safe provider code/command/response code -> include normalized fields.
- Unknown or free-form field -> omit it.
- Error with rejected address or SMTP response text -> log generic category and safe numeric/token fields only.
- Missing logger -> return the delivery result without throwing a logging error.
5. Good/Base/Bad Cases
- Good: record
EENVELOPE,RCPT TO,550, request ID and failed count. - Base: plain
Errorbecomes onlyerrorCode=email_delivery_failed. - Bad: pass Nodemailer's raw
errto Pino, which serializes rejected addresses.
6. Tests Required
- Mock a provider error containing a rejected full address, response and envelope.
- Assert serialized log context contains none of those values and retains only allowlisted metadata.
- Cover disabled, configured, success and failure delivery paths.
7. Wrong vs Correct
Wrong
logger.error({ err: providerError, recipient }, "delivery failed");
Correct
logger.error({ ...buildSafeErrorLog(providerError), requestId, failed }, "delivery failed");
Scenario: Current Fact Source Sensitive Gate
1. Scope / Trigger
- Trigger: adding, registering or changing a CURRENT fact source, README, deployment entry or environment template.
2. Signatures
- Gate:
node scripts/qa/check-sensitive-content.mjs. - Registry:
docs/文档事实源状态矩阵.mdrows whose status isCURRENTand contract path is underdocs/. - Output:
fileRef,line,category; never the matched text.
3. Contracts
- Scan fixed execution/deployment entrypoints plus every CURRENT docs contract derived from the status matrix; do not maintain a partial duplicate docs list.
- Detect secret assignments, credential URLs, full accounts, personal workspace paths, private keys and public IPv4 values.
- Allow explicit placeholders, environment interpolation and loopback addresses.
- Missing registry files remain a docs-audit concern; existing scanned files must not be silently excluded.
4. Validation & Error Matrix
- Any sensitive match ->
FAILwith non-zerofindingCount. - CURRENT doc added to the matrix -> automatically enters the scan set.
- EVIDENCE/HISTORICAL issue row -> does not enter the CURRENT docs scan set.
- Placeholder such as
<repo-root>-> pass.
5. Good/Base/Bad Cases
- Good: all current docs and deploy entrypoints scan clean with metadata-only output.
- Base: historical evidence stays outside this current-source blocking gate.
- Bad: hard-code six docs while the matrix contains eighteen CURRENT docs.
6. Tests Required
- Parser fixture with CURRENT doc, historical doc and CURRENT Issue row.
- Assertion that representative current manuals/specs appear in the resolved default set.
- Repository gate must report
findingCount=0and the expected expanded file count after registry changes.
7. Wrong vs Correct
Wrong
const files = ["docs/文档索引.md", "docs/产品设计规则.md"];
Correct
const files = [...fixedEntrypoints, ...extractCurrentDocumentFiles(statusMatrix)];