How to Write Time-Based Security Policies in SafeDep vet
Kunal SinghTable of Contents
Malicious open source packages rely on speed. Threat actors publish compromised code to registries like npm and PyPI, hoping automated build systems or developers under high cognitive load pull the payload before security scanners catch it.
One practical defense against this race condition is a cooling-off period. If a package version was published less than 24 hours ago, blocking it temporarily gives the community and detection systems time to analyze it.
The right cooling-off duration is highly opinionated and depends on an organization’s risk appetite. A security-critical financial system might enforce a 7 day waiting period, while a fast-moving startup may accept 24 hours. Because there is no one size fits all answer, SafeDep took a policy as code approach, letting teams define their own time-based rules that can be version controlled, reviewed, and adapted as their risk posture evolves.
SafeDep/vet supports these time-based rules through the now() function within its Common Expression Language (CEL) policy evaluator. now() returns the current UTC timestamp, making it straightforward to calculate durations against package metadata.
The following guide covers how to integrate the now() function into security workflows using the CLI, Policy files, and GitHub Actions.
1. Using now() Inline via the CLI
For quick ad-hoc scans or bash scripts, a CEL expression can be passed directly to vet using the --filter-v2 flag. --filter-v2 is the new filter evaluation engine, which uses Insights V2 for rich package metadata, including published_at timestamp values.
To block any package published within the last 24 hours (a 24-hour cooling-off period):
vet scan -D . \ --filter-v2 'has(pkg.insight.package_published_at) && (now() - pkg.insight.package_published_at).getHours() < 24' \ --filter-failThe now() function can also enforce temporary, time-limited security freezes. For example, failing any dependency scan if the current date is past January 1st, 2026:
vet scan -D . \ --filter-v2 'now() > timestamp("2026-01-01T00:00:00Z")' \ --filter-failThe --filter-fail flag ensures the CLI exits with a non-zero status code if the policy triggers, making it well-suited for CI pipelines.
2. Using now() in a Policy YAML File
For production environments, rules should be defined in a policy.yaml file. This approach allows teams to document their security posture and track changes via git.
The following example policy.yaml denies both recent packages and recent high-severity vulnerabilities:
This uses the Policy v2 syntax. A full example can be found here.
version: POLICY_VERSION_V2type: POLICY_TYPE_DENYtarget: POLICY_TARGET_VETname: General Purpose OSS Best Practiceslabels: - general - safedep-managedrules: - name: critical-or-high-vulns description: Critical or high risk vulnerabilities were found check: RULE_CHECK_VULNERABILITY value: pkg.vulnerabilities.exists(vuln, vuln.severity in ['CRITICAL', 'HIGH'])
- name: Deny Recently Updated description: This policy denies recently updated packages check: RULE_CHECK_MAINTENANCE value: (now() - pkg.insight.package_published_at).getHours() < 24To execute this filter suite locally or in CI:
vet scan -D . --filter-v2-suite policy.yaml --filter-fail3. Enforcing Policies in GitHub Actions
A natural place to enforce a cooling-off period is in Pull Requests. Using the official safedep/vet-action, teams can automatically run their policy.yaml on every PR.
First, commit the policy file to a dedicated path in the repository, such as .github/vet/policy.yaml.
Then, update the GitHub workflow .yml file to point to it:
name: vet Security Scanon: [pull_request, push]
jobs: vet: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4
- name: Run vet with time-based policy uses: safedep/vet-action@v1 with: policy-v2: .github/vet/policy.yaml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}This automation ensures any Pull Request introducing a package published less than 24 hours ago will fail the CI check, preventing zero-day malicious drops from being merged.
Time-based policies serve as a blunt but effective guardrail against fast-moving supply chain attacks. By utilizing vet’s CEL evaluation and the now() function, practitioners can build dynamic, timestamp-aware rules that execute directly in terminal or CI environments.
This feature was made possible by a great community contribution from @againagainst in PR #682. Open source security is a team effort, and contributions like this help make the ecosystem safer for everyone.
Install vet to start scanning projects, enforcing policies, and preventing supply chain attacks.
- Policy as Code
- SafeDep vet
- CEL
- Security
- CI/CD
Author
Kunal Singh
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering

Malicious npm Package pino-sdk-v2 Exfiltrates Secrets to Discord
A malicious npm package impersonating the popular pino logger was detected by SafeDep. The package hides obfuscated code inside a legitimate library file to steal environment secrets and send them to...

Integrate SafeDep MCP in GitHub Agentic Workflow
Learn how to integrate SafeDep MCP with GitHub Agentic Workflows to automatically evaluate the security posture of OSS dependencies in your pull requests using AI.

Gryph: Audit Trail for AI Coding Agents
AI coding agents operate with broad access to your codebase, credentials, and shell. Gryph logs every action they take to a local SQLite database, making agent behavior visible, queryable, and...

Shadow AI Discovery: Find Every AI Tool and SDK in Your Stack
AI tools and SDKs are spreading across developer environments faster than security teams can track. vet discovers agents, MCP servers, extensions, and AI SDK usage in code. Open source, local, one...

Ship Code
Not Malware
Install the SafeDep GitHub App to keep malicious packages out of your repos.
