---
title: "How to Write Time-Based Security Policies in SafeDep vet"
description: "Protect against unknown malicious open source packages by enforcing a supply chain cooling-off period using the now() CEL function in SafeDep vet."
url: "https://safedep.io/blog/writing-time-based-policies-in-vet-cel"
author: "Kunal Singh"
published: "2026-03-10"
---

# How to Write Time-Based Security Policies in SafeDep vet

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](https://github.com/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](https://docs.safedep.io/vet/guides/insights-api-using-typescript) 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):

```bash
vet scan -D . \
  --filter-v2 '!has(pkg.insight.package_published_at) || (now() - pkg.insight.package_published_at).getHours() < 24' \
  --filter-fail
```

This policy expression evaluates two conditions: whether a package was published within the last 24 hours, or if it lacks a `published_at` timestamp entirely.

> The `!has(pkg.insight.package_published_at)` check acts as a safeguard against packages published mere moments ago. SafeDep retrieves package version data from sources like [deps.dev](https://deps.dev). If a package is so new that it hasn't been indexed yet, the `published_at` field will be missing, and the policy implicitly treats it as a brand-new dependency.

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:

```bash
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](https://github.com/safedep/vet-action/blob/main/data/policy-v2.yml).

```yaml
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: !has(pkg.insight.package_published_at) || (now() - pkg.insight.package_published_at).getHours() < 24
```

To execute this filter suite locally or in CI:

```bash
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:

```yaml
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](https://github.com/againagainst) in [PR #682](https://github.com/safedep/vet/pull/682). Open source security is a team effort, and contributions like this help make the ecosystem safer for everyone.

[Install vet](https://github.com/safedep/vet) to start scanning projects, enforcing policies, and preventing supply chain attacks.
