@cliwant/mcp-sam-gov 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/LICENSE +21 -21
  2. package/README.ja.md +48 -16
  3. package/README.ko.md +48 -16
  4. package/README.md +226 -64
  5. package/dist/attachments.d.ts +63 -0
  6. package/dist/attachments.d.ts.map +1 -0
  7. package/dist/attachments.js +576 -0
  8. package/dist/attachments.js.map +1 -0
  9. package/dist/bls.d.ts +267 -0
  10. package/dist/bls.d.ts.map +1 -0
  11. package/dist/bls.js +1549 -0
  12. package/dist/bls.js.map +1 -0
  13. package/dist/cache.d.ts +2 -0
  14. package/dist/cache.d.ts.map +1 -1
  15. package/dist/cache.js +4 -0
  16. package/dist/cache.js.map +1 -1
  17. package/dist/census.d.ts +182 -0
  18. package/dist/census.d.ts.map +1 -0
  19. package/dist/census.js +576 -0
  20. package/dist/census.js.map +1 -0
  21. package/dist/ckan.d.ts +141 -0
  22. package/dist/ckan.d.ts.map +1 -0
  23. package/dist/ckan.js +393 -0
  24. package/dist/ckan.js.map +1 -0
  25. package/dist/clinicaltrials.d.ts +180 -0
  26. package/dist/clinicaltrials.d.ts.map +1 -0
  27. package/dist/clinicaltrials.js +730 -0
  28. package/dist/clinicaltrials.js.map +1 -0
  29. package/dist/cms.d.ts +140 -0
  30. package/dist/cms.d.ts.map +1 -0
  31. package/dist/cms.js +482 -0
  32. package/dist/cms.js.map +1 -0
  33. package/dist/coerce.d.ts +32 -0
  34. package/dist/coerce.d.ts.map +1 -0
  35. package/dist/coerce.js +50 -0
  36. package/dist/coerce.js.map +1 -0
  37. package/dist/datagov-catalog.d.ts +84 -0
  38. package/dist/datagov-catalog.d.ts.map +1 -0
  39. package/dist/datagov-catalog.js +233 -0
  40. package/dist/datagov-catalog.js.map +1 -0
  41. package/dist/datagov.d.ts +146 -0
  42. package/dist/datagov.d.ts.map +1 -0
  43. package/dist/datagov.js +689 -0
  44. package/dist/datagov.js.map +1 -0
  45. package/dist/datagovKey.d.ts +36 -0
  46. package/dist/datagovKey.d.ts.map +1 -0
  47. package/dist/datagovKey.js +60 -0
  48. package/dist/datagovKey.js.map +1 -0
  49. package/dist/datasource.d.ts +313 -0
  50. package/dist/datasource.d.ts.map +1 -0
  51. package/dist/datasource.js +551 -0
  52. package/dist/datasource.js.map +1 -0
  53. package/dist/disclosure.d.ts +55 -0
  54. package/dist/disclosure.d.ts.map +1 -0
  55. package/dist/disclosure.js +57 -0
  56. package/dist/disclosure.js.map +1 -0
  57. package/dist/ecfr.d.ts +4 -2
  58. package/dist/ecfr.d.ts.map +1 -1
  59. package/dist/ecfr.js +74 -1
  60. package/dist/ecfr.js.map +1 -1
  61. package/dist/echo.d.ts +143 -0
  62. package/dist/echo.d.ts.map +1 -0
  63. package/dist/echo.js +424 -0
  64. package/dist/echo.js.map +1 -0
  65. package/dist/edgar.d.ts +377 -0
  66. package/dist/edgar.d.ts.map +1 -0
  67. package/dist/edgar.js +2418 -0
  68. package/dist/edgar.js.map +1 -0
  69. package/dist/errors.d.ts +26 -0
  70. package/dist/errors.d.ts.map +1 -1
  71. package/dist/errors.js +69 -1
  72. package/dist/errors.js.map +1 -1
  73. package/dist/fac.d.ts +180 -0
  74. package/dist/fac.d.ts.map +1 -0
  75. package/dist/fac.js +416 -0
  76. package/dist/fac.js.map +1 -0
  77. package/dist/far.d.ts +170 -0
  78. package/dist/far.d.ts.map +1 -0
  79. package/dist/far.js +804 -0
  80. package/dist/far.js.map +1 -0
  81. package/dist/fdic.d.ts +599 -0
  82. package/dist/fdic.d.ts.map +1 -0
  83. package/dist/fdic.js +1624 -0
  84. package/dist/fdic.js.map +1 -0
  85. package/dist/federal-register.d.ts +139 -2
  86. package/dist/federal-register.d.ts.map +1 -1
  87. package/dist/federal-register.js +419 -3
  88. package/dist/federal-register.js.map +1 -1
  89. package/dist/fema.d.ts +181 -0
  90. package/dist/fema.d.ts.map +1 -0
  91. package/dist/fema.js +436 -0
  92. package/dist/fema.js.map +1 -0
  93. package/dist/fpds.d.ts +108 -0
  94. package/dist/fpds.d.ts.map +1 -0
  95. package/dist/fpds.js +519 -0
  96. package/dist/fpds.js.map +1 -0
  97. package/dist/gao.d.ts +64 -0
  98. package/dist/gao.d.ts.map +1 -0
  99. package/dist/gao.js +640 -0
  100. package/dist/gao.js.map +1 -0
  101. package/dist/govinfo.d.ts +111 -0
  102. package/dist/govinfo.d.ts.map +1 -0
  103. package/dist/govinfo.js +422 -0
  104. package/dist/govinfo.js.map +1 -0
  105. package/dist/grants.d.ts +27 -4
  106. package/dist/grants.d.ts.map +1 -1
  107. package/dist/grants.js +111 -6
  108. package/dist/grants.js.map +1 -1
  109. package/dist/gsa-csv.d.ts +249 -0
  110. package/dist/gsa-csv.d.ts.map +1 -0
  111. package/dist/gsa-csv.js +784 -0
  112. package/dist/gsa-csv.js.map +1 -0
  113. package/dist/integrity.d.ts +212 -0
  114. package/dist/integrity.d.ts.map +1 -0
  115. package/dist/integrity.js +707 -0
  116. package/dist/integrity.js.map +1 -0
  117. package/dist/meta.d.ts +165 -0
  118. package/dist/meta.d.ts.map +1 -0
  119. package/dist/meta.js +162 -0
  120. package/dist/meta.js.map +1 -0
  121. package/dist/nih.d.ts +117 -0
  122. package/dist/nih.d.ts.map +1 -0
  123. package/dist/nih.js +291 -0
  124. package/dist/nih.js.map +1 -0
  125. package/dist/nppes.d.ts +157 -0
  126. package/dist/nppes.d.ts.map +1 -0
  127. package/dist/nppes.js +648 -0
  128. package/dist/nppes.js.map +1 -0
  129. package/dist/nsf.d.ts +176 -0
  130. package/dist/nsf.d.ts.map +1 -0
  131. package/dist/nsf.js +554 -0
  132. package/dist/nsf.js.map +1 -0
  133. package/dist/nvd.d.ts +176 -0
  134. package/dist/nvd.d.ts.map +1 -0
  135. package/dist/nvd.js +912 -0
  136. package/dist/nvd.js.map +1 -0
  137. package/dist/ofac.d.ts +205 -0
  138. package/dist/ofac.d.ts.map +1 -0
  139. package/dist/ofac.js +919 -0
  140. package/dist/ofac.js.map +1 -0
  141. package/dist/pricing.d.ts +110 -0
  142. package/dist/pricing.d.ts.map +1 -0
  143. package/dist/pricing.js +843 -0
  144. package/dist/pricing.js.map +1 -0
  145. package/dist/sam-gov/client.d.ts +60 -2
  146. package/dist/sam-gov/client.d.ts.map +1 -1
  147. package/dist/sam-gov/client.js +320 -54
  148. package/dist/sam-gov/client.js.map +1 -1
  149. package/dist/sam-gov/index.d.ts +1 -1
  150. package/dist/sam-gov/index.d.ts.map +1 -1
  151. package/dist/sam-gov/index.js +1 -1
  152. package/dist/sam-gov/index.js.map +1 -1
  153. package/dist/sam-gov/types.d.ts +24 -0
  154. package/dist/sam-gov/types.d.ts.map +1 -1
  155. package/dist/sba.d.ts +72 -0
  156. package/dist/sba.d.ts.map +1 -0
  157. package/dist/sba.js +281 -0
  158. package/dist/sba.js.map +1 -0
  159. package/dist/server.d.ts +14 -2
  160. package/dist/server.d.ts.map +1 -1
  161. package/dist/server.js +3889 -296
  162. package/dist/server.js.map +1 -1
  163. package/dist/snapshot.d.ts +98 -0
  164. package/dist/snapshot.d.ts.map +1 -0
  165. package/dist/snapshot.js +146 -0
  166. package/dist/snapshot.js.map +1 -0
  167. package/dist/socrata.d.ts +157 -0
  168. package/dist/socrata.d.ts.map +1 -0
  169. package/dist/socrata.js +448 -0
  170. package/dist/socrata.js.map +1 -0
  171. package/dist/treasury.d.ts +143 -0
  172. package/dist/treasury.d.ts.map +1 -0
  173. package/dist/treasury.js +436 -0
  174. package/dist/treasury.js.map +1 -0
  175. package/dist/usaspending.d.ts +260 -65
  176. package/dist/usaspending.d.ts.map +1 -1
  177. package/dist/usaspending.js +1629 -201
  178. package/dist/usaspending.js.map +1 -1
  179. package/dist/usitc.d.ts +142 -0
  180. package/dist/usitc.d.ts.map +1 -0
  181. package/dist/usitc.js +339 -0
  182. package/dist/usitc.js.map +1 -0
  183. package/package.json +24 -2
  184. package/src/attachments.ts +652 -0
  185. package/src/bls.ts +1943 -0
  186. package/src/cache.ts +5 -0
  187. package/src/census.ts +735 -0
  188. package/src/ckan.ts +495 -0
  189. package/src/clinicaltrials.ts +923 -0
  190. package/src/cms.ts +634 -0
  191. package/src/coerce.ts +47 -0
  192. package/src/datagov-catalog.ts +296 -0
  193. package/src/datagov.ts +907 -0
  194. package/src/datagovKey.ts +68 -0
  195. package/src/datasource.ts +721 -0
  196. package/src/disclosure.ts +61 -0
  197. package/src/ecfr.ts +98 -1
  198. package/src/echo.ts +496 -0
  199. package/src/edgar.ts +3014 -0
  200. package/src/errors.ts +84 -1
  201. package/src/fac.ts +529 -0
  202. package/src/far.ts +1007 -0
  203. package/src/fdic.ts +2052 -0
  204. package/src/federal-register.ts +512 -3
  205. package/src/fema.ts +541 -0
  206. package/src/fpds.ts +620 -0
  207. package/src/gao.ts +744 -0
  208. package/src/govinfo.ts +497 -0
  209. package/src/grants.ts +139 -7
  210. package/src/gsa-csv.ts +992 -0
  211. package/src/integrity.ts +928 -0
  212. package/src/meta.ts +292 -0
  213. package/src/nih.ts +375 -0
  214. package/src/nppes.ts +834 -0
  215. package/src/nsf.ts +706 -0
  216. package/src/nvd.ts +1124 -0
  217. package/src/ofac.ts +1166 -0
  218. package/src/pricing.ts +1075 -0
  219. package/src/sam-gov/client.ts +345 -63
  220. package/src/sam-gov/index.ts +5 -1
  221. package/src/sam-gov/types.ts +22 -0
  222. package/src/sba.ts +357 -0
  223. package/src/server.ts +4551 -328
  224. package/src/snapshot.ts +192 -0
  225. package/src/socrata.ts +532 -0
  226. package/src/treasury.ts +575 -0
  227. package/src/usaspending.ts +1946 -214
  228. package/src/usitc.ts +420 -0
