From guidewire-pack
Hardens Guidewire Cloud API integrations for SOC 2 audits: least-privilege RBAC, encrypted secrets via SOPS+age, PII redaction, audit-trail capture, and token leak response.
How this skill is triggered — by the user, by Claude, or both
Slash command
/guidewire-pack:guidewire-security-and-rbacThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
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...
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 exposure2plugins reuse this skill
First indexed Jul 18, 2026
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin guidewire-packAuthenticates Guidewire Cloud API integrations with OAuth2 token caching, secret rotation, and PKIX trust store hardening. Use when handling token expiry, scope drift, or cert errors.
Implements enterprise security patterns for Claude Code: role-based permission hardening, secrets blocking scripts, Vault integration, audit hooks for SOC2, HIPAA, GDPR compliance.
Audits application and infrastructure access controls: RBAC/ABAC models, MFA, user lifecycle, service accounts, least privilege, break-glass. Maps to SOC 2, HIPAA, PCI-DSS. Use during quarterly reviews or before audits.