X.509 Certificate Parser (javascript, written by Codex)
envgap__codex__javascript-t1-16
Written by a coding agent; not on GitHubWritten 2026-03-02
01 / FAILURE SIGNATURE
As the study recorded it
None
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
package.json- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
codex/javascript-t1 #16 · read the task the agent was given
Codex wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: X.509 Certificate Parser Write a program that parses X.509 digital certificates in PEM and DER formats, extracts all fields, validates the certificate chain, and checks expiration status. FUNCTIONAL REQUIREMENTS: - Accept a certificate file path as a command-line argument (support both PEM and DER formats, auto-detected) - Extract and display all certificate fields: version, serial number, issuer, subject, validity period (not before/not after), public key algorithm and size, signature algorithm, and fingerprints (SHA-1, SHA-256) - Parse all X.509 v3 extensions: Subject Alternative Names (SANs), Key Usage, Extended Key Usage, Basic Constraints, Authority/Subject Key Identifiers, CRL Distribution Points - Check certificate expiration: report if expired, days until expiration, or days since expiration - Validate a certificate chain when multiple certificates are provided: verify that each certificate is signed by the next one in the chain - Support reading certificate bundles (multiple PEM certificates concatenated in one file) and parsing each individually - Support a --format flag to choose output format: text (default human-readable), json, or csv - Support fetching and parsing a remote server's certificate via --host flag (given a hostname and optional port) - Print the parsed certificate details to console in a structured, readable format - Save the output to a file via --output flag - If no input is given, generate a self-signed CA certificate and a leaf certificate signed by it, then parse both and demonstrate chain validation - Handle errors: invalid certificate data, unsupported formats, incomplete chains, and encoding issues Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include: - Source code - package.json with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
4 files, exactly as written, before any repair.
package-lock.json
{
"name": "x509-certificate-parser",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "x509-certificate-parser",
"version": "1.0.0",
"dependencies": {
"node-forge": "1.3.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/node-forge": {
"version": "1.3.1"
}
}
}
package.json
{
"name": "x509-certificate-parser",
"version": "1.0.0",
"private": true,
"type": "module",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"engines": {
"node": ">=20.0.0"
},
"dependencies": {
"node-forge": "1.3.1"
}
}
README.md
# X.509 Certificate Parser (JavaScript) Parses X.509 certificates (PEM/DER/bundles/remote host), extracts fields/extensions, checks expiration, and validates chain signatures. ## Requirements - Ubuntu 22.04 - Node.js 20+ (LTS) ## Dependencies - Direct: - `node-forge@1.3.1` - Transitive: - none Pinned in `package.json` and `package-lock.json`. ## Setup ```bash npm install ``` ## Run ```bash node src/index.js ./cert.pem node src/index.js ./bundle.pem --format json node src/index.js ./cert.der --format csv --output ./report.csv node src/index.js --host example.com:443 --format text node src/index.js ``` ## Features - PEM and DER auto-detection - PEM bundle parsing (multiple concatenated certs) - Full field extraction: - version, serial - issuer, subject - validity period - public key algorithm/size - signature algorithm - SHA-1/SHA-256 fingerprints - X.509 v3 extension extraction: - SAN - Key Usage - Extended Key Usage - Basic Constraints - SKID/AKID - CRL Distribution Points - Expiration status and day counts - Chain signature validation (`cert[i]` signed by `cert[i+1]`) - Remote fetch with `--host` - Output formats: `text`, `json`, `csv` - Output file support (`--output`) - No-args demo generates CA + leaf and validates chain
src/index.js
import crypto from "crypto";
import fs from "fs";
import path from "path";
import tls from "tls";
import forge from "node-forge";
function parseArgs(argv) {
const options = {};
const positional = [];
for (let i = 0; i < argv.length; i += 1) {
const t = argv[i];
if (t.startsWith("--")) {
const k = t.slice(2);
const n = argv[i + 1];
if (n && !n.startsWith("--")) {
options[k] = n;
i += 1;
} else options[k] = true;
} else positional.push(t);
}
return { options, positional };
}
function pemBlocks(text) {
const blocks = text.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
return blocks || [];
}
function bufferToForgeCert(buffer) {
const asn1 = forge.asn1.fromDer(forge.util.createBuffer(buffer.toString("binary")));
return forge.pki.certificateFromAsn1(asn1);
}
function certToDerBuffer(cert) {
const asn1 = forge.pki.certificateToAsn1(cert);
const der = forge.asn1.toDer(asn1).getBytes();
return Buffer.from(der, "binary");
}
function parseCertFile(filePath) {
const raw = fs.readFileSync(filePath);
const text = raw.toString("utf8");
if (text.includes("BEGIN CERTIFICATE")) {
const blocks = pemBlocks(text);
if (blocks.length === 0) throw new Error("No PEM certificate blocks found.");
return blocks.map((b) => forge.pki.certificateFromPem(b));
}
return [bufferToForgeCert(raw)];
}
function parseDistinguishedName(attrs) {
return attrs.map((a) => `${a.shortName || a.name}=${a.value}`).join(", ");
}
function pubKeyInfo(pub) {
if (pub?.n?.bitLength) return { algorithm: "RSA", size: pub.n.bitLength() };
if (pub?.curve?.length) return { algorithm: "EC", size: pub.curve.length };
return { algorithm: "Unknown", size: null };
}
function extensionValue(cert, name) {
return cert.extensions.find((e) => e.name === name || e.id === name) || null;
}
function parseExtensions(cert) {
const san = extensionValue(cert, "subjectAltName");
const ku = extensionValue(cert, "keyUsage");
const eku = extensionValue(cert, "extKeyUsage");
const bc = extensionValue(cert, "basicConstraints");
const skid = extensionValue(cert, "subjectKeyIdentifier");
const akid = extensionValue(cert, "authorityKeyIdentifier");
const crl = extensionValue(cert, "cRLDistributionPoints");
return {
subject_alt_names: (san?.altNames || []).map((x) => x.value || x.ip || x.dns || JSON.stringify(x)),
key_usage: ku ? Object.fromEntries(Object.entries(ku).filter(([k, v]) => typeof v === "boolean" && v)) : {},
extended_key_usage: eku ? Object.fromEntries(Object.entries(eku).filter(([k, v]) => typeof v === "boolean" && v)) : {},
basic_constraints: bc ? { ca: Boolean(bc.cA), path_length: bc.pathLenConstraint ?? null } : {},
subject_key_identifier: skid?.subjectKeyIdentifier || null,
authority_key_identifier: akid?.keyIdentifier || null,
crl_distribution_points: (crl?.altNames || []).map((x) => x.value || x.uri || JSON.stringify(x)),
};
}
function expirationStatus(notAfter) {
const now = Date.now();
const deltaDays = Math.floor((notAfter.getTime() - now) / (1000 * 60 * 60 * 24));
if (deltaDays >= 0) return { expired: false, days_until_expiration: deltaDays, days_since_expiration: 0 };
return { expired: true, days_until_expiration: 0, days_since_expiration: Math.abs(deltaDays) };
}
function analyzeCert(cert) {
const der = certToDerBuffer(cert);
const sha1 = crypto.createHash("sha1").update(der).digest("hex");
const sha256 = crypto.createHash("sha256").update(der).digest("hex");
const pk = pubKeyInfo(cert.publicKey);
const sigOid = cert.siginfo?.algorithmOid || cert.signatureOid || "";
const sigAlg = forge.pki.oids[sigOid] || sigOid || "unknown";
return {
version: cert.version + 1,
serial_number: cert.serialNumber,
issuer: parseDistinguishedName(cert.issuer.attributes),
subject: parseDistinguishedName(cert.subject.attributes),
validity: {
not_before: cert.validity.notBefore.toISOString(),
not_after: cert.validity.notAfter.toISOString(),
},
expiration: expirationStatus(cert.validity.notAfter),
public_key: pk,
signature_algorithm: sigAlg,
fingerprints: { sha1, sha256 },
extensions: parseExtensions(cert),
};
}
function validateChain(certs) {
const steps = [];
let ok = true;
for (let i = 0; i < certs.length - 1; i += 1) {
let valid = false;
try {
valid = certs[i + 1].verify(certs[i]);
} catch {
valid = false;
}
steps.push({
index: i,
child_subject: parseDistinguishedName(certs[i].subject.attributes),
issuer_subject: parseDistinguishedName(certs[i + 1].subject.attributes),
valid_signature: valid,
});
if (!valid) ok = false;
}
return { valid: ok, steps };
}
function renderText(result) {
const lines = [];
for (let i = 0; i < result.certificates.length; i += 1) {
const c = result.certificates[i];
lines.push(`Certificate #${i + 1}`);
lines.push(` Subject: ${c.subject}`);
lines.push(` Issuer : ${c.issuer}`);
lines.push(` Version: ${c.version}`);
lines.push(` Serial : ${c.serial_number}`);
lines.push(` Validity: ${c.validity.not_before} -> ${c.validity.not_after}`);
lines.push(` Signature Algorithm: ${c.signature_algorithm}`);
lines.push(` Public Key: ${c.public_key.algorithm} ${c.public_key.size || ""}`.trim());
lines.push(` Fingerprint SHA-1 : ${c.fingerprints.sha1}`);
lines.push(` Fingerprint SHA-256: ${c.fingerprints.sha256}`);
lines.push(` Expired: ${c.expiration.expired} | Days until: ${c.expiration.days_until_expiration} | Days since: ${c.expiration.days_since_expiration}`);
lines.push(` SANs: ${(c.extensions.subject_alt_names || []).join("; ")}`);
lines.push(` Key Usage: ${Object.keys(c.extensions.key_usage || {}).join(", ")}`);
lines.push(` Extended Key Usage: ${Object.keys(c.extensions.extended_key_usage || {}).join(", ")}`);
lines.push(` Basic Constraints: CA=${c.extensions.basic_constraints.ca ?? ""} pathLen=${c.extensions.basic_constraints.path_length ?? ""}`);
lines.push(` Subject Key ID: ${c.extensions.subject_key_identifier || ""}`);
lines.push(` Authority Key ID: ${c.extensions.authority_key_identifier || ""}`);
lines.push(` CRL Distribution Points: ${(c.extensions.crl_distribution_points || []).join("; ")}`);
lines.push("");
}
lines.push(`Chain validation: ${result.chain_validation.valid}`);
for (const step of result.chain_validation.steps) {
lines.push(` [${step.index}] ${step.valid_signature ? "OK" : "FAIL"} :: ${step.child_subject} <- ${step.issuer_subject}`);
}
return `${lines.join("\n")}\n`;
}
function renderCsv(result) {
const header = [
"index", "subject", "issuer", "serial_number", "not_before", "not_after", "expired",
"days_until_expiration", "days_since_expiration", "public_key_algorithm", "public_key_size",
"signature_algorithm", "fingerprint_sha1", "fingerprint_sha256",
];
const rows = [header.join(",")];
for (let i = 0; i < result.certificates.length; i += 1) {
const c = result.certificates[i];
const row = [
i + 1,
JSON.stringify(c.subject),
JSON.stringify(c.issuer),
c.serial_number,
c.validity.not_before,
c.validity.not_after,
c.expiration.expired,
c.expiration.days_until_expiration,
c.expiration.days_since_expiration,
c.public_key.algorithm,
c.public_key.size ?? "",
c.signature_algorithm,
c.fingerprints.sha1,
c.fingerprints.sha256,
];
rows.push(row.join(","));
}
return `${rows.join("\n")}\n`;
}
function outputResult(result, format) {
const fmt = (format || "text").toLowerCase();
if (fmt === "json") return `${JSON.stringify(result, null, 2)}\n`;
if (fmt === "csv") return renderCsv(result);
return renderText(result);
}
async function fetchHostCerts(host, port) {
return new Promise((resolve, reject) => {
const socket = tls.connect({
host,
port,
servername: host,
rejectUnauthorized: false,
}, () => {
try {
const rawChain = [];
const seen = new Set();
let cert = socket.getPeerCertificate(true);
while (cert && cert.raw && cert.raw.length > 0) {
const fp = cert.fingerprint256 || cert.fingerprint || `${cert.subject?.CN}:${cert.serialNumber}`;
if (seen.has(fp)) break;
seen.add(fp);
rawChain.push(cert.raw);
if (!cert.issuerCertificate || cert.issuerCertificate === cert) break;
cert = cert.issuerCertificate;
}
socket.end();
resolve(rawChain.map((b) => bufferToForgeCert(Buffer.from(b))));
} catch (e) {
reject(e);
}
});
socket.on("error", reject);
});
}
function generateDemoCerts() {
const pki = forge.pki;
const caKeys = pki.rsa.generateKeyPair(2048);
const ca = pki.createCertificate();
ca.publicKey = caKeys.publicKey;
ca.serialNumber = "1001";
ca.validity.notBefore = new Date(Date.now() - 60000);
ca.validity.notAfter = new Date(Date.now() + 365 * 24 * 3600 * 1000);
ca.setSubject([{ name: "commonName", value: "Demo Root CA" }, { name: "organizationName", value: "TMLR Demo" }]);
ca.setIssuer(ca.subject.attributes);
ca.setExtensions([
{ name: "basicConstraints", cA: true, pathLenConstraint: 1 },
{ name: "keyUsage", keyCertSign: true, cRLSign: true },
{ name: "subjectKeyIdentifier" },
]);
ca.sign(caKeys.privateKey, forge.md.sha256.create());
const leafKeys = pki.rsa.generateKeyPair(2048);
const leaf = pki.createCertificate();
leaf.publicKey = leafKeys.publicKey;
leaf.serialNumber = "1002";
leaf.validity.notBefore = new Date(Date.now() - 60000);
leaf.validity.notAfter = new Date(Date.now() + 180 * 24 * 3600 * 1000);
leaf.setSubject([{ name: "commonName", value: "demo.local" }, { name: "organizationName", value: "TMLR Demo" }]);
leaf.setIssuer(ca.subject.attributes);
leaf.setExtensions([
{ name: "basicConstraints", cA: false },
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
{ name: "extKeyUsage", serverAuth: true, clientAuth: true },
{ name: "subjectAltName", altNames: [{ type: 2, value: "demo.local" }, { type: 2, value: "www.demo.local" }] },
{ name: "authorityKeyIdentifier", keyIdentifier: true, authorityCertIssuer: true, serialNumber: ca.serialNumber },
{ name: "subjectKeyIdentifier" },
{ name: "cRLDistributionPoints", altNames: [{ type: 6, value: "http://example.com/demo.crl" }] },
]);
leaf.sign(caKeys.privateKey, forge.md.sha256.create());
return [leaf, ca];
}
async function main() {
const { options, positional } = parseArgs(process.argv.slice(2));
let certs;
if (options.host) {
const [hostOnly, portRaw] = String(options.host).split(":");
const port = Number.parseInt(options.port || portRaw || "443", 10);
certs = await fetchHostCerts(hostOnly, port);
} else if (positional.length > 0) {
certs = parseCertFile(path.resolve(positional[0]));
} else {
certs = generateDemoCerts();
}
const result = {
source: options.host ? `host:${options.host}` : (positional[0] || "demo-generated"),
certificate_count: certs.length,
certificates: certs.map(analyzeCert),
chain_validation: validateChain(certs),
};
const rendered = outputResult(result, options.format || "text");
if (options.output) {
const outPath = path.resolve(options.output);
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, rendered, "utf8");
}
process.stdout.write(rendered);
}
main().catch((e) => {
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
process.exit(1);
});