Validate RFC 3161 document timestamps and expose timestamping (#7095)

# Description of Changes

Fixes timestamp issue and adds timestamp to the signing/security policiy
---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have run `task check` to verify linters, typechecks, and tests
pass
- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing)
for more details.
This commit is contained in:
Anthony Stirling
2026-07-21 17:37:54 +00:00
committed by GitHub
parent 5e3e89ccb2
commit 621731bda1
9 changed files with 179 additions and 8 deletions
@@ -7475,6 +7475,10 @@ label = "Redact sensitive information"
desc = "Removes hidden JavaScript so nothing can run automatically when the document is opened."
label = "Strip active content"
[portal.policies.wizard.capability.timestampPdf]
desc = "Proves the document existed in this exact form at a point in time, using an independent timestamp authority. Only a hash is sent - the document never leaves your server."
label = "Add a trusted timestamp"
[portal.policies.wizard.capability.watermark]
desc = "Stamps a visible mark (e.g. “Confidential”) across every page."
label = "Apply a watermark"
@@ -8,7 +8,7 @@ export interface SignatureValidationBackendResult {
coversEntireDocument?: boolean | null; // false = content appended after signing
revocationChecked?: boolean | null;
revocationStatus?: string | null; // "not-checked" | "good" | "revoked" | "soft-fail" | "unknown"
validationTimeSource?: string | null; // "current" | "signing-time" | "timestamp"
validationTimeSource?: string | null; // "current" | "signing-time" | "timestamp" | "document-timestamp"
signerName?: string | null;
signatureDate?: string | null;
reason?: string | null;
@@ -37,7 +37,7 @@ export interface SignatureValidationSignature {
coversEntireDocument?: boolean | null; // false = content appended after signing
revocationChecked?: boolean | null;
revocationStatus?: string | null; // "not-checked" | "good" | "revoked" | "soft-fail" | "unknown"
validationTimeSource?: string | null; // "current" | "signing-time" | "timestamp"
validationTimeSource?: string | null; // "current" | "signing-time" | "timestamp" | "document-timestamp"
signerName: string;
signatureDate: string;
reason: string;
@@ -114,6 +114,14 @@ const CAPABILITY_META: Record<
descEn:
"Removes hidden JavaScript so nothing can run automatically when the document is opened.",
},
timestampPdf: {
labelKey: "portal.policies.wizard.capability.timestampPdf.label",
labelEn: "Add a trusted timestamp",
descKey: "portal.policies.wizard.capability.timestampPdf.desc",
descEn:
"Proves the document existed in this exact form at a point in time, using an independent timestamp authority. Only a hash is sent - the document never leaves your server.",
},
watermark: {
labelKey: "portal.policies.wizard.capability.watermark.label",
labelEn: "Apply a watermark",
@@ -21,6 +21,7 @@ describe("POLICY_OPERATIONS", () => {
"ocr",
"redact",
"sanitize",
"timestampPdf",
"watermark",
]);
for (const id of ALL_TOOL_IDS) {
@@ -7,6 +7,7 @@
import { describeToolOperation } from "@app/hooks/tools/shared/toolOperationDescriptor";
import { redactOperationConfig } from "@app/hooks/tools/redact/useRedactOperation";
import { sanitizeOperationConfig } from "@app/hooks/tools/sanitize/useSanitizeOperation";
import { timestampPdfOperationConfig } from "@app/hooks/tools/timestampPdf/useTimestampPdfOperation";
import { addWatermarkOperationConfig } from "@app/hooks/tools/addWatermark/useAddWatermarkOperation";
import { ocrOperationConfig } from "@app/hooks/tools/ocr/useOCROperation";
import { flattenOperationConfig } from "@app/hooks/tools/flatten/useFlattenOperation";
@@ -53,6 +54,12 @@ export const POLICY_OPERATIONS = {
"/api/v1/security/sanitize-pdf",
sanitizeOperationConfig,
),
// RFC 3161 timestamp. Already a SISO tool; surfacing it here is what makes a signature durable
// in a pipeline (PAdES-LTV), and only a SHA-256 hash reaches the TSA - never the document.
timestampPdf: describeToolOperation(
"/api/v1/security/timestamp-pdf",
timestampPdfOperationConfig,
),
watermark: describeToolOperation(
"/api/v1/security/add-watermark",
addWatermarkOperationConfig,