Skip to content

feat(pe): inspect Authenticode certificate tables - #4830

Merged
morluto merged 12 commits into
morluto:mainfrom
mvanhorn:fix/pe-certificates
Oct 22, 2026
Merged

morluto merged 12 commits into
morluto:mainfrom
mvanhorn:fix/pe-certificates

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add inspect_pe_certificates / inspect-pe-certificates for one explicit
local PE32/PE32+ image. The workflow reads
IMAGE_DIRECTORY_ENTRY_SECURITY as a file-offset WIN_CERTIFICATE table and
returns entry ranges, types, payload SHA-256, and structurally decoded PKCS#7
signedData signer/X.509 identity fields when type 2 is present.

It does not execute the image, verify signatures, chains, timestamps, or
Authenticode digest coverage. A missing security directory is observed absence
of that table, not proof the image is unsigned in every catalog or overlay
sense. Malformed PE headers, truncated entries, and non-signedData PKCS#7 fail
without partial success. No Windows SDK is required.

Usage

rea inspect-pe-certificates /absolute/path/to/application.exe --json

MCP: inspect_pe_certificates with an absolute path.

Tests

Merge resolution at 3ca73d13a against main (c7efd3a38): preserves both PE certificate inspection and native export inspection across CLI/MCP registration, composition, effect metadata, and local-contract validation.

  • npm run test:focused selecting the PE certificate domain/parser/service, CLI/MCP/filesystem tests, tool-effects and local-contract tests, and native-export/PE-resource filesystem tests — exit 0, 10 files, 51 tests passed. Runtime and packaged-skill build passed.
  • npm run check — exit 0.
  • npm run docs:generate and npm run docs:check — exit 0; includes 9 README consistency tests.
  • Full suite and real Windows image verification were not run for this merge resolution; the CLI/MCP workflows used fixtures.

Earlier validation:

  • npm run check:fast
  • npm run test:local -- domain, parser, and service tests (10 passed)
  • npm run test:focused -- CLI, MCP, and filesystem tests (7 passed on this
    feature; plus the shared local-contract NUL case)

Demo

demo

Author readiness

Human owner: @mvanhorn

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-17T16:18:35.170210Z 6d1755f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@greptile-apps

greptile-apps Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5 Tier: plus

[Medium impact] The PR appears safe to merge, with a non-blocking correction needed for older certificate name encodings.

Findings

  1. P2 Older certificate names change ▶
Fix with agent prompt
### Issue 1
src/native/pe/PeCertificateDer.ts:227-229
Names stored as `TeletexString` (`0x14`) fall through to Latin-1 here. For example, the valid T.61 bytes `c2 65` become `Âe` instead of `é`. Inspection succeeds, but the returned issuer and subject names contain the wrong text. This is a non-blocking concern for older certificates. Decode this encoding correctly, or reject unsupported name encodings instead of silently treating them as Latin-1.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds inspect_pe_certificates and inspect-pe-certificates for one selected PE image.

  • A selected PE image now gets a report of its certificate table and embedded signer details.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  CLI["CLI: resolve selected path"] --> Service["Check input"]
  MCP["MCP: absolute selected path"] --> Service
  Service --> Reader["Read bounded, stable regular file"]
  Reader --> PE["Read PE headers and certificate table"]
  PE --> DER["Decode type-2 PKCS#7 within limits"]
  DER --> Evidence["Return ranges, hashes, and identity fields"]
Loading

Reviews (7) · Last reviewed commit: "fix(merge): preserve PE certificate and ..." · Reviewed by Greptile

Comment thread src/native/pe/PeCertificateDer.ts
Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread src/native/pe/PeCertificateParser.ts Outdated
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Addressed all five Greptile findings in e68166d, each with a regression test:

  1. Certificate payloads can exhaust memory: readDer now shares one node budget across the whole payload, so sibling fan-out stops before allocating past it.
  2. Truncated PKCS#7 succeeds: SignedData now requires version, digestAlgorithms, encapContentInfo and a final signerInfos SET in RFC 5652 order, and each SignerInfo requires its signature OCTET STRING.
  3. Certificate identifiers change value: decodeOid decodes every arc as an unbounded base-128 value and splits the first combined arc afterwards, so 2.999.3 and 2.25.4294967296 round-trip.
  4. Certificate names get corrupted: UniversalString is decoded as UTF-32BE, and invalid lengths or code points are rejected.
  5. Broken PE headers succeed: the certificate parser now uses the same header admission as the resource layout (readPeHeaders), so a section table that does not fit SizeOfHeaders is rejected.
    I also merged main (a50bb02, which passes signal to finishAnalysisCommand for the new command) and mirrored the PE certificates README row into the translated READMEs (7eef463), which clears the documentation links check behind Docs Generation and the test shards.

