How to Write Time-Based Security Policies in SafeDep vet

Kunal Singh Kunal Singh
4 min read

Table 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):

Terminal window
vet scan -D . \
--filter-v2 'has(pkg.insight.package_published_at) && (now() - pkg.insight.package_published_at).getHours() < 24' \
--filter-fail

The 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:

Terminal window
vet scan -D . \
--filter-v2 'now() > timestamp("2026-01-01T00:00:00Z")' \
--filter-fail

The --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_V2
type: POLICY_TYPE_DENY
target: POLICY_TARGET_VET
name: General Purpose OSS Best Practices
labels:
- general
- safedep-managed
rules:
- 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() < 24

To execute this filter suite locally or in CI:

Terminal window
vet scan -D . --filter-v2-suite policy.yaml --filter-fail

3. 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 Scan
on: [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

Kunal Singh

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

Gryph: Audit Trail for AI Coding Agents

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...

abhisek
Background
SafeDep Logo

Ship Code

Not Malware

Install the SafeDep GitHub App to keep malicious packages out of your repos.

GitHub Install GitHub App