Skip to content

feat(lark): add durable single-group knowledge intake - #13

Closed
harley wants to merge 1 commit into
mainfrom
feat/tarley-event-intake
Closed

harley wants to merge 1 commit into
mainfrom
feat/tarley-event-intake

Conversation

@harley

@harley harley commented Oct 8, 2026 •

Copy link
Copy Markdown

Superseded by #16, which retains only the optional generic ingress relay. Installation-specific workflow code has moved to the separate private operations repository. This embedded implementation will not be merged; its branch and source remain preserved.

What does this PR do?

Adds a disabled-by-default, single-group Lark knowledge pilot for the CTO search. Ordinary group messages currently stop at the private agent's mention gate, leaving file intake dependent on a laptop monitor. This extends the existing authenticated persistent connection with durable capture before that gate, a resumable file pipeline and a separate source-only group Q&A route.

The first release handles human messages/thread replies and uploaded PDF/DOCX files. It preserves private candidate assessments and human hiring decisions. Linked-document/wiki discovery remains outside this release.

Related Issue

Implements the reviewed Tarley event-driven intake plan. Operator boundaries and activation steps are recorded in docs/runbooks/lark-group-knowledge.md.

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Documentation update
  • Tests (adding or improving test coverage)
  • CI / infrastructure

Changes Made

  • Atomically store source events and durable jobs; deduplicate receive replays and file digests, preserve originals, and resume after restart.
  • Bound PDF/DOCX parsing and scanned-PDF OCR. Cache claim-evidence assessments against the published CTO JD; quarantine identity conflicts and preserve existing hiring stages, human notes and attachments.
  • Answer group questions only from freshly checked group sources, with current membership checks and exact quoted citations. The group route never invokes the private agent or reads private assessments.
  • Use native-thread replies and durable UUIDs; quarantine uncertain sends beyond the provider's deduplication window. Reconcile paginated history, old threads, edits and recalls every 15 minutes.
  • Add teardown cleanup, concurrent-index retry hooks, model-data disclosures and a capture/process cutover runbook.

Intent and Risks

capture overlaps the existing monitor without model calls, candidate writes or knowledge replies. process needs a coordinated single-writer cutover. The policy remains blank by default. No deployment, production subscriptions/scopes, heartbeat settings or real group/candidate messages were changed.

Activation still requires the dedicated app's approved all-group-message permission, verified history/resource/member access, an authorized model/cost boundary and a genuine upload/Q&A/outage-recovery check. Local fixtures are not live activation proof. The daily model limit counts invocations, not dollars or SDK retry requests. Reconciliation is bounded; incomplete passes retain the watermark and expose failure. Operational rollback returns to capture mode and retains evidence; destructive down migrations are not the rollback procedure.

How to Test

Validated in the managed isolated PostgreSQL environment with synthetic inputs:

  • Full regular Go suite: make env-exec ARGS='-- env GOFLAGS=-p=2 GOMAXPROCS=2 bash scripts/test-go.sh --only regular'.
  • Guarded agent suite: make env-exec ARGS='-- bash scripts/test-go.sh --only agent' (no real-agent smoke).
  • Race detector: Lark integration and channel engine packages, including database-backed recovery and identity-race regressions.
  • Migration/concurrent-index cleanup and workspace-deletion checks.
  • Installed Poppler/Tesseract smoke with synthetic text and scanned PDFs.
  • Regenerated sqlc output and git diff --check.

The initial broad parallel run exposed daemon timeout/process-tree cleanup failures; a bounded-concurrency regular run passed. A later full run passed all packages except two media-reconciler tests while the local API shared the fixture database; after stopping the API, the service package passed in isolation. The runbook now requires stopping background workers before database-backed suites. No frontend/mobile changes, live model calls, real candidate files or production messaging tests.

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • I have updated relevant documentation to reflect my changes
  • If this PR touches Chinese product copy, I checked it against apps/docs/content/docs/developers/conventions.zh.mdx
  • I have considered and documented any risks above
  • I will address all reviewer comments before requesting merge

No UI changes; screenshots are not applicable. No new runtime, coding tool or UI tab.

AI Disclosure

AI tool used: Codex, with sequential CE specialist and local adversarial reviews. The optional Claude pass could not start because its acpx dependency was unavailable; no code was sent to that provider.

Prompt / approach: Implement the reviewed, narrowly scoped intake plan in an isolated worktree; verify durable recovery, native-thread routing, source-only group authority and private candidate preservation with synthetic inputs. Prepare a ready-for-review PR only; do not merge or deploy.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security Review Summary

This PR introduces a significant new feature for Lark knowledge intake with PDF/DOCX file processing. I've identified 4 critical security vulnerabilities that must be addressed before merge:

Critical Issues Found

  1. Path Traversal Vulnerability (CWE-22): Untrusted file paths used in temporary directory operations without validation
  2. Command Injection Risk (CWE-78): External command execution (pdfinfo, pdftotext, pdftoppm, tesseract) with insufficient input sanitization
  3. Unrestricted File Upload (CWE-434): No malware scanning on user-uploaded files stored directly in database
  4. Crash Risk (CWE-755): XML decoder processing untrusted DOCX content without panic recovery