Comment thread src/native/pe/PeCertificateDer.ts

@CassieFox23 CassieFox23 left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR: I checked the two open Greptile threads against 7eef463. One still happens. A very long OID makes the cert parser stall for about 20 seconds. The other does not: a truncated PKCS#7 payload is rejected.

Edit: I retested this PR at head e1e756c on Node 22, 24 and 26; the results are here.

I ran named test files and small local probes. I did not run any real signed program or a live server.

Findings and verdicts

Head 7eef463. "Reproduces" means a probe I wrote shows the problem. "Does not reproduce" means the same probe now gives the safe answer.

# Thread Sev Verdict What I saw
6 Oversized OIDs freeze requests P1 Reproduces A type-2 entry with a 262,144-byte OID inside a 262,688-byte PE took 19,930 ms in parsePeCertificates with the default limits. A normal 9-byte OID took 0 ms
2 Truncated PKCS#7 succeeds P1 Does not reproduce SEQUENCE { INTEGER 1, SET {} } is rejected ("missing encapContentInfo"). A SignerInfo with no signature field is rejected ("missing its signature OCTET STRING")

How the stall grows. Each row is one run of parsePeCertificates on a PE with one type-2 entry. The OID is that many bytes of 0xff ending in 0x7f.

OID bytes Time
16,384 19 ms
32,768 63 ms
65,536 234 ms
131,072 1,001 ms
262,144 19,930 ms

The parser decodes the contentType OID before it checks for signedData. The loop keeps growing one BigInt, and the DER node budget does not limit one OID's length. The 262,688-byte file is far below the 64 MiB default file limit. A cap on OID length and arc size before the loop would stop it.

Two notes on the table. The OID needs non-zero high bits to grow: bytes of 0x80 stay at zero and finish instantly. The box was shared with other test jobs, and I ran each size once. Read the 19.9 seconds as one run, not a benchmark.

Two other shapes of thread 2 are still accepted. One is an empty OCTET STRING signature (04 00). The other is an empty signerInfos SET {}. Neither is the exact case in the thread. The tool says it does not verify signatures, so I did not count them as a hit. Read "removing the signature bytes" literally and the 04 00 shape is a partial match.

More on the truncated PKCS#7 thread

Three shapes are rejected. One is the bot's SEQUENCE { INTEGER 1, SET {} }. One has no signerInfos. One has a SignerInfo with no signature field. A full SignerInfo is accepted, as it should be.

Test results, 8 test files this PR touches:

Node Result
24.18.0 8 files, 35 tests passed

Not run

  • A live MCP or CLI server stall. I called the parser function only, so I did not see a server freeze.
  • No real signed Windows apps. Every input was built by script.
  • Node 22 and 26. I used Node 24 only.
  • The four resolved threads. I only noted them as resolved.
  • Main and the PR base. Main has no copy of this feature, so it has no fix either.
  • Whether a maintainer already commented on the PR.

Thanks, mvanhorn, for the work on this.

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.9
  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads
  • RAM: 32 GB DDR4-3600
  • Node: 24.18.0
  • Head / base: 7eef463 / 36d156f (main was d6ca7b3)
  • Other: the box was shared with other test jobs, so treat the timings as rough.
Commands
npm --prefix <tree> ci --ignore-scripts
npm --prefix <tree> run build:cached
npm --prefix <tree> run test:prepare
npm --prefix <tree> run test:focused -- <8 files>   (Node 24)
node <probe>.mjs   (imports dist/native/pe/PeCertificateDer.js and PeCertificateParser.js from the built tree)

The 8 files: src/native/pe/PeCertificateParser.test.ts, src/domain/native/peCertificates.test.ts, src/application/binaryDiagnostics/PeCertificatesService.test.ts, src/contracts/toolEffects.test.ts, tests/acceptance/peCertificatesCli.test.ts, tests/acceptance/tools/localToolContracts.test.ts, tests/boundary/filesystem/peCertificates.test.ts, tests/boundary/mcp/peCertificatesMcp.test.ts.

None of them mentions doctor; I checked with grep first.

Probe for the oversized OID, in the shape of the DER bytes:

ContentInfo ::= SEQUENCE {
  OBJECT IDENTIFIER, content = (N - 1) bytes of 0xff, then 0x7f
  [0] SEQUENCE { INTEGER 1 } }

The payload sits in a WIN_CERTIFICATE entry of type 2 in a minimal PE32 image, and parsePeCertificates runs with the schema defaults (max_file_bytes 67,108,864; max_entries 8).

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

@mvanhorn

Copy link
Copy Markdown
Contributor Author

Thanks for checking. c653f48 caps OBJECT IDENTIFIER length at 256 bytes and each arc at 32 bytes before decodeOid runs. The 262,144-byte OID case now fails with "OBJECT IDENTIFIER exceeds 256 bytes" in under a millisecond, and there is a regression test for it. I left the truncated PKCS#7 path as is since it did not reproduce. I also merged current main; format, lint, typecheck, build and the focused PE certificate tests pass locally.

Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread src/native/pe/PeCertificateParser.ts Outdated
@CassieFox23

CassieFox23 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR: I retested e1e756c. My oversized-OID finding is fixed: every size now fails in under 2 ms. Two of the three open bot threads still reproduce. One huge certificate name uses about 16 times its size in heap. A 4-byte zero table is reported complete. The old truncated-PKCS#7 thread is still not reproduced.

Edit: this comment first said I was retesting. The results are below.

I only ran files I made and named test files. I did not run any real signed program or a live server. "Reproduces" means a probe I wrote shows the problem in the thread.

My earlier findings

Finding At 7eef463 At e1e756c Fixed?
Oversized OIDs freeze requests A 262,144-byte OID in a 262,688-byte PE took 19,930 ms Same file: 0 ms, rejected with "OBJECT IDENTIFIER exceeds 256 bytes." Yes
Truncated PKCS#7 succeeds Not reproduced Not reproduced, same answers Unchanged

For the OID, every size I tried before (16,384 up to 262,144 bytes) now fails in 0 to 1 ms. A normal OID still parses. A 256-byte OID is decoded, a 257-byte one is rejected, and a real 2.25 UUID-style arc still decodes.

The 3 open bot threads

# Thread Sev Verdict What I saw at e1e756c
1 Certificate names exhaust memory P1 Reproduces One BMPString subject value (one DER node). The parser kept 128 MiB of heap for an 8 MiB value, 512 MiB for 32 MiB, and 896 MiB for 56 MiB. That is 16 times the value's size each time. A UniversalString costs 8 times. A UTF8String of the same size costs none. A crash was not reached
2 Truncated tables look complete P2 Reproduces A table of 4 zero bytes gives status complete with 0 entries. 7 zero bytes does the same. 4 non-zero bytes and 8 zero bytes are rejected. A valid entry followed by 4 zero bytes gives complete with 1 entry
3 Truncated PKCS#7 succeeds (outdated) P1 Not reproduced SEQUENCE { INTEGER 1, SET {} } is rejected ("missing encapContentInfo"). A SignerInfo with no signature field is rejected ("missing its signature OCTET STRING"). A full SignerInfo is accepted

Thread 3 has two shapes the code still accepts: an empty OCTET STRING signature (04 00) and an empty signerInfos SET {}. Neither is the exact case in the thread. The tool says it does not verify signatures. I did not count them as a hit.

Heap numbers for thread 1

Heap still held after the parse and a forced garbage collection, one subject name, Node 24.18.0.

Name bytes BMPString UniversalString UTF8String (control)
1 MiB 16 MiB 8 MiB 0 MiB
2 MiB 32 MiB 16 MiB 0 MiB
4 MiB 64 MiB 32 MiB 0 MiB
8 MiB 128 MiB (223 ms) 64 MiB (111 ms) 0 MiB (10 ms)
32 MiB 512 MiB (942 ms) not run not run
56 MiB 896 MiB (1,583 ms) not run not run

The thread says an 8 MiB BMPString raised heap by about 128 MiB. I measured the same. The 56 MiB file is under the 64 MiB default file limit. 8 MiB BMP on Node 22 and 26 also gave 128 MiB. Each number is one run, on a box that may have been shared. I tested one name in one certificate. Several large names would add up. I did not test that.

Tests

Node Result
22.23.1 8 files, 36 tests passed
24.18.0 8 files, 36 tests passed
26.11.1 8 files, 36 tests passed

At 7eef463 the same files had 35 tests. The new one covers the oversized OID. Nothing covers the other two open threads. The OID, PKCS#7 and table probes gave identical answers on all three Node versions.

Not run

  • A live server stall with a large name. I measured heap and time in-process.
  • Several large names in one file.
  • Real signed Windows apps. Every input came from a script.
  • Whether trailing zero bytes can be real padding in a signed file. I did not read the spec.
  • Main. I did not rerun these on main.
  • The 32 MiB and 56 MiB runs on Node 22 and 26.
  • The full suite, typecheck and lint. Only named test files ran.

Thanks, mvanhorn, for the quick OID fix.

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.11
  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads
  • RAM: 32 GB DDR4-3600
  • Node: 22.23.1, 24.18.0, 26.11.1
  • Head / base: e1e756c / 9258df0
  • Other: the box may have been shared with other test jobs, so treat the timings as rough.
Commands
npm ci --ignore-scripts; npm run build:cached; npm run test:prepare    (tree at e1e756c75, Node 24)
npm run test:focused -- <the 8 test files below>                        (Node 22, 24, 26)
node <probe>.mjs   (imports dist/native/pe/PeCertificateDer.js and PeCertificateParser.js from the built tree)

The 8 files: src/application/binaryDiagnostics/PeCertificatesService.test.ts, src/contracts/toolEffects.test.ts, src/domain/native/peCertificates.test.ts, src/native/pe/PeCertificateParser.test.ts, tests/acceptance/peCertificatesCli.test.ts, tests/acceptance/tools/localToolContracts.test.ts, tests/boundary/filesystem/peCertificates.test.ts, tests/boundary/mcp/peCertificatesMcp.test.ts. None mentions doctor; I checked with grep first.

Probe shapes, built by script inside a minimal PE32 image with one type-2 entry:

OID: ContentInfo { OBJECT IDENTIFIER of N bytes (0xff ... 0x7f), [0] SEQUENCE { INTEGER 1 } }
names: a signedData with one certificate whose subject CN is a BMPString, UniversalString or UTF8String of N MiB
tables: the security table is 4 zero bytes, or a valid entry plus 4 zero bytes, and so on

🤖 Written and tested with Claude Code (Sonnet 5.5, verified by Opus 5.5), posted with my OK 🦊

@cursor

cursor Bot commented Oct 21, 2026

Copy link
Copy Markdown

Restack is c7183b654 on morluto/rea:fix/pe-certificates (main d7ca93cc2). Force-with-lease onto mvanhorn:fix/pe-certificates was rejected because the restack updates .github/workflows/ci.yml and this GitHub App cannot change workflow files on the fork. The fork ref is still e1e756c75c.

Conflict files: README.md, docs/index.md, src/application/CapabilityInventory.ts, src/contracts/toolEffects.test.ts, src/server/createServer.ts, docs/readme/README_ko.md, docs/readme/README_ru.md, docs/readme/README_vi.md, docs/readme/README_zh-TW.md, docs/readme/README_zh.md.

On c7183b654: empty PKCS#7 signatures, an empty signerInfos set, and a truncated issuerAndSerialNumber fail; certificate names share a 64 KiB decoded-byte budget and are copied once; a short WIN_CERTIFICATE header fails instead of reporting the table complete.

npm run typecheck passed. Focused PE certificate tests: 8 files, 41 passed. Not merged.

mvanhorn and others added 6 commits October 21, 2026 23:37
Add inspect_pe_certificates for IMAGE_DIRECTORY_ENTRY_SECURITY
WIN_CERTIFICATE entries and structurally decoded PKCS#7 signer
and X.509 identity fields. The workflow reads a stable PE image
without executing it or verifying signatures, chains, timestamps,
or Authenticode digest coverage.
- Cap decoded DER values with a shared node budget so sibling fan-out
  cannot exhaust memory before the PKCS#7 check
- Require SignedData version, encapContentInfo and a final signerInfos
  SET in RFC 5652 order, and a signature OCTET STRING in each SignerInfo
- Decode OBJECT IDENTIFIER arcs as unbounded base-128 values, including
  a multi-byte first combined arc
- Decode UniversalString names as UTF-32BE and reject invalid values
- Share PE header admission with the resource layout so a section table
  that does not fit SizeOfHeaders is rejected
A crafted type-2 certificate with a 262,144-byte contentType OID stalled
decodeOid for about 20 seconds, because one arc kept growing a BigInt
before the signedData check ran. decodeOid now rejects an OID longer than
256 bytes and an arc longer than 32 bytes before decoding it. Certificate
OIDs, including 2.25 UUID arcs, fit under both caps.
SignedData now requires one SignerInfo with a non-empty signature and a
complete issuerAndSerialNumber. Certificate names share a 64 KiB decoded-byte
budget and are copied once. A security directory that ends in a short
WIN_CERTIFICATE header fails instead of reporting a complete table. The
certificate CLI forwards the caller abort signal into cleanup.

Co-authored-by: morluto <morluto@users.noreply.github.com>
The certificate restack follows current main. Workflow files stay at the
fork tip so this branch update does not rewrite .github/workflows.

Co-authored-by: morluto <morluto@users.noreply.github.com>
@cursor
cursor Bot force-pushed the fix/pe-certificates branch from e1e756c to c13f794 Compare October 21, 2026 23:38
@cursor

cursor Bot commented Oct 21, 2026

Copy link
Copy Markdown

PR head is now c13f79435 on mvanhorn:fix/pe-certificates. Restacked onto current main (a73eac190) with the three certificate review fixes. .github/workflows is unchanged from e1e756c75c, which is what let the force-with-lease push land. Not merged.

Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml
Certificate and signer serial numbers and validity times now count against
the shared decoded-text budget before they are converted, so a crafted
certificate cannot push megabytes of text into the hashed evidence. The
budget error now names certificate text rather than only names.

Restore the CI workflow to match main: the CI_TIMING_TIER input, the
tier-named timing artifacts and their publication conditions, the Windows
typecheck, and the path and snapshot suites in the curated Windows lane.
Keep byte entropy from main next to the PE certificate provider,
contract, CLI command and server wiring.
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Addressed the three Greptile findings in 1ebf689, which also merges main into the branch.

  • Certificate and signer serial numbers and validity times now count against the shared decoded-text budget before conversion and hashing, the same as names.
  • ci.yml matches main again: the CI_TIMING_TIER input, the tier-named timing artifacts and their publication conditions, the Windows typecheck, and the path and snapshot suites in the curated Windows lane.
  • The merge keeps byte entropy from main next to the PE certificate provider, contract, CLI command and server wiring.

format:check, lint, typecheck and build pass, along with the focused PE certificate, byte entropy, tool contract, catalog, ciTestReports, ciTestHistory and curatedWindowsLane tests.

Keep Authenticode certificate inspection, including the decoded-text
budget, beside ELF notes and the current CI workflow.

Co-authored-by: morluto <morluto@users.noreply.github.com>
Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread src/domain/native/peCertificates.ts Outdated
Comment thread src/native/pe/PeCertificateDer.ts Outdated
Comment thread src/native/pe/PeCertificateDer.ts Outdated
cursoragent and others added 2 commits October 22, 2026 08:08
Reject X.509 records that omit subjectPublicKeyInfo or signatureValue,
return subjectKeyIdentifier bytes as bounded hex, and advertise an absolute
path in the PE certificate input schema.

Co-authored-by: morluto <morluto@users.noreply.github.com>
ContentInfo and its signedData wrapper no longer ignore trailing values, and
directory strings must round-trip as UTF-8 instead of becoming U+FFFD.

Co-authored-by: morluto <morluto@users.noreply.github.com>
@morluto
morluto merged commit ca4ca65 into morluto:main Oct 22, 2026
43 of 44 checks passed
Comment on lines +227 to +229
default:
chargeDecodedName(budget, node.content.length);
return node.content.toString("latin1");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Older certificate names change

Names stored as TeletexString (0x14) fall through to Latin-1 here. For example, the valid T.61 bytes c2 65 become Âe instead of é. Inspection succeeds, but the returned issuer and subject names contain the wrong text. This is a non-blocking concern for older certificates. Decode this encoding correctly, or reject unsupported name encodings instead of silently treating them as Latin-1.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/native/pe/PeCertificateDer.ts
Line: 227-229

Comment:
**Older certificate names change**

Names stored as `TeletexString` (`0x14`) fall through to Latin-1 here. For example, the valid T.61 bytes `c2 65` become `Âe` instead of `é`. Inspection succeeds, but the returned issuer and subject names contain the wrong text. This is a non-blocking concern for older certificates. Decode this encoding correctly, or reject unsupported name encodings instead of silently treating them as Latin-1.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@morluto morluto mentioned this pull request Oct 25, 2026
3 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants