npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@liiift-studio/unseal

v0.1.0

Published

Detect and remove fake PDF redactions. Works on PDFs from any tool.

Readme

unseal

Detect and remove fake PDF redactions. Works on PDFs from any tool — Acrobat, PDFTron, open-redact-pdf, and others.

The first package of its kind on npm.

Installation

npm install @liiift-studio/unseal

CLI

# Audit a PDF for fake or insecure redactions
npx @liiift-studio/unseal audit document.pdf

# Use a more thorough preset
npx @liiift-studio/unseal audit document.pdf --preset compliance

# Output as JSON
npx @liiift-studio/unseal audit document.pdf --json

# Strip fake redactions and write a usable PDF
npx @liiift-studio/unseal strip document.pdf --output clean.pdf

# Also write a JSON findings report
npx @liiift-studio/unseal strip document.pdf --output clean.pdf --report findings.json

Presets

| Preset | Checks | Speed | |---|---|---| | quick (default) | Text-under-box, incremental save, metadata, pending annotations | <100ms | | compliance | All quick checks + glyph position analysis | ~500ms | | forensic | All checks including pattern oracle | Slow |

API

import { audit, unseal, AuditPresets } from '@liiift-studio/unseal';
import { readFile, writeFile } from 'fs/promises';

const pdf = await readFile('document.pdf');

// Audit — Tier 1 checks
const report = await audit(pdf.buffer, AuditPresets.quick);
console.log(report.clean);      // false
console.log(report.findings);   // AuditFinding[]
console.log(report.sha256);     // SHA-256 of the input PDF

// Audit — Tier 2/3 checks
const deepReport = await audit(pdf.buffer, AuditPresets.forensic);

// Strip fake redactions
const result = await unseal(pdf.buffer, { output: 'both' });
if (result.pdf) {
  await writeFile('clean.pdf', result.pdf);
}
console.log(result.overlaysStripped);       // number
console.log(result.annotationsRemoved);     // number
console.log(result.priorRevisionRecovered); // boolean

// Strip with Tier 2/3 audit included
const result2 = await unseal(pdf.buffer, {
  output: 'both',
  auditOptions: { glyphPositionLeak: true, patternOracle: true },
});

How it works

Fake redactions fall into four scenarios:

| Scenario | Description | |---|---| | A | Black rectangle drawn over text in the content stream — text is still in the file | | B | Redact annotation added but never applied — underlying text untouched | | C | PDF saved incrementally — prior unredacted revision still in the file | | D | Text removed from content stream but detectable by layout analysis |

Checks

| Check | Tier | Severity | What it detects | |---|---|---|---| | text-under-box | 1 | CRITICAL | Text covered by a filled rectangle | | incremental-save | 1 | HIGH | Multiple %%EOF markers (prior revision present) | | metadata-leak | 1 | INFO/MEDIUM | Author, email, phone in PDF metadata | | pending-annotation | 1 | CRITICAL | Unapplied /Redact annotations | | glyph-position | 2 | HIGH/MEDIUM | Character spacing anomalies inconsistent with the embedded font's advance widths — indicates OCR reconstruction or uniform-width character substitution | | pattern-oracle | 3 | — | LLM-ranked candidate strings for each redaction bar, inferred from bar width and surrounding context |

Types

interface AuditReport {
  clean: boolean;
  findings: AuditFinding[];
  checkedAt: string;    // ISO 8601
  sha256: string;       // SHA-256 hex of input PDF
}

interface AuditFinding {
  check: 'text-under-box' | 'incremental-save' | 'metadata-leak' | 'pending-annotation' | 'glyph-position';
  severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO';
  page?: number;
  bbox?: [number, number, number, number];
  detail: string;
  recoveredText?: string;
  candidates?: CandidateString[];  // Tier 3 only
}

interface CandidateString {
  text: string;
  confidence: number;   // 0–1
  reasoning: string;
}

interface UnsealOptions {
  stripOverlays?: boolean;          // Default: true
  stripAnnotations?: boolean;       // Default: true
  extractPriorRevision?: boolean;   // Default: true
  annotateCandidates?: boolean;     // Default: true
  output?: 'pdf' | 'report' | 'both';  // Default: 'both'
  includeAudit?: boolean;           // Default: true
  auditOptions?: AuditOptions;      // Enable Tier 2/3 checks
}

interface UnsealResult {
  pdf?: Uint8Array;
  findings: UnsealFinding[];
  overlaysStripped: number;
  annotationsRemoved: number;
  priorRevisionRecovered: boolean;
  auditReport?: AuditReport;
}

Requirements

  • Node.js >= 18
  • No native binaries
  • Tier 3 pattern oracle requires an ANTHROPIC_API_KEY or Vercel AI Gateway credentials

License

MIT — unseal.dev