From guidewire-pack
Lock down a Guidewire Cloud API integration so it survives a SOC 2 audit, an NAIC Model Audit Rule review, and a real-world incident — least-privilege role design, encrypted committed secrets via SOPS+age, PII redaction in logs (SSN/DOB/claim narrative), audit-trail capture, cross-tenant isolation for multi-carrier integrations, and detect-and-rotate response to token leaks. Use when designing the security posture for a new integration, hardening an existing one before audit, or responding to a leaked credential. Trigger with "guidewire security", "guidewire rbac", "guidewire pii redaction", "guidewire audit trail", "guidewire secret leak".
npx claudepluginhub flight505/skill-forge --plugin guidewire-packThis skill is limited to using the following tools:
Build the security posture an integration needs in production: secrets that cannot leak from the repo, roles that cannot escalate beyond their job, logs that cannot exfiltrate PII, and an audit trail that satisfies SOC 2 and NAIC Model Audit Rule reviewers. This skill is the application of generic security practice to the specific shape of Guidewire Cloud API — claim and policy data carry regul...
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
Build the security posture an integration needs in production: secrets that cannot leak from the repo, roles that cannot escalate beyond their job, logs that cannot exfiltrate PII, and an audit trail that satisfies SOC 2 and NAIC Model Audit Rule reviewers. This skill is the application of generic security practice to the specific shape of Guidewire Cloud API — claim and policy data carry regulated PII; a leaked client_secret can read or write a carrier's entire book of business; carriers operate under state-level insurance regulator scrutiny.
Five real-world failures this skill prevents:
.env in the repo — a git push to a public mirror leaks live credentials; the only defense is to never have plaintext in the tree, full stop.pc.account.write "to make development easier" and is never narrowed; the audit finding cites OWASP A01 broken access control.console.log(claim) dumps SSN, DOB, claim narrative, and phone numbers into the logging pipeline, where they replicate to every downstream tool the company uses.guidewire-install-auth and guidewire-sdk-patternssops and age installed locally (brew install sops age / apt install sops / mise install age)Build the posture in this order. Each layer addresses one of the five failures listed in Overview.
Plaintext .env files committed to a repo are the single largest source of credential leaks in the SaaS world. SOPS + age allows secrets to live in the repo as ciphertext that only holders of the age private key can read. The audit trail of "who rotated what when" comes free with git log.
sops-init # writes .sops.yaml + .env.sops + scripts/sops-env (idempotent)
sops secrets.prod.sops.yaml # interactive edit; saves re-encrypted
git add secrets.prod.sops.yaml && git commit -m "chore(secrets): rotate gw client secret"
In the runtime, decrypt via the anchored regex pattern (do not use the naive sed 's/^/export /' — see guidewire-install-auth for the bare-export-leak failure mode):
eval "$(sops -d secrets.prod.sops.yaml | sed -nE 's/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/export \1=\2/p')"
Per-environment recipient lists in .sops.yaml mean dev keys cannot decrypt prod secrets — limit blast radius of a compromised dev workstation.
In GCC > Identity & Access > Applications > [your-app] > Permissions, assign only the roles the integration actually needs. A read-only reporting integration should hold pc.account.read and pc.policy.read only; a webhook event consumer needs zero write roles.
Reporting integration: pc.account.read, pc.policy.read, cc.claim.read
Broker portal (read+quote): pc.account.read, pc.account.write, pc.submission.write
Renewal job: pc.policy.read, pc.policy.write
Claims FNOL intake: cc.claim.write, cc.contact.write
Webhook event consumer: (no Cloud API roles — just receives App Events)
Validate the issued token carries only the expected scopes on every refresh (guidewire-install-auth's scope-drift gate). A token with extra scopes is a configuration error; alert and treat as an incident.
Guidewire claim and contact resources carry regulated PII: SSN, date of birth, driver's license number, claim narrative, phone, email, address. Redact at the logging boundary, not in the calling code (which always misses cases). Apply the redactor to every structured log entry the integration emits.
const PII_PATHS = [
"ssn", "taxId", "dateOfBirth", "driversLicenseNumber",
"primaryPhone.phoneNumber", "workPhone.phoneNumber", "primaryEmail",
"addressLine1", "addressLine2", "description", // claim narrative
];
export function redactPii<T extends object>(record: T): T {
const clone = JSON.parse(JSON.stringify(record));
for (const path of PII_PATHS) {
setByPath(clone, path, redactValue(getByPath(clone, path)));
}
return clone;
}
function redactValue(v: unknown): string | unknown {
if (typeof v !== "string" || !v) return v;
if (/^\d{3}-?\d{2}-?\d{4}$/.test(v)) return "***-**-****"; // SSN
if (/^\d{4}-\d{2}-\d{2}/.test(v)) return v.slice(0, 4) + "-**-**"; // DOB → year only
if (v.length <= 4) return "***";
return v.slice(0, 2) + "***" + v.slice(-2);
}
Wire the redactor into the logger transport (Pino redact, Winston format, OpenTelemetry log processor) so every log entry passes through it before serialization. Per-call console.log(redactPii(claim)) works in theory and fails in practice — engineers forget.
Every state-mutating Cloud API call should leave a row in an internal audit table the integration owns. Cloud API has its own audit, but it cannot tell investigators which logical operation drove a request, only that the Service Application made the call.
CREATE TABLE integration_audit (
id UUID PRIMARY KEY,
correlation_id UUID NOT NULL,
actor TEXT NOT NULL, -- the upstream user, broker, or job triggering the call
service_app TEXT NOT NULL, -- the GCC Service Application id
api_method TEXT NOT NULL, -- POST / PATCH / DELETE
api_path TEXT NOT NULL, -- /pc/rest/v1/policies/pc:8001
resource_id TEXT, -- post-response id
idempotency_key UUID NOT NULL,
status_code INT NOT NULL,
reason TEXT, -- "renewal-window-open", "broker-quote-flow", etc.
at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Each row pairs to one Cloud API call. SOC 2 reviewers ask "show me every write this integration made on behalf of broker acme-insurance in March"; this table makes the answer a SQL query, not a forensic exercise.
If the integration serves more than one carrier, every carrier gets:
client_id/client_secret)secrets.[carrier-slug].sops.yaml encrypted to a tenant-specific recipientSharing one Service Application across tenants creates legal and contractual exposure beyond the technical risk; a single compromised credential reveals every tenant's data, and an audit finding will cite SOC 2 CC6.1 (logical access controls).
When a secret is suspected leaked (a developer pasted it into a Slack message, a CI log captured it, a public commit briefly contained it):
# 1. Rotate immediately in GCC — generates a new client_secret
# 2. Update the encrypted secret file
sops secrets.prod.sops.yaml # set GW_CLIENT_SECRET to the new value
git commit -m "rotate(secrets): GW client secret — leak suspected $(date -Iseconds)"
# 3. Deploy to all environments
# 4. Use dual-secret window (per guidewire-install-auth) so in-flight requests do not fail
# 5. Audit Cloud API access logs for unauthorized calls during the leak window
# 6. Document the incident in the audit table per step 4
Do not "wait until business hours" — credential leaks compound by the minute. The rotation is reversible; the data exfiltration is not.
A production-grade security posture ships with all of the following:
secrets.*.sops.yaml files committed to git, encrypted to per-environment age recipients; no plaintext .env files anywhere in the tree.redactPii() to every structured log entry before serialization.integration_audit table populated synchronously with every state-mutating Cloud API call..sops.yaml recipient layeringcreation_rules:
- path_regex: secrets\.dev\.sops\.yaml$
age: age1devkey...,age1leadkey...
- path_regex: secrets\.uat\.sops\.yaml$
age: age1uatkey...,age1leadkey...
- path_regex: secrets\.prod\.sops\.yaml$
age: age1prodkey...,age1leadkey...
A developer's age key decrypts dev only; the lead's key decrypts every environment for incident response. Rotation of an environment's age recipient is sops updatekeys secrets.<env>.sops.yaml.
import pino from "pino";
const log = pino({
redact: {
paths: [
"*.ssn", "*.taxId", "*.dateOfBirth",
"*.primaryPhone.phoneNumber", "*.primaryEmail",
"*.description",
],
censor: "[REDACTED]",
},
});
log.info({ claim: claimResource }, "claim received");
await db.insert("integration_audit", {
id: crypto.randomUUID(),
correlation_id: ctx.correlationId,
actor: ctx.user.id,
service_app: process.env.GW_CLIENT_ID,
api_method: "POST",
api_path: "/pc/rest/v1/policies/pc:8001/endorse",
resource_id: response.data.id,
idempotency_key: idempotencyKey,
status_code: 200,
reason: "broker-portal-mid-term-coverage-add",
});
| Symptom | Cause | Solution |
|---|---|---|
403 Forbidden despite valid token | scope drift — GCC admin removed a role | scope-drift gate alerts on every refresh; not a transport failure, surface as incident |
Plaintext .env discovered in git history | committed before SOPS adoption | rotate every credential in the leaked file immediately; rewrite history with git filter-repo if the leak is recent |
| Logger emits SSN to Splunk | redactPii not wired into the transport | add at the logger config, not at call sites; remove the per-call redactPii() in favor of transport-level |
| SOPS commit accidentally encrypted to wrong recipient | .sops.yaml regex did not match | run sops updatekeys on the affected file; recipients are visible in the YAML metadata |
Bare export in cron mail leaks every env var | naive sed 's/^/export /' instead of anchored regex | switch to sed -nE 's/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/export \1=\2/p' |
| Audit query "what did integration do for broker X last month" returns nothing | audit table not populated | wire integration_audit insert into every write helper; backfill is impossible |
| One client_secret used across two carriers | shared Service Application | split immediately; rotate both halves to scope the blast radius |
| Token in a CI log | secret printed accidentally during deployment | rotate via the detect-and-rotate runbook; audit access during the leak window |
For deeper coverage (Vault Agent integration, dynamic secrets, token-binding, federated identity, FIPS-140 trust stores), see implementation guide and API reference.
guidewire-install-auth — the auth layer this hardens; scope-drift gate, dual-secret rotationguidewire-sdk-patterns — error mapping that surfaces 403 as a structured exception this skill alerts onguidewire-observability-and-incident-response — emits the alerts this skill's audit table powersguidewire-ci-cd-pipeline — promotes encrypted secrets through environments without plaintext exposure