Install
$ agentstack add skill-kyleoliveiro-sg-gov-skills-secure-coding-as Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Secure coding for SG Government: AS + CK controls
The ICT&SS Policy Reform (IM8's successor) Cybersecurity Control Catalog contains 15 Application Security (AS) controls and 4 Cryptography/Key Management (CK) controls that land directly in application code. This skill maps them to coding decisions so an agent writing or reviewing code produces compliant output the first time, with control IDs the agency's security team can trace to its System Security Plan (SSP).
Use this skill in three modes:
- Writing new code — apply the scenario sections below as you write.
- Reviewing a diff — run the review checklist against every changed file; flag
violations with the control ID.
- Auditing a codebase — sweep for each checklist row; report findings per control ID
with file:line references.
Source and currency
Control text in this skill and in [references/as-controls.md](references/as-controls.md) is embedded from info.standards.tech.gov.sg as of 2026-07-16. The standards iterate actively. For compliance-critical decisions (SSP evidence, audit responses, deviations), verify against the live pages:
- AS family: https://info.standards.tech.gov.sg/control-catalog/cybersecurity/as/
- CK family: https://info.standards.tech.gov.sg/control-catalog/cybersecurity/ck/
- SSP profiles (control levels): https://info.standards.tech.gov.sg/ssp/
Machine-readable OSCAL: https://github.com/GovtechSG/tech-standards — useful for tooling but lags the site; the site is authoritative.
Before you start: levels and parameters
- Levels come from the project's SSP, not the catalog. Level 0 = mandatory, no
deviation. Level 1 = default; deviations need documented IDSC/CIO/CISO approval. Level 2 = risk-based best practice. In the Medium-Risk Cloud SSP, AS-1, AS-3, AS-7 and AS-8 are Level 0 (promoted from Level 1 in low-risk); AS-10, AS-12, AS-13, AS-14 sit at Level 2 in both cloud SSPs; CK-1/CK-2 are Level 1 in medium-risk. Use the sibling skill ssp-navigator to determine the applicable SSP and each control's level for this system — treat every control below as required unless the SSP says otherwise.
- Parameters are agency-defined.
as-5_prm_1/2/3(password length, policy, type),
as-11_prm_1/2 (session hours, session type), ck-2_prm_1 (key rotation days) have no catalog values. Never invent them. Ask the user for the project's SSP parameter values, or locate them in the repo (SSP document, security config, compliance docs). If unavailable, implement against a named configuration constant, flag it as TODO(SSP): confirm as-5_prm_1, and say so in your report.
Full statement/recommendation/risk text for every control: [references/as-controls.md](references/as-controls.md). The sections below are the working summary.
Handling input and output
AS-1 — Input Validation. Validate all application inputs against expected type, structure, or format. Why: unvalidated input invites injection, data manipulation, and crashes that can lead to unauthorised access or service disruption.
- Validate at the trust boundary with a schema library (Zod/Joi in Node, Pydantic in
Python, Bean Validation in Java), not ad-hoc if checks scattered through handlers.
- Cover every input channel: body, query string, path params, headers, cookies, file
metadata, webhook payloads, message-queue consumers.
- Allowlist (expected shape) over denylist (known-bad patterns). Reject, don't repair.
- Level 0 in medium-risk cloud — never skip, never partially apply.
AS-2 — Parameterised Interfaces. Use parameterised interfaces for database queries and system commands. Why: string-built queries are the direct path to SQL/command injection and system compromise.
- Prepared statements, query builders, or ORMs only. Any string concatenation or
template interpolation into SQL, shell commands, LDAP filters, or NoSQL queries is a finding — including "safe-looking" cases like dynamic column names (allowlist those).
- For OS commands, use exec-style APIs with argument arrays (
execFile,subprocess.run
with a list, ProcessBuilder), never shell=True / backtick interpolation.
AS-3 — Output Sanitisation. Sanitise all application outputs used to render an HTML document. Why: unsanitised output is XSS — malicious code executing in users' browsers.
- Rely on auto-escaping template engines/frameworks (React JSX, Jinja2 autoescape,
Thymeleaf). Treat every escape hatch — dangerouslySetInnerHTML, | safe, v-html, innerHTML — as a finding unless the value passes an HTML sanitiser (e.g. DOMPurify) with a tight allowlist.
- Applies to API responses that clients render into HTML, error pages, and emails too.
- Level 0 in medium-risk cloud. Pairs with the AS-9 CSP header as defence in depth.
Authentication, passwords, and sessions
AS-4 — Authentication Mechanism Rate-Limiting. Rate-limit all authentication mechanisms. Why: without it, weak credentials fall to brute force.
- Site recommendation: max 3 consecutive failed attempts within 15 minutes (or other
reasonable limit); add time delays between attempts; consider CAPTCHA/bot mitigation.
- "All mechanisms" means login, registration, password reset, OTP/MFA verification, API
token endpoints — not just the main login form. Registration is the one teams forget: an unthrottled signup endpoint enables mass account creation and email/SMS flooding, and it exercises the same password and notification paths as the rest of auth. Key limits on account and source IP.
AS-5 — Password Requirements. Where SSO or passwordless is not supported, enforce the SSP-defined minimum length (as-5_prm_1), policy (as-5_prm_2), and password type (as-5_prm_3). Why: short or common passwords are the cheapest account takeover.
- Prefer SSO (for agency staff, typically the WOG identity provider; for public users,
Singpass/Corppass — see AC-7) or passwordless before building password auth at all.
- Per NIST SP 800-63B (which the control cites): length is the primary strength factor;
composition rules add little. Implement a length minimum plus breached/common-password screening rather than character-class rules, unless as-5_prm_2 says otherwise.
- Do not invent parameter values — get them from the SSP (see "Before you start").
AS-6 — Password Salting and Hashing. Store passwords as salted hashes using an offline-attack-resistant scheme per NIST SP 800-63B; salts must be (a) generated by a cryptographically secure PRNG, (b) at least 32 bits, (c) unique per account. Why: on breach, unsalted or weakly hashed passwords are trivially extracted.
- Use Argon2, scrypt, bcrypt, or PBKDF2 via a maintained library. These generate
compliant per-account salts internally — do not hand-roll salting, and never use general-purpose hashes (MD5/SHA-x) for passwords.
- CSPRNG examples from the control itself:
crypto.randomBytesnotMath.random
(Node.js); java.security.SecureRandom.nextBytes not java.util.Random (Java). Python: secrets, not random.
AS-7 — Access Control Check Enforcement. Perform access control checks on all authenticated requests. Why: authenticated-but-unauthorised access is how data breaches and IDOR happen.
- Enforce via middleware/filters/route guards so checks are structural, not per-handler
discipline: deny by default, every route declares its authorisation requirement.
- Check object-level ownership on every request (
WHERE owner_id = :current_user), not
just role at login. Re-check on state-changing requests; never trust hidden fields or client-supplied IDs as authorisation.
- Level 0 in medium-risk cloud. In review, any new authenticated route without an
explicit authorisation check is a finding.
AS-11 — Session Management. Require the SSP-defined session type (as-11_prm_2) to re-authenticate after the session exceeds as-11_prm_1 hour(s), or terminate the session. Why: stale sessions let whoever holds them act as the user indefinitely.
- Enforce absolute session lifetime server-side (issue time + max age), not only idle
timeout, and not only cookie expiry — the server must reject expired sessions.
- NIST SP 800-63B reference points from the control: AAL1 re-auth every 30 days; AAL2
12 h or 30 min inactivity; AAL3 12 h or 15 min inactivity. Also consider forcing re-authentication on role/credential changes and before privileged functions.
- For JWTs: short-lived access tokens plus revocable refresh tokens whose absolute
lifetime respects as-11_prm_1. Get the parameter from the SSP.
AS-15 — Password Change. Force a password change on suspected account compromise. Why: without it, an attacker with a stolen credential keeps access indefinitely.
- Build the mechanism: a
must_change_password/ credential-invalidation flag that
blocks all authenticated activity except the change flow, plus session revocation.
- Wire detection signals to it: unusual login activity, repeated failures (from AS-4),
breach-list hits. Notify the user promptly; consider MFA as a compensating layer.
Secrets and cryptography
AS-8 — Secrets Management. Store secrets in a proper secrets management solution with access control, encryption, and monitoring. Why: hard-coded or plaintext secrets in code or config are standing credentials for anyone who reads the repo.
- Secrets = API keys, access keys, DB credentials, signing keys, any static credential.
Never unencrypted in source code, config files, or committed .env files.
- Use cloud-native (AWS Secrets Manager, Azure Key Vault) or cloud-agnostic (HashiCorp
Vault, CyberArk Conjur) stores; inject at runtime. On platforms, use the platform's secret facility — e.g. GitHub Actions secrets, not variables.
- Level 0 in medium-risk cloud. Push protection and secret scanning in the pipeline are
SD-1/SD-6 — see the secure-pipeline skill; this control is about where the app gets secrets at runtime.
AS-14 — Secure Cryptographic Libraries. Use reputable, secure cryptographic libraries and functions for all cryptographic operations. Why: home-grown or obsolete crypto quietly fails, exposing data.
- Never implement primitives yourself. Use the platform's vetted library (OpenSSL,
libsodium, language-standard crypto modules); prefer FIPS 140-2/140-3 validated modules where assurance matters. Keep libraries updated.
- Follow the OWASP Cryptographic Storage Cheat Sheet for algorithm/mode choices; flag
legacy algorithms (MD5, SHA-1 for signatures, DES/3DES, ECB mode) in review.
CK-1 — Cryptographic Key Establishment. Use industry-standard key establishment and key derivation (NIST SP 800-56A/B/C). Why: weak establishment = weak or broken encryption. In code: use your KMS or library's standard ECDH/RSA-KEM + HKDF flows; never derive keys from passwords without a proper KDF.
CK-2 — Cryptographic Key Rotation. Rotate keys every ck-2_prm_1 days (SSP-defined — do not invent). Why: long-lived keys raise breakage risk. Enable automatic rotation in KMS (AWS KMS, Azure Key Vault) and design code to be key-version-aware (encrypt with current, decrypt with any active version).
CK-3 — Cryptographic Key Management. Manage keys across their full lifecycle (generation → storage → distribution → use → rotation → backup → revocation → destruction) per NIST SP 800-57 / ISO/IEC 27017. Why: poorly managed keys mean unauthorised access and compromised crypto. Mostly process, but code must not undermine it: no keys in code, logs, or error messages; support revocation without redeploy.
CK-4 — Cryptographic Key Storage. Store keys securely with least-privilege access control. Why: readable keys = readable data. Keep keys in KMS/HSM-backed storage; scope IAM so the app can use keys (encrypt/decrypt/sign) but not export them.
HTTP security headers
AS-9 — Content Security Policy. Set minimally permissive CSP response headers. Why: CSP is the browser-side backstop against XSS when AS-3 sanitisation slips.
- Set explicit fetch directives —
default-src,script-src,style-src,
connect-src, img-src, media-src, object-src — starting from default-src 'self'; object-src 'none' and loosening only with justification.
- "Minimally permissive" rules out
script-src 'unsafe-inline'/'unsafe-eval'and
bare wildcards; use nonces or hashes for inline scripts. Reference: OWASP Secure Headers Project Best Practices.
AS-10 — HSTS. Set Strict-Transport-Security with max-age ≥ 31536000 (1 year). Why: shorter or missing HSTS leaves users open to protocol downgrade attacks.
Strict-Transport-Security: max-age=31536000; includeSubDomains(addpreloadwhen
the domain is ready). The ≥ 31536000 floor is in the control statement itself — flag any lower value in review.
- Set both headers at one layer (framework middleware such as helmet, or the CDN/reverse
proxy) and verify they actually reach the client on every response, including errors.
File uploads
AS-12 — Malware Scanning of Uploaded Files. Scan uploads for malware before the system processes them further. Why: unscanned files infect downstream consumers.
- Pattern from the control: upload to temporary/quarantine storage → scan on ephemeral
compute (e.g. serverless + ClamAV or a cloud malware-scanning service) → move clean files to processing storage, unsafe files to quarantine. Nothing reads the file before the verdict.
- In review: any handler that parses, thumbnails, virus-scans-later, or serves an upload
before scanning is a finding. Combine with AS-1 validation of file type/size/name.
Error handling
AS-13 — Exposure of Internal System Details. Never disclose internal system details to public users. Why: stack traces, debug info, and version strings hand attackers a map of what to exploit.
- Production error responses: generic message + correlation ID; full detail goes to
server-side logs only (and sanitise logs of classified/sensitive data — LM-19).
- Disable framework debug modes in production (
DEBUG=False, no dev error pages);
strip Server/X-Powered-By headers; keep API error bodies free of SQL fragments, file paths, and dependency versions.
- Errors must stay useful: contextually appropriate messages that help the user act
without revealing internals.
Review checklist
For diff review, check each row against every changed file; for audits, sweep the codebase per row. Cite findings as [AS-n]/[CK-n] with file:line.
| ID | Check | Look for | |---|---|---| | AS-1 | All inputs schema-validated at trust boundary | new/changed endpoints, parsers, queue consumers without validation | | AS-2 | Queries/commands parameterised | string-built SQL/shell/NoSQL, shell=True, template interpolation into queries | | AS-3 | HTML-bound output escaped/sanitised | dangerouslySetInnerHTML, innerHTML, \| safe, disabled autoescape | | AS-4 | Auth endpoints rate-limited | signup/login/reset/OTP/token routes without limiter or lockout/delay | | AS-5 | Password rules = SSP params (as-5_prm_1/2/3) | hard-coded lengths that don't match SSP; missing common-password screen | | AS-6 | Salted hash, memory-hard KDF, CSPRNG, ≥32-bit unique salt | MD5/SHA-x passwords, Math.random/java.util.Random, shared or absent salt | | AS-7 | AuthZ check on every authenticated request | new routes outside guard middleware; missing object-ownership checks (IDOR) | | AS-8 | Secrets from a secrets manager, never in code/config | hard-coded keys, committed .env, plaintext config credentials, CI variables holding secrets | | AS-9 | CSP present and minimally permissive | missing CSP; unsafe-inline/unsafe-eval/* in script-src | | AS-10 | HSTS max-age ≥ 31536000 | missing header; max-age below one year | | AS-11 | Server-side absolute session timeout = as-11_prm_1 h | cookie-only expiry, non-expiring JWTs, no re-auth on privilege change | | AS-12 | Uploa
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kyleoliveiro
- Source: kyleoliveiro/sg-gov-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.