FileScan performs heuristic detection of malicious / script-injection content in uploaded files. It is not a certified CDR (Content Disarm & Reconstruction) product and is not a replacement for a full antivirus or a certified commercial solution.
- It is a defense-in-depth layer, not a guarantee.
- Obfuscated/encrypted payloads and zero-day threats may evade detection.
- The optional ClamAV layer detects known malware only (signature-based).
- The software is provided without warranty (see LICENSE).
Always validate behavior in your own environment before relying on it.
FileScan is scan-only: it inspects an upload and returns a verdict — it never
serves files back. For PDF/OOXML, Clean means the supported structural/active-content
inspection completed with no finding, warning, skipped work, exhausted budget or ambiguity.
It still does not mean "malware-free" or "safe to render inline in a browser".
For the class of attack where a PDF (or other document) carries auto-running JavaScript aimed at stored XSS, the strongest mitigations happen at serving time, in the consuming application — FileScan cannot apply them. When you serve a user-uploaded file to a browser, you must:
- Serve it as a download, not inline:
Content-Disposition: attachment; filename="...". - Disable MIME sniffing:
X-Content-Type-Options: nosniff. - Send the correct, explicit
Content-Type(e.g.application/pdf) — do not let the browser guess. - Apply a restrictive Content-Security-Policy on the route that renders/embeds uploads,
e.g.
Content-Security-Policy: script-src 'self'; object-src 'none'. - Prefer serving user uploads from a separate origin/sandbox domain (no session cookies) so that even a rendered payload cannot reach the main app's context.
Treating a Clean verdict as a license to render the file inline on the application's
own origin re-introduces the stored-XSS risk that FileScan reduced.
O FileScan apenas escaneia: ele inspeciona o upload e devolve um veredito — nunca
serve o arquivo. Para PDF/OOXML, Clean significa que a inspeção estrutural/conteúdo ativo
suportada terminou sem achado, warning, trabalho pulado, orçamento esgotado ou ambiguidade.
Ainda não significa "sem malware" nem "pode renderizar inline com segurança".
Para a classe de ataque em que um PDF (ou outro documento) carrega JavaScript de auto-execução visando stored XSS, as mitigações mais fortes acontecem no momento de servir o arquivo, no app consumidor — o FileScan não tem como aplicá-las. Ao entregar um arquivo enviado por usuário a um browser, você deve:
- Servir como download, não inline:
Content-Disposition: attachment; filename="...". - Desligar o MIME sniffing:
X-Content-Type-Options: nosniff. - Mandar o
Content-Typecorreto e explícito (ex.:application/pdf) — não deixar o browser adivinhar. - Aplicar uma Content-Security-Policy restritiva na rota que renderiza/embute uploads,
ex.:
Content-Security-Policy: script-src 'self'; object-src 'none'. - De preferência, servir uploads de usuário a partir de uma origem/domínio sandbox separado (sem cookies de sessão), para que mesmo um payload renderizado não alcance o contexto do app principal.
Tratar um veredito Clean como licença para renderizar o arquivo inline na própria
origem da aplicação reintroduz o risco de stored XSS que o FileScan reduziu.
These are conscious gaps in the heuristic layer; ClamAV (when enabled) and the caller responsibilities above are the complementary layers.
- Non-Flate stream filters. PDF scanning covers the structural regions
through PdfPig (xref/trailer, indirect references and object streams), plus stream bodies that are literal
(no
/Filter) or FlateDecode (always decompressed and scanned inflated — compressed bytes are never judged raw, since random compressed data produces false positives). Bodies behind other filters (ASCIIHex, ASCII85, LZW, or chained filters), inside an encrypted PDF, in a structurally invalid file, behind an unsupported predictor, or past per-stream/aggregate entry, expansion, attachment-count or recursion limits are not silently skipped: the scan returnsNotInspected(neverClean) so the caller fails closed. Actually reading those bodies would require CDR or a sandbox. Embedded files are identified throughFileSpecassociations (EmbeddedFiles,AF, andFSinside/Subtype /FileAttachmentannotations) and direct or indirect/EFrelations; the optional/Type /EmbeddedFilemarker is not trusted as the sole source. Unrelated/EFor/FSextension keys are ignored. Invalid name-tree values (including/Limitson the root node) and broken/cyclic/non-stream associated targets returnNotInspected. Literal and hexadecimal name keys and child/Limitsare compared by decoded bytes. Cancellation propagates through parsing and decompression. - The "full inspection or
NotInspected" guarantee covers PDF and OOXML only. For legacy OLE2 (.doc/.xls), images, CSV and plain text the engine is a best-effort byte-scanning heuristic and never reports incomplete inspection:Cleanthere means "no marker matched", not "fully parsed". An obfuscated or compressed OLE2 VBA macro can evade the raw scan and still come backClean— if you accept OLE2 uploads, plug in an antivirus (IVirusScanner) or block those extensions viaAllowedExtensions.
São lacunas conscientes da camada heurística; o ClamAV (quando habilitado) e as responsabilidades de quem chama (acima) são as camadas complementares.
- Filtros de stream não-Flate. O scan de PDF cobre as regiões estruturais
via PdfPig (xref/trailer, referências indiretas e object streams) e os corpos de stream
literais (sem
/Filter) ou FlateDecode (sempre descomprimidos e varridos inflados — bytes comprimidos nunca são julgados crus, porque dados comprimidos aleatórios geram falso positivo). Corpos atrás de outros filtros (ASCIIHex, ASCII85, LZW ou filtros encadeados), com predictor não suportado, dentro de PDF criptografado, em estrutura inválida ou além dos limites por stream/agregados de entradas, expansão, anexos ou profundidade não são pulados em silêncio: o scan devolveNotInspected(nuncaClean) e o chamador falha fechado. LER esses corpos de fato exigiria CDR ou sandbox. Arquivos embutidos são identificados por associaçõesFileSpec(EmbeddedFiles,AFeFSdentro de annotations/Subtype /FileAttachment) e relações/EFdiretas ou indiretas; o marcador opcional/Type /EmbeddedFilenão é a única fonte. Chaves de extensão/EFou/FSsem esse contexto são ignoradas. Valores inválidos na name tree (inclusive/Limitsno nó raiz) e alvos associados quebrados, cíclicos ou que não sejam stream devolvemNotInspected. Chaves e/Limitsliterais ou hexadecimais são comparados pelos bytes decodificados. Cancelamento é propagado por parsing e descompressão. - A garantia "inspeção integral ou
NotInspected" cobre só PDF e OOXML. Para OLE2 legado (.doc/.xls), imagens, CSV e texto puro o motor é heurística best-effort de varredura de bytes e nunca reporta inspeção incompleta:Cleannesses formatos significa "nenhum marcador casou", não "parseado por completo". Uma macro VBA OLE2 ofuscada ou comprimida pode evadir a varredura crua e ainda voltarClean— se você aceita uploads OLE2, plugue um antivírus (IVirusScanner) ou bloqueie essas extensões viaAllowedExtensions.
If you find a security issue in FileScan itself (e.g. a way to bypass a check, a crash/DoS, or a false-negative class), please open a private report via GitHub Security Advisories, or open an issue without including a working malicious payload.
Please do not attach real malware or real user documents to public issues.
The _testfiles/ directory contains synthetic proof-of-concept samples (benign
demonstrators such as app.alert, calc, EICAR, and <?php echo>). Do not add real
malware or real user data to this repository.