Security Architecture Concerns

The feature processes untrusted user files through external binaries, which creates multiple attack surfaces. While the code includes some protections (size limits, timeouts, expansion limits), the fundamental security controls are insufficient for production deployment:

  • No malware scanning before storage
  • External command execution without resource isolation
  • Path operations without traversal prevention
  • Missing panic recovery for untrusted document parsing

Recommendation

Block merge until the identified security vulnerabilities are remediated. Consider implementing: sandboxed file processing, malware scanning integration, strict path validation, and comprehensive error recovery for all untrusted input processing.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.


⚠️ This PR contains more than 30 files. Amazon Q is better at reviewing smaller PRs, and may miss issues in larger changesets.

Comment on lines +171 to +172
input := filepath.Join(dir, "source.pdf")
if err = os.WriteFile(input, data, 0600); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 Security Vulnerability: Path traversal risk when writing untrusted PDF data to temporary directory. An attacker could craft a malicious filename in the Lark message to escape the temp directory and write files to arbitrary locations on the filesystem.1

The input path constructed at line 171-172 uses unsanitized data that originates from user-controlled Lark messages. While os.MkdirTemp creates a random directory, the fixed filename "source.pdf" provides a predictable target. More critically, the subsequent command execution at lines 175, 187, and 206-209 pass this path to external binaries (pdfinfo, pdftotext, pdftoppm, tesseract) without validation.

Add explicit path validation to ensure the constructed paths remain within the temporary directory before executing any external commands.

Footnotes

  1. CWE-22: Path Traversal - https://cwe.mitre.org/data/definitions/22.html ↩

Comment on lines +140 to +141
func knowledgeCommand(ctx context.Context, name string, args ...string) ([]byte, error) {
cmd := exec.CommandContext(ctx, name, args...)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 Security Vulnerability: Command injection risk through unsanitized external command execution. The knowledgeCommand function executes external binaries (pdfinfo, pdftotext, pdftoppm, tesseract) with paths derived from untrusted user input without proper validation.1

While Go's exec.CommandContext prevents shell injection when args are passed as separate parameters, the file paths themselves originate from user-controlled Lark message data. An attacker could exploit this through: (1) Path traversal in the temp directory reference, (2) Symbolic link attacks if the temp directory is predictable, or (3) Resource exhaustion by uploading specially crafted PDFs that cause these external tools to hang or consume excessive resources.

The 60-second timeout provides some protection against hangs, but does not prevent resource exhaustion within that window. Add validation to ensure all paths stay within the expected temporary directory, and consider adding resource limits (memory, CPU) to the external process execution.

Footnotes

  1. CWE-78: OS Command Injection - https://cwe.mitre.org/data/definitions/78.html ↩

}
digest := sha256.Sum256(data)
j.State.Digest = hex.EncodeToString(digest[:])
err = g.pool.QueryRow(ctx, `INSERT INTO lark_knowledge_file(installation_id,chat_id,digest,filename,original) VALUES($1,$2,$3,$4,$5) ON CONFLICT(installation_id,chat_id,digest) DO UPDATE SET digest=EXCLUDED.digest RETURNING id`, g.policy.InstallationID, g.policy.ChatID, j.State.Digest, knowledgeFilename(file.Name), data).Scan(&j.State.FileID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 Security Vulnerability: Untrusted file data stored directly in database without malware scanning. Files up to 20 MiB from Lark users are stored in the lark_knowledge_file.original column without any malware or content validation beyond format checks.1

An attacker could upload malicious payloads disguised as PDF/DOCX files. While the extraction logic has some ZIP bomb protection for DOCX files, it does not scan for: (1) Embedded malware in PDF JavaScript/attachments, (2) Exploit payloads targeting PDF reader vulnerabilities, or (3) Malicious macros or embedded objects in Office documents.

Consider integrating malware scanning before storing files, or at minimum add content security policy checks and sandboxing for file processing operations.

Footnotes

  1. CWE-434: Unrestricted Upload of File with Dangerous Type - https://cwe.mitre.org/data/definitions/434.html ↩

Comment on lines +77 to +92
decoder := xml.NewDecoder(bytes.NewReader(raw))
var out strings.Builder
paragraph := 0
inText := false
readable := 0
const wordNS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
for {
if err := ctx.Err(); err != nil {
return "", err
}
tok, err := decoder.Token()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return "", permanentKnowledgeError("invalid DOCX XML; original retained")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 Crash Risk: Missing error handling for XML decoder allows malformed DOCX files to panic the server. The xml.NewDecoder at line 77 processes untrusted ZIP archive content without comprehensive error recovery.1

While the code checks for io.EOF and basic XML errors, deeply nested or malformed XML structures could trigger decoder panics that aren't caught. The Go XML decoder has known issues with certain malformed inputs that can cause runtime panics rather than returning errors.

Wrap the entire XML parsing section (lines 83-122) in a recover block to catch and convert any decoder panics into permanentKnowledgeError results.

Footnotes

  1. CWE-755: Improper Handling of Exceptional Conditions - https://cwe.mitre.org/data/definitions/755.html ↩

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.

1 participant