Skip to content

New certificates for your domains

Who this is for: security teams who want to know about a new certificate on their domains (or a lookalike using one of their domain names) before an attacker uses it against their customers.

Before you start: the connections crtsh and security-chat, and the company variables company_domains and security_email.

Every hour, the workflow queries crt.sh (a public certificate transparency log search) for each domain in company_domains. It remembers the highest certificate id it has seen for each domain, so it only reports certificates logged since the last check. When there are new ones, it emails security a CSV and posts a short alert to a chat channel. A new certificate for one of your domains is often routine (a renewal, a new subdomain), but it can also reveal a phishing site or shadow IT using your name before you’d otherwise notice.

  • crtsh: a connection with no sign-in, base https://crt.sh. It only reads crt.sh’s public search results; there is nothing to authenticate.
  • security-chat: a webhook connection to your chat tool (for example a Slack or Microsoft Teams incoming webhook), used only to post the short alert text.
  • It emails only security_email, a CSV of the new certificates and nothing else.
  • State: one small number per domain (the highest certificate id seen), nothing else.
  • No secret value is ever in the workflow.
  1. Create the crtsh connection with no sign-in and base address https://crt.sh (see Create a connection).
  2. Create the security-chat connection pointing at your chat tool’s incoming webhook.
  3. Add the company variables company_domains (a list of your domains, for example ["bank.example", "partner-portal.example"]) and security_email.
  4. Take it into use from the Liitora library (see Take a library workflow into use), then read the test cases on your copy, run them and approve it.
  • Fewer routine alerts. crt.sh logs every certificate, including automatic renewals. If your certificate authority issues predictably, filter the script’s results by issuer_name to alert only on unexpected issuers.
  • A stricter check for lookalike domains. Add a second, separate schedule that searches for domains that merely contain your brand name (not your own registered domains), to catch phishing domains that aren’t subdomains of yours at all.
  • A different chat tool. Change the security-chat connection and the notify_chat step’s url and json to match your tool’s incoming webhook format.
  • Daily instead of hourly. Certificate transparency logs are not instant; if hourly is more noise than you want, change the cron to run once or twice a day.

Every hour, on the hour or when someone starts it by hand: query crt.sh for each domain and find certificates issued since the last check. Depending on that result, build a CSV of the new certificates and post a short alert to the security chat channel, then email security about the new certificates.

What Details
Publisher Liitora
Version 1.0.0
Licence Apache-2.0
Content hash sha256:f2311d47aaceaa2a333ab35016cd04e980f1e39443808239dfdef23c1d50b7ff
Tests 3
Connections crtsh, security-chat
Company variables company_domains, security_email
Secrets None
Personal data None. Certificate records name domains and issuers, not people.
Saved state Yes
Runs on your own runner No
Actions platform/csv.build@v1, platform/email.send@v1, platform/webhook.send@v1

Liitora signs each version of this workflow. When your company takes it into use, Liitora checks the content hash and the signature again before it copies the workflow into your company. See How the library is kept safe.

# A Liitora library workflow for a bank CISO (see the guide, Library > New certificates for
# your domains). Connections used: crtsh (no sign-in, base https://crt.sh),
# security-chat (a webhook to a chat channel). Company variables
# used: company_domains (a list of domain names), security_email.
name: New certificates for your domains
description: Every hour, query crt.sh for each company domain, remember the highest certificate id seen, and alert security by email and chat about certificates issued since the last check, which can reveal phishing sites or shadow IT.
on:
schedule:
- cron: "0 * * * *"
timezone: UTC
manual: true
concurrency:
group: certificate-watch
cancel_in_progress: false
steps:
- id: find_new
name: Query crt.sh for each domain and find certificates issued since the last check
connections:
crtsh: crtsh
with:
domains: ${{ vars.company_domains }}
run: |
export default async function run(ctx, input) {
const crtsh = ctx.connection("crtsh");
const domains = input.domains || [];
const newCerts = [];
for (const domain of domains) {
const cursorKey = `cert_max_id_${domain.replace(/\./g, "_")}`;
const cursor = (await ctx.state.get(cursorKey)) || 0;
const res = await crtsh.get("/", { query: { q: `%.${domain}`, output: "json" } });
if (!res.ok) {
throw new Error(`crt.sh answered ${res.status} for ${domain}`);
}
const rows = res.json() || [];
let maxId = cursor;
for (const row of rows) {
const id = Number(row.id);
if (id > cursor) {
newCerts.push({
domain,
id,
common_name: row.common_name || row.name_value || "",
issuer_name: row.issuer_name || "",
entry_timestamp: row.entry_timestamp || "",
});
}
if (id > maxId) {
maxId = id;
}
}
if (maxId > cursor) {
await ctx.state.set(cursorKey, maxId);
}
}
newCerts.sort((a, b) => (a.entry_timestamp < b.entry_timestamp ? -1 : 1));
return { newCerts, count: newCerts.length };
}
- id: build_csv
name: Build a CSV of the new certificates
needs: [find_new]
if: ${{ steps.find_new.outputs.newCerts.length > 0 }}
uses: platform/csv.build@v1
with:
rows: ${{ steps.find_new.outputs.newCerts }}
columns: [domain, id, common_name, issuer_name, entry_timestamp]
filename: new-certificates.csv
- id: email_security
name: Email security about the new certificates
needs: [build_csv]
if: ${{ steps.find_new.outputs.newCerts.length > 0 }}
uses: platform/email.send@v1
with:
to: ["${{ vars.security_email }}"]
subject: "New certificates for your domains: ${{ steps.find_new.outputs.count }}"
text: "crt.sh logged new certificates for your domains since the last check. A new certificate can be routine, or it can be a phishing site or shadow IT using your name. The certificates are attached as a CSV."
attachments: ["${{ artifacts.new-certificates.csv }}"]
- id: notify_chat
name: Post a short alert to the security chat channel
needs: [find_new]
if: ${{ steps.find_new.outputs.newCerts.length > 0 }}
uses: platform/webhook.send@v1
connections:
target: security-chat
with:
url: /hooks/certs
json:
text: "${{ steps.find_new.outputs.count }} new certificate(s) found for your domains. Check email for the details."
  • 1.0.0 (2026-10-04): First version in the library.