@@ -0,0 +1,576 @@
1
+ /**
2
+ * @cliwant/mcp-sam-gov/attachments — read the ACTUAL solicitation document.
3
+ *
4
+ * Why this exists
5
+ * ----------------
6
+ * An AI doing procurement research needs the real RFP/SOW/Q&A/wage-table TEXT,
7
+ * not just the synopsis (`sam_fetch_description`) or metadata. A SAM notice
8
+ * carries ATTACHMENTS (`resourceLinks` → keyless download URLs) that hold the
9
+ * true requirements. The rest of the server exposes those URLs but cannot read
10
+ * them; this module fetches one keyless and returns its extracted text so the
11
+ * agent can analyze the buy.
12
+ *
13
+ * SAM attachments are predominantly PDF (compressed streams / CID fonts — not
14
+ * hand-rollable), so PDF text extraction uses `unpdf` (a single self-contained
15
+ * package that bundles pdfjs; no transitive deps). text/HTML also occur and are
16
+ * decoded/stripped locally. DOCX is a ZIP whose `word/document.xml` holds the
17
+ * body text (deflate-compressed); we parse the ZIP container by hand and inflate
18
+ * that entry with Node's built-in `node:zlib` — NO new dependency. Other
19
+ * binaries are NOT half-parsed — they return `text:null` with an honest note.
20
+ *
21
+ * Truthfulness invariants (a reviewer WILL attack these):
22
+ * - A DOWN fetch (5xx / network / timeout) THROWS `upstream_unavailable`
23
+ * (retryable). A down service is NOT an empty attachment — never `text:""`.
24
+ * - A 404 THROWS `not_found` (the attachment id is gone).
25
+ * - A PDF that fails to parse (encrypted/corrupt) → `text:null` +
26
+ * `extractionError` note — NEVER a crash, NEVER a fake "".
27
+ * - A DOCX that fails to parse (corrupt/not-a-zip/no `word/document.xml`) →
28
+ * `text:null` + a disclosed note — NEVER a crash, NEVER a fake "".
29
+ * - A non-extractable format (binary) → `text:null` + honest note.
30
+ * - SSRF: only sam.gov / api.sam.gov hosts are fetched; anything else →
31
+ * `invalid_input` with NO network call.
32
+ * - `truncated`/`pages` are honest.
33
+ */
34
+ import zlib from "node:zlib";
35
+ import { extractText, getDocumentProxy } from "unpdf";
36
+ import { ToolErrorCarrier } from "./errors.js";
37
+ import { withMeta } from "./meta.js";
38
+ /** Default cap on returned text (chars). Large enough for a full RFP body. */
39
+ const DEFAULT_MAX_CHARS = 200_000;
40
+ /** Hard guard on the cap (mirrors the Zod max) — belt-and-suspenders. */
41
+ const MAX_MAX_CHARS = 500_000;
42
+ /**
43
+ * Refuse to buffer/parse an attachment larger than this (SAM/S3 send
44
+ * content-length; SAM files are well under this). Bounds memory before the whole
45
+ * body is read into an ArrayBuffer + parsed by unpdf.
46
+ */
47
+ const MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
48
+ /**
49
+ * Cap on the DECOMPRESSED size of a DOCX's `word/document.xml`. The fetch size
50
+ * guard bounds the compressed input, but deflate can expand ~1000× — a crafted
51
+ * "zip bomb" DOCX (SAM accepts vendor uploads) could inflate to GBs and OOM. A
52
+ * real Word body is far under this; `inflateRawSync` throws past it → caught →
53
+ * text:null + a disclosed note.
54
+ */
55
+ const MAX_DOCX_XML_BYTES = 100 * 1024 * 1024;
56
+ /** Browser-ish UA — SAM serves the keyless file endpoint to a normal client. */
57
+ const BROWSER_UA = "Mozilla/5.0 (compatible; @cliwant/mcp-sam-gov; +https://github.com/cliwant/mcp-sam-gov)";
58
+ /** Fetch timeout for a (potentially large) attachment download. */
59
+ const FETCH_TIMEOUT_MS = 30_000;
60
+ /**
61
+ * SSRF allow-list: an `https://` URL whose host is exactly `sam.gov` or
62
+ * `api.sam.gov` (or a subdomain of `sam.gov`). We intentionally do NOT fetch
63
+ * arbitrary hosts — the input is a SAM attachment download URL, nothing else.
64
+ */
65
+ function assertSamAttachmentUrl(url) {
66
+ let parsed;
67
+ try {
68
+ parsed = new URL(url);
69
+ }
70
+ catch {
71
+ throw new ToolErrorCarrier({
72
+ kind: "invalid_input",
73
+ message: `Not a valid URL: ${JSON.stringify(url)}. Expected a SAM attachment download URL (https://sam.gov/... or https://api.sam.gov/...).`,
74
+ retryable: false,
75
+ });
76
+ }
77
+ if (parsed.protocol !== "https:") {
78
+ throw new ToolErrorCarrier({
79
+ kind: "invalid_input",
80
+ message: `Only https:// SAM attachment URLs are fetched (got ${parsed.protocol}//). SSRF hygiene: this tool fetches sam.gov / api.sam.gov only.`,
81
+ retryable: false,
82
+ });
83
+ }
84
+ const host = parsed.hostname.toLowerCase();
85
+ const isSam = host === "sam.gov" ||
86
+ host === "api.sam.gov" ||
87
+ host.endsWith(".sam.gov");
88
+ if (!isSam) {
89
+ throw new ToolErrorCarrier({
90
+ kind: "invalid_input",
91
+ message: `Refusing to fetch non-SAM host ${JSON.stringify(host)}. This tool only reads sam.gov / api.sam.gov attachment URLs (SSRF hygiene + scope). Pass a URL from sam_get_opportunity's attachments[].url.`,
92
+ retryable: false,
93
+ });
94
+ }
95
+ return parsed;
96
+ }
97
+ /** Parse a filename out of a `content-disposition` header, if present. */
98
+ function filenameFromDisposition(disposition) {
99
+ if (!disposition)
100
+ return null;
101
+ // RFC 5987 `filename*=UTF-8''...` takes precedence when present.
102
+ const star = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(disposition);
103
+ if (star && star[1]) {
104
+ try {
105
+ return decodeURIComponent(star[1].trim().replace(/^"|"$/g, ""));
106
+ }
107
+ catch {
108
+ /* fall through to plain filename */
109
+ }
110
+ }
111
+ const plain = /filename="?([^";]+)"?/i.exec(disposition);
112
+ if (plain && plain[1])
113
+ return plain[1].trim();
114
+ return null;
115
+ }
116
+ /** Lowercased file extension (no dot) from a filename, or "". */
117
+ function extOf(filename) {
118
+ if (!filename)
119
+ return "";
120
+ const m = /\.([A-Za-z0-9]+)\s*$/.exec(filename);
121
+ return m && m[1] ? m[1].toLowerCase() : "";
122
+ }
123
+ /**
124
+ * Detect the format from magic bytes (authoritative) + filename ext +
125
+ * content-type (advisory). Magic bytes win because SAM serves everything as
126
+ * `application/octet-stream`.
127
+ */
128
+ function detectFormat(bytes, contentType, ext) {
129
+ const ct = contentType.toLowerCase();
130
+ // %PDF
131
+ if (bytes.length >= 4 &&
132
+ bytes[0] === 0x25 &&
133
+ bytes[1] === 0x50 &&
134
+ bytes[2] === 0x44 &&
135
+ bytes[3] === 0x46) {
136
+ return "pdf";
137
+ }
138
+ // PK\x03\x04 (ZIP container) → treat as docx ONLY when the ext/CT say so;
139
+ // other zip-based Office/archive formats fall through to "binary".
140
+ if (bytes.length >= 4 &&
141
+ bytes[0] === 0x50 &&
142
+ bytes[1] === 0x4b &&
143
+ bytes[2] === 0x03 &&
144
+ bytes[3] === 0x04) {
145
+ if (ext === "docx" || ct.includes("wordprocessingml"))
146
+ return "docx";
147
+ return "binary";
148
+ }
149
+ // HTML: content-type says so, OR the body starts (after BOM/whitespace) with
150
+ // '<' plus an html-ish token.
151
+ if (ct.includes("text/html") || ct.includes("application/xhtml")) {
152
+ return "html";
153
+ }
154
+ const head = sniffLeadingText(bytes, 512).trimStart().toLowerCase();
155
+ if (head.startsWith("<!doctype html") || head.startsWith("<html")) {
156
+ return "html";
157
+ }
158
+ // A body that opens with a tag and carries an html-ish element (but is NOT an
159
+ // XML declaration) reads as HTML.
160
+ if (!head.startsWith("<?xml") &&
161
+ head.startsWith("<") &&
162
+ /<(html|body|div|p|table|head)\b/.test(head)) {
163
+ return "html";
164
+ }
165
+ // Plain textual content-types.
166
+ if (ct.includes("text/plain") ||
167
+ ct.includes("text/csv") ||
168
+ ct.includes("application/csv") ||
169
+ ct.includes("text/xml") ||
170
+ ct.includes("application/xml") ||
171
+ ct.includes("application/json") ||
172
+ ct.includes("text/")) {
173
+ return "text";
174
+ }
175
+ // Extension-based textual fallback (SAM serves octet-stream).
176
+ if (["txt", "csv", "xml", "json", "tsv", "md", "log"].includes(ext)) {
177
+ return "text";
178
+ }
179
+ // Looks like text if it decodes cleanly with no NUL/control soup.
180
+ if (looksTextual(bytes))
181
+ return "text";
182
+ return "binary";
183
+ }
184
+ /** Decode the first `n` bytes as UTF-8 for a cheap content sniff. */
185
+ function sniffLeadingText(bytes, n) {
186
+ const slice = bytes.subarray(0, Math.min(n, bytes.length));
187
+ try {
188
+ return new TextDecoder("utf-8", { fatal: false }).decode(slice);
189
+ }
190
+ catch {
191
+ return "";
192
+ }
193
+ }
194
+ /**
195
+ * Heuristic: does the byte buffer look like human-readable text? True when
196
+ * there are no NUL bytes and the share of control chars (outside tab/CR/LF) is
197
+ * tiny. Used only as a last-resort fallback when magic bytes + CT + ext are
198
+ * all inconclusive.
199
+ */
200
+ function looksTextual(bytes) {
201
+ const sample = bytes.subarray(0, Math.min(1024, bytes.length));
202
+ if (sample.length === 0)
203
+ return false;
204
+ let control = 0;
205
+ for (const b of sample) {
206
+ if (b === 0)
207
+ return false; // NUL ⇒ binary
208
+ if (b < 0x09 || (b > 0x0d && b < 0x20))
209
+ control++;
210
+ }
211
+ return control / sample.length < 0.05;
212
+ }
213
+ /** Decode common HTML/XML entities that survive tag-stripping. */
214
+ function decodeEntities(s) {
215
+ return s
216
+ .replace(/&nbsp;/g, " ")
217
+ .replace(/&lt;/g, "<")
218
+ .replace(/&gt;/g, ">")
219
+ .replace(/&quot;/g, '"')
220
+ .replace(/&#0?39;/g, "'")
221
+ .replace(/&apos;/g, "'")
222
+ .replace(/&#x27;/g, "'")
223
+ // &amp; last so we don't double-decode an already-decoded entity.
224
+ .replace(/&amp;/g, "&");
225
+ }
226
+ /**
227
+ * Strip HTML to readable text: drop <script>/<style> CONTENT entirely, remove
228
+ * all remaining tags, decode entities, collapse whitespace. Mirrors the
229
+ * approach used elsewhere in the server (sam-gov description, gao, ecfr).
230
+ */
231
+ function stripHtml(html) {
232
+ return decodeEntities(html
233
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
234
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
235
+ .replace(/<[^>]+>/g, " "))
236
+ .replace(/\s+/g, " ")
237
+ .trim();
238
+ }
239
+ /**
240
+ * Extract the body text of a DOCX (Office Open XML) file, dependency-free.
241
+ *
242
+ * A DOCX is a ZIP archive; the document body lives in `word/document.xml`
243
+ * (normally deflate-compressed, ZIP method 8). We parse the ZIP container by
244
+ * hand — locate the End-Of-Central-Directory record, walk the central
245
+ * directory to find `word/document.xml`, read its local file header to compute
246
+ * the compressed-data offset, slice those bytes, and inflate them with Node's
247
+ * built-in `node:zlib` (`inflateRawSync`; method 0 = stored → bytes as-is). The
248
+ * resulting XML is reduced to plain text: `</w:p>`/`</w:tr>` become newlines,
249
+ * `<w:tab/>` a tab, ALL remaining tags are stripped whole (so open-tag
250
+ * attributes like `w14:paraId="…"` never leak), entities are decoded, and
251
+ * runs of intra-line whitespace are collapsed.
252
+ *
253
+ * DEFENSIVE: every offset/length read is bounds-checked. Any structural problem
254
+ * (buffer too small, EOCD/CD/LFH signature bad, an offset or length past the
255
+ * end of the buffer, `word/document.xml` absent, an unsupported compression
256
+ * method, or an inflate throw) raises a normal `Error`. It never reads out of
257
+ * bounds, never loops unbounded, and never returns garbage — the CALLER catches
258
+ * the throw and reports `text:null` + a disclosed note.
259
+ *
260
+ * @throws {Error} on any malformed/unsupported input.
261
+ */
262
+ function extractDocxText(bytes) {
263
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
264
+ const u32 = (o) => dv.getUint32(o, true); // little-endian
265
+ const u16 = (o) => dv.getUint16(o, true);
266
+ const utf8 = (start, end) => new TextDecoder("utf-8", { fatal: false }).decode(bytes.subarray(start, end));
267
+ const EOCD_SIG = 0x06054b50; // PK\x05\x06 End-Of-Central-Directory
268
+ const CD_SIG = 0x02014b50; // PK\x01\x02 central-directory file header
269
+ const LFH_SIG = 0x04034b50; // PK\x03\x04 local file header
270
+ const n = bytes.length;
271
+ // EOCD is 22 bytes minimum (before any trailing comment).
272
+ if (n < 22)
273
+ throw new Error("not a valid DOCX (file too small to be a ZIP)");
274
+ // 1. Locate the End-Of-Central-Directory by scanning backward. It sits within
275
+ // the last 22 bytes + an optional comment (<= 0xffff), so bound the scan.
276
+ let eocd = -1;
277
+ const minStart = Math.max(0, n - 22 - 0xffff);
278
+ for (let i = n - 22; i >= minStart; i--) {
279
+ if (u32(i) === EOCD_SIG) {
280
+ eocd = i;
281
+ break;
282
+ }
283
+ }
284
+ if (eocd < 0) {
285
+ throw new Error("not a valid DOCX (ZIP End-Of-Central-Directory not found)");
286
+ }
287
+ const entryCount = u16(eocd + 10);
288
+ const cdOffset = u32(eocd + 16);
289
+ if (cdOffset >= n) {
290
+ throw new Error("corrupt DOCX (ZIP central-directory offset past end of file)");
291
+ }
292
+ // 2. Walk the central directory to find `word/document.xml`.
293
+ let p = cdOffset;
294
+ let target = null;
295
+ for (let e = 0; e < entryCount; e++) {
296
+ if (p + 46 > n) {
297
+ throw new Error("corrupt DOCX (ZIP central-directory entry truncated)");
298
+ }
299
+ if (u32(p) !== CD_SIG) {
300
+ throw new Error("corrupt DOCX (ZIP central-directory signature invalid)");
301
+ }
302
+ const method = u16(p + 10);
303
+ const compressedSize = u32(p + 20);
304
+ const nameLen = u16(p + 28);
305
+ const extraLen = u16(p + 30);
306
+ const commentLen = u16(p + 32);
307
+ const lho = u32(p + 42);
308
+ const nameStart = p + 46;
309
+ if (nameStart + nameLen > n) {
310
+ throw new Error("corrupt DOCX (ZIP entry name past end of file)");
311
+ }
312
+ const name = utf8(nameStart, nameStart + nameLen);
313
+ if (name === "word/document.xml") {
314
+ target = { method, compressedSize, lho };
315
+ break;
316
+ }
317
+ p = nameStart + nameLen + extraLen + commentLen;
318
+ }
319
+ if (!target) {
320
+ throw new Error("not a valid Word document (word/document.xml not found in the ZIP)");
321
+ }
322
+ // 3. Read the local file header to compute where the entry's data begins.
323
+ const { method, compressedSize, lho } = target;
324
+ if (lho + 30 > n) {
325
+ throw new Error("corrupt DOCX (ZIP local file header past end of file)");
326
+ }
327
+ if (u32(lho) !== LFH_SIG) {
328
+ throw new Error("corrupt DOCX (ZIP local file header signature invalid)");
329
+ }
330
+ const localNameLen = u16(lho + 26);
331
+ const localExtraLen = u16(lho + 28);
332
+ const dataStart = lho + 30 + localNameLen + localExtraLen;
333
+ if (dataStart + compressedSize > n) {
334
+ throw new Error("corrupt DOCX (ZIP entry data past end of file)");
335
+ }
336
+ const comp = bytes.subarray(dataStart, dataStart + compressedSize);
337
+ // 4. Decompress: method 8 = deflate (raw), method 0 = stored (as-is).
338
+ let xmlBytes;
339
+ if (method === 8) {
340
+ // maxOutputLength caps the inflated size → throws on a zip-bomb DOCX.
341
+ xmlBytes = zlib.inflateRawSync(comp, { maxOutputLength: MAX_DOCX_XML_BYTES });
342
+ }
343
+ else if (method === 0) {
344
+ xmlBytes = comp;
345
+ }
346
+ else {
347
+ throw new Error(`unsupported DOCX ZIP compression method ${method}`);
348
+ }
349
+ const xml = new TextDecoder("utf-8", { fatal: false }).decode(xmlBytes);
350
+ // 5. XML → text. Convert paragraph/row breaks and tabs to whitespace FIRST,
351
+ // then strip ALL remaining tags whole (`<w:p w14:paraId="…">` included, so
352
+ // no attribute text leaks), decode entities, collapse intra-line runs.
353
+ const text = decodeEntities(xml
354
+ .replace(/<\/w:p>/g, "\n")
355
+ .replace(/<\/w:tr>/g, "\n")
356
+ .replace(/<w:tab\b[^>]*\/?>/g, "\t")
357
+ .replace(/<[^>]+>/g, ""))
358
+ .replace(/[ \t]+/g, " ")
359
+ .replace(/ *\n */g, "\n")
360
+ .replace(/\n{3,}/g, "\n\n")
361
+ .trim();
362
+ return text;
363
+ }
364
+ /**
365
+ * Fetch a SAM attachment keyless and return its extracted TEXT.
366
+ *
367
+ * See the module header for the truthfulness invariants. Throws a
368
+ * `ToolErrorCarrier` for: bad/non-SAM URL (`invalid_input`, no fetch), a 404
369
+ * (`not_found`), or a down fetch (`upstream_unavailable`, retryable). Otherwise
370
+ * ALWAYS returns a payload — a document we cannot extract yields `text:null` +
371
+ * a disclosed note, never an exception and never a fabricated string.
372
+ */
373
+ export async function fetchAttachmentText(args) {
374
+ const parsed = assertSamAttachmentUrl(args.url);
375
+ const url = parsed.toString();
376
+ const maxChars = Math.min(Math.max(1, args.maxChars ?? DEFAULT_MAX_CHARS), MAX_MAX_CHARS);
377
+ // ── Fetch keyless. A down fetch is an OUTAGE (throw), never empty text. ──
378
+ let res;
379
+ try {
380
+ res = await fetch(url, {
381
+ headers: { "User-Agent": BROWSER_UA, Accept: "*/*" },
382
+ redirect: "follow",
383
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
384
+ });
385
+ }
386
+ catch (e) {
387
+ // Network-level fault or timeout ⇒ retryable outage.
388
+ const err = {
389
+ kind: "upstream_unavailable",
390
+ message: `Network error fetching SAM attachment ${url}: ${e.message}. The service is unavailable, NOT an empty attachment — retry.`,
391
+ retryable: true,
392
+ retryAfterSeconds: 30,
393
+ upstreamEndpoint: "sam:attachment",
394
+ };
395
+ throw new ToolErrorCarrier(err);
396
+ }
397
+ if (res.status === 404) {
398
+ throw new ToolErrorCarrier({
399
+ kind: "not_found",
400
+ message: `SAM attachment not found (HTTP 404) at ${url}. The attachment id is gone.`,
401
+ retryable: false,
402
+ upstreamStatus: 404,
403
+ upstreamEndpoint: "sam:attachment",
404
+ });
405
+ }
406
+ if (!res.ok) {
407
+ // Any other non-2xx (5xx, 429, transient 4xx) ⇒ treat as a retryable
408
+ // outage. A DOWN attachment endpoint is NOT an empty document.
409
+ throw new ToolErrorCarrier({
410
+ kind: "upstream_unavailable",
411
+ message: `SAM attachment fetch returned HTTP ${res.status} at ${url} — the service is unavailable, NOT an empty attachment. Retry.`,
412
+ retryable: true,
413
+ retryAfterSeconds: 60,
414
+ upstreamStatus: res.status,
415
+ upstreamEndpoint: "sam:attachment",
416
+ });
417
+ }
418
+ // Redirect safety (SSRF defense-in-depth): `fetch` followed redirects — SAM's
419
+ // download endpoint 303-redirects to a time-signed S3 URL and the extraction
420
+ // DEPENDS on that hop, so off-host redirects can't be forbidden outright.
421
+ // Instead re-validate the FINAL host: allow *.sam.gov and SAM's S3 attachment
422
+ // store, reject anything else — a redirect must never reach an internal /
423
+ // cloud-metadata / attacker host and have its body read back as "text".
424
+ const finalHost = (() => {
425
+ try {
426
+ return new URL(res.url).hostname.toLowerCase();
427
+ }
428
+ catch {
429
+ return "";
430
+ }
431
+ })();
432
+ // SAM's attachment endpoint 303-redirects to a SINGLE time-signed S3 bucket
433
+ // (LIVE-VERIFIED 2026-07-05: `iae-fbo-attachments.s3.amazonaws.com`). Pin THAT
434
+ // bucket — across S3 host-form/region variants (`<b>.s3.amazonaws.com`,
435
+ // `<b>.s3.<region>.amazonaws.com`, `<b>.s3-<region>.amazonaws.com`, dualstack)
436
+ // — rather than trusting ALL of AWS S3: a sam.gov→attacker-owned-bucket redirect
437
+ // (any `*.s3.amazonaws.com` is registrable) must NOT have its body read back as
438
+ // "SAM text". Fail-closed: an unknown bucket → rejected with a clear error.
439
+ const SAM_S3_BUCKETS = ["iae-fbo-attachments"];
440
+ const isSamS3Host = (h) => h.endsWith(".amazonaws.com") &&
441
+ SAM_S3_BUCKETS.some((b) => h.startsWith(`${b}.s3.`) || h.startsWith(`${b}.s3-`));
442
+ const finalHostOk =
443
+ // Only trust an EMPTY final host when NO redirect occurred; if `fetch` redirected
444
+ // but the runtime hid the URL we cannot verify the target → reject (was trusted).
445
+ (finalHost === "" && !res.redirected) ||
446
+ finalHost === "sam.gov" ||
447
+ finalHost.endsWith(".sam.gov") ||
448
+ isSamS3Host(finalHost);
449
+ if (!finalHostOk) {
450
+ throw new ToolErrorCarrier({
451
+ kind: "invalid_input",
452
+ message: `The SAM attachment URL redirected to an unexpected host ${JSON.stringify(finalHost)}; refusing to read it back (SSRF safety). Expected sam.gov or SAM's S3 attachment store.`,
453
+ retryable: false,
454
+ });
455
+ }
456
+ // Size guard: refuse an attachment larger than the cap BEFORE buffering it
457
+ // whole (unpdf then parses the whole buffer). SAM/S3 send content-length.
458
+ const declaredLen = Number(res.headers.get("content-length") ?? "");
459
+ if (Number.isFinite(declaredLen) && declaredLen > MAX_ATTACHMENT_BYTES) {
460
+ throw new ToolErrorCarrier({
461
+ kind: "invalid_input",
462
+ message: `SAM attachment is ${Math.round(declaredLen / 1048576)} MB, over this tool's ${Math.round(MAX_ATTACHMENT_BYTES / 1048576)} MB limit — download it directly at ${url}.`,
463
+ retryable: false,
464
+ upstreamEndpoint: "sam:attachment",
465
+ });
466
+ }
467
+ const contentType = res.headers.get("content-type");
468
+ const disposition = res.headers.get("content-disposition");
469
+ const filename = filenameFromDisposition(disposition);
470
+ const ext = extOf(filename);
471
+ const buf = await res.arrayBuffer();
472
+ const bytes = new Uint8Array(buf);
473
+ const sizeBytes = bytes.byteLength;
474
+ const format = detectFormat(bytes, contentType ?? "", ext);
475
+ const notes = [];
476
+ let text = null;
477
+ let pages = null;
478
+ if (format === "pdf") {
479
+ try {
480
+ const pdf = await getDocumentProxy(bytes);
481
+ const { totalPages, text: extracted } = await extractText(pdf, {
482
+ mergePages: true,
483
+ });
484
+ pages = totalPages;
485
+ text = extracted;
486
+ }
487
+ catch (e) {
488
+ // Encrypted / corrupt / unsupported PDF. This is NOT an outage and NOT an
489
+ // empty document — disclose the failure and return text:null.
490
+ text = null;
491
+ pages = null;
492
+ notes.push(`PDF text extraction failed (extractionError): ${e.message}. The file may be encrypted, corrupt, or image-only (scanned). It was NOT read; download it at ${url}.`);
493
+ }
494
+ }
495
+ else if (format === "docx") {
496
+ // DOCX (ZIP + word/document.xml): extract the body text dependency-free.
497
+ // A corrupt/invalid/no-word-document DOCX is NOT an outage and NOT an empty
498
+ // document — disclose the failure and return text:null (the B1 empty-guard
499
+ // downstream turns a genuinely empty body into text:null + a disclosed note).
500
+ try {
501
+ text = extractDocxText(bytes);
502
+ pages = null;
503
+ }
504
+ catch (e) {
505
+ text = null;
506
+ pages = null;
507
+ notes.push(`DOCX text extraction failed: ${e.message}. The file may be corrupt or not a valid Word document — download it at ${url}.`);
508
+ }
509
+ }
510
+ else if (format === "html") {
511
+ text = stripHtml(new TextDecoder("utf-8", { fatal: false }).decode(bytes));
512
+ }
513
+ else if (format === "text") {
514
+ text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
515
+ }
516
+ else {
517
+ // binary / other — do NOT half-parse.
518
+ text = null;
519
+ pages = null;
520
+ notes.push(`${format} content is not extractable keyless in this tool — download it at ${url}.`);
521
+ }
522
+ // ── Validate the extracted text is REAL content, not a masquerade. ──
523
+ // A fetch+parse that yields EMPTY text (an image-only/scanned PDF with no text
524
+ // layer — common for wage determinations, signed SF-1442s, drawings — or an
525
+ // empty body) or DECODE-GARBAGE (a non-UTF-8 charset mis-decoded into NUL /
526
+ // replacement-char soup) must NOT read as "the document's text is empty/this".
527
+ // Report text:null + a disclosed reason instead (a scanned PDF keeps its honest
528
+ // `pages` count — it HAS pages, just no text layer).
529
+ if (text != null) {
530
+ const replacementShare = (text.match(/\uFFFD/g)?.length ?? 0) / Math.max(1, text.length);
531
+ if (text.trim().length === 0) {
532
+ notes.push(format === "pdf"
533
+ ? `This PDF has NO extractable text layer — it is likely scanned/image-only (a photo of the page). It was NOT read as text; download it at ${url} (or run OCR).`
534
+ : `The ${format} attachment decoded to empty/whitespace content — nothing to read; download it at ${url}.`);
535
+ text = null;
536
+ }
537
+ else if (text.includes("\u0000") || replacementShare > 0.1) {
538
+ notes.push(`The ${format} attachment could not be decoded as readable UTF-8 text (it likely uses a different character set, or is binary mislabeled as text) — download it at ${url}.`);
539
+ text = null;
540
+ }
541
+ }
542
+ // ── Truncate honestly. ──
543
+ let truncated = false;
544
+ if (text != null && text.length > maxChars) {
545
+ text = text.slice(0, maxChars);
546
+ truncated = true;
547
+ notes.push(`Text truncated to maxChars=${maxChars} characters (the document is longer). Raise maxChars or fetch the file directly for the full text.`);
548
+ }
549
+ const extracted = text != null;
550
+ if (extracted) {
551
+ notes.push(`Detected format: ${format}.`);
552
+ }
553
+ const data = {
554
+ url,
555
+ filename,
556
+ contentType,
557
+ format,
558
+ sizeBytes,
559
+ pages,
560
+ text,
561
+ truncated,
562
+ extracted,
563
+ };
564
+ return withMeta(data, {
565
+ source: "sam.gov (keyless attachment)",
566
+ keylessMode: true,
567
+ // We fully delivered the document iff we extracted text AND did not truncate.
568
+ complete: extracted && !truncated ? undefined : false,
569
+ truncated,
570
+ returned: extracted ? 1 : 0,
571
+ totalAvailable: null,
572
+ fieldsUnavailable: extracted ? [] : ["text"],
573
+ notes,
574
+ });
575
+ }
576
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}