Skip to main content

Security Testing Policy

Security Testing Policy

Organization: {{ORG_NAME}}Bilko — Balkan Accounting SaaS Policy Number: POL-SEC-TEST-{{NUMBER}}001 Version: {{VERSION}}1.0 Date: {{DATE}}2026-02-23 Author: {{AUTHOR}}Compliance Architect Status: Draft | In Review | Approved Reviewers: {{REVIEWERS}}CTO, Engineering Lead Next Review: {{REVIEW_DATE}}2026-08-23 Classification: Confidential

Document History

Version Date Author Changes
0.1 {{DATE}}2026-02-23 {{AUTHOR}}Compliance Architect Initial draft — Bilko security testing policy

1. Purpose & Scope

Purpose: This policy defines the security testing methodology, tools, frequency, and remediation requirements for all systems operated by {{ORG_NAME}}.Bilko. Security testing is mandatory and non-negotiable — no system goes to production without completing applicable security tests.

Scope:

  • All production applicationsapplications: andbilko.io APIs(Vercel), api.bilko.io (Railway)
  • AllPostgreSQL infrastructuredatabase (cloud,Railway on-premises,EU containers)West)
  • AllCloudflare internalR2 toolsfile with access to production datastorage
  • All CI/CD pipelines and build systems
  • All third-Third-party integrationsintegrations: whereSEF {{ORG_NAME}}(Serbia), controlsHR-FISK/FINA the integration code(Croatia)

Policy Owner: {{CISO_NAME}}Compliance Architect ({{CISO_EMAIL}})[email protected])

OperationalRegulatory Owner:basis:

Security
    team

  • GDPR Art. 32 — "regular testing, assessing and evaluating the effectiveness of technical and organisational measures"
  • ZZPL Art. 50 (Serbia) — security testing obligation
  • Zakon o računovodstvu — integrity of financial processing systems

2. Security Testing Methodology

Approach: Shift-left security — testing integrated throughout development, not bolted on at the end.

Testing layers:

Development Phase
    ├── SAST — Static code analysis (every commit)
    ├── SCA — Dependency vulnerability scan (every commit)
    └── IaCSecret Security ScanscanningInfrastructure-as-codeDetect reviewleaked credentials (every commit)

Build / CI Phase
    ├── SAST — Full codebase scan (every PR)
    ├── SCA — LicenseFull +dependency vulnerability checkaudit (every PR)
    ├── Container scanning — Image vulnerability analysis (every build)
    └── Secret scanning — DetectFull leakedrepo credentialsscan (every commit)PR)

Deployment Phase
    ├── DAST — DynamicOWASP ZAP scan against staging (every deployment)
    └── API securitysecurity: scanOrg-scope isolation Endpoint fuzzingtests (every deployment)

Operational Phase
    ├── IAST — Runtime analysis in staging (continuous)
    ├── Vulnerability assessment — External attack surface (quarterly)
    ── Penetration test — Manual expert testing (annual)annual, └──Phase Red team exercise (annual for critical systems)2+)

3. Testing Types, Tools & Schedule

3.1 SAST — Static Application Security Testing

Purpose: Detect security vulnerabilities in TypeScript/JavaScript source code at development time.code.

Property Value
Tool(s)Tool {{SAST_TOOL}}ESLint (e.g.,with Semgrep,eslint-plugin-security SonarQube,+ CodeQL,TypeScript Snykstrict Code)mode
Frequency Every commit (incremental)pre-commit hook) + every PR (full scan)CI)
Languages covered {{LANGUAGE_LIST}}TypeScript (frontend Next.js + backend Express)
Integration Git pre-Pre-commit hook (incremental) + CIGitHub pipeline (full)Actions
Blocking YES — PR cannot merge if Critical or High findings unresolved
Results location{{DASHBOARD_URL}}

SAST rules in scope:

  • OWASP Top 10 (A01-A10)
  • SQL injection,injection command injection, LDAP injectionpatterns
  • XSS patterns (reflected,dangerouslySetInnerHTML stored,without DOM-based)sanitization)
  • PathHardcoded traversalsecrets / localAPI file inclusionkeys
  • Insecure deserializationJWT usage (no algorithm verification, expired tokens not rejected)
  • HardcodedRaw credentialsSQL /in secretsPrisma ($queryRaw with user input)
  • InsecureMissing cryptographyorg-scope usageWHERE clauses
  • AuthenticationNo-eval, andno-unsafe-regex session management flaws
  • Insecure direct object referencesrules

Configuration:ESLint configuration:

# .sast-config.yml
severity_threshold: medium  # Block on medium and above
exclude_paths:
  -{
  "**/__tests__/**extends": ["
  -plugin:security/recommended"],
  "**/node_modules/**"rules": -{
    "**/*.test.ts"security/detect-object-injection": custom_rules:"warn",
    -"security/detect-non-literal-regexp": {{CUSTOM_RULE_FILE}"error",
    "security/detect-possible-timing-attacks": "error",
    "no-eval": "error",
    "@typescript-eslint/no-explicit-any": "error"
  }
}

False positive handling: Developers may suppress findings with an inline comment — requires reviewer approval and explanation. All suppressions are audited quarterly.


3.2 DAST — Dynamic Application Security Testing

Purpose: Detect runtime vulnerabilities by sending attack traffic to a running instance.

Property Value
Tool(s)Tool {{DAST_TOOL}}OWASP ZAP (e.g.,Zed OWASPAttack ZAP, Burp Suite Enterprise, Nuclei)Proxy)
Frequency Every deployment to staging + weekly full scan of staging
Target https://staging.{{DOMAIN}}bilko.io — NEVER production without explicitwritten approval
Integration Post-deployment CI step
Blocking YES — deployment halted if Critical finding discovered
Results location{{DASHBOARD_URL}}

DAST scan scope:scope for Bilko:

  • All API endpoints (authenticated + unauthenticated)
  • Authentication flowsflows: (login, registration,register, password reset)reset, 2FA
  • Invoice CRUD endpoints (create, update, delete)
  • Expense endpoints
  • VAT report generation
  • File upload (receipt attachments)
  • SEF/HR-FISK submission endpoints
  • SearchOrg-scope and filter parameters
  • Error handlingisolation (stackcross-tenant traceaccess disclosure)attempts)
  • Security headers presence and correctness
  • SSL/TLS configuration

Critical Bilko-specific DAST authentication setup:tests:

#1. dast-auth.ymlCross-tenant loginUrl:IDOR https:test:
   - Login as org A user
   - Attempt GET /api/v1/invoices/{invoice-id-from-org-B}
   - Expected: 403 Forbidden
   - If 200: CRITICAL finding — immediate halt

2. RBAC bypass test:
   - Login as accountant role
   - Attempt POST /staging.api/v1/invoices (create)
   - Expected: 403 Forbidden
   - Attempt DELETE /api/v1/invoices/{{DOMAIN}}/auth/loginid}
   loginRequestData:- email:Expected: "{{DAST_TEST_USER_EMAIL}}"403 password:Forbidden

"{{DAST_TEST_USER_PASSWORD}}"3. indicateSuccessPattern:SQL "access_token"injection scanAsAuthenticatedUser:via truePrisma:
   - Attempt org-scope bypass via invoice filter parameters
   - Expected: Prisma parameterization prevents injection

4. Tax ID in response check:
   - Verify tax IDs returned as masked/encrypted, not plaintext
   - Verify tax IDs not present in error responses or logs

Production DAST: Requires written approval from CISO + Engineering Lead. Scoped to non-destructive read-only tests only. Scheduled during off-peak hours.


3.3 SCA — Software Composition Analysis

Purpose: Identify known vulnerabilities (CVEs) in open source dependencies. Verify license compliance.

Property Value
Tool(s)Tool Snyknpm /audit + Dependabot / OWASP Dependency-Check(GitHub)
Frequency Every commit (Dependabot) + weekly full scannpm audit
Blocking YES — PR cannot merge with Critical CVE (unresolved)
License check YES — copyleft licenses (GPL, AGPL) require legal review
Results location{{DASHBOARD_URL}}

Dependency update policy:

  • Security patches:patches (Critical/High CVE): Merge within 24h / 7 days per remediation SLA (see §4)
  • Minor updates: Merge within 14 days
  • Major updates: Planned migration within 90 days

Approved license list:licenses: MIT, Apache 2.0, BSD (2-clause, 3-clause), ISC,ISC MPL 2.0


Current Bilko key dependencies to monitor:

prisma (ORM — SQL injection prevention)
jsonwebtoken / jose (JWT — auth bypass if vulnerable)
bcrypt / bcryptjs (password hashing — compromise risk)
zod (input validation — bypass risk)
express (web framework — RCE risk)

3.4 IAST — Interactive ApplicationVitest Security TestingTests

Purpose: RuntimeBilko-specific automated security analysisregression duringtests integration/acceptancefor testing.accounting Combines benefits of SAST and DAST.isolation.

Property Value
Tool(s)Tool {{IAST_TOOL}}Vitest (e.g.,unit Contrast+ Security,integration) Seeker,+ HCLPlaywright AppScan)(E2E)
EnvironmentFrequency StagingEvery onlycommit — never production(CI)
FrequencyContinuous during staging test suite execution
IntegrationTest runner + agent sidecar
BlockingFindings reviewed within 48h — blocks next release if Critical

3.5 Container / Infrastructure Scanning

PropertyValue
Tool(s)Trivy / Grype / AWS Inspector
ScopeAll container images + Kubernetes manifests + Terraform/CDK code
FrequencyEvery image build + daily registry scan
Blocking YES — Criticaltest CVEsfailure in base image blockblocks deployment
IaC scanningCheckov / tfsec — every PR

ContainerRequired hardeningsecurity requirements:test cases:

    // 
  • Non-root1. userRBAC intests Dockerfile
  • Read-onlyall rootrole/action filesystemcombinations where possible
  • No secrets in image layers
  • Base image: distroless or minimaldescribe('RBAC', (Alpine)
  • )
  • No=> unnecessary{ packages
  • test('accountant
cannot
create invoice', async () => { const res = await api.post('/api/v1/invoices', invoice, { role: 'accountant' }); expect(res.status).toBe(403); }); test('viewer cannot generate report', async () => { const res = await api.get('/api/v1/reports/vat', { role: 'viewer' }); expect(res.status).toBe(403); }); }); // 2. Org-scope isolation tests describe('Multi-tenant isolation', () => { test('org A cannot access org B invoice', async () => { const orgBInvoice = await createInvoice({ org: 'org-b' }); const res = await api.get(`/api/v1/invoices/${orgBInvoice.id}`, { org: 'org-a' }); expect(res.status).toBe(403); }); test('org A cannot list org B contacts', async () => { const res = await api.get('/api/v1/contacts', { org: 'org-a' }); expect(res.body.data.every(c => c.organizationId === 'org-a-id')).toBe(true); }); }); // 3. VAT calculation accuracy tests describe('VAT calculations', () => { test('Serbian VAT at 20%', () => { expect(calculateVAT(new Decimal('100.00'), 'RS')).toEqual(new Decimal('20.00')); }); test('Croatian VAT at 25%', () => { expect(calculateVAT(new Decimal('100.00'), 'HR')).toEqual(new Decimal('25.00')); }); test('BiH VAT at 17%', () => { expect(calculateVAT(new Decimal('100.00'), 'BA')).toEqual(new Decimal('17.00')); }); test('NUMERIC precision — no float rounding', () => { expect(calculateVAT(new Decimal('33.33'), 'RS')).toEqual(new Decimal('6.67')); }); }); // 4. Input validation tests describe('Zod validation', () => { test('rejects non-UUID customerId', async () => { const res = await api.post('/api/v1/invoices', { customerId: 'not-a-uuid' }); expect(res.status).toBe(400); }); test('rejects future invoice date beyond 1 year', async () => { const res = await api.post('/api/v1/invoices', { invoiceDate: '2030-01-01' }); expect(res.status).toBe(400); }); });

3.65 Penetration Testing

Purpose: Expert manual security assessment simulating a real attacker.assessment.

Property Value
Frequency Annual minimum; after major architectural changes
ScopeWhen FullBefore applicationCroatia/Serbia + API + infrastructure + social engineeringlaunch (optional)mandatory for HR-FISK compliance)
Provider {{PENTEST_FIRM}}External security retainer:firm {{RETAINER_STATUS}}(TBD)
Methodology OWASP Testing Guide v4.2 + PTES
Rules of engagementWritten agreement required before each engagement
OutputFull findings report + executive summary + remediation guidance
Blocking Critical findings: system taken offline until remediated

Penetration test scope template:for Bilko:

In-scope:
- Production URLs:Production: https://{{DOMAIN}},bilko.io, https://api.{{DOMAIN}}bilko.io
- Authentication flows (login, registration,register, MFA,2FA, SSO)password reset)
- All authenticated API endpoints
- Multi-tenant isolation (IDOR across organizations)
- File upload functionality(receipt attachments)
- AdministrativeSEF functionsand HR-FISK submission endpoints
- CloudFinancial infrastructurecalculation integrity (read-onlyVAT, reconnaissance)double-entry)

Out-of-scope:
- Denial of service attacks
- Railway and Vercel infrastructure (cloud provider responsibility)
- Cloudflare infrastructure
- Social engineering of staff without prior approval
- Physical penetration testing (unless separately scoped)
- Third-party servicesSEF/FINA (AWS infrastructure itself, Stripe, etc.)
- Automated scanning beyond initial reconnaissance (provider uses their own tools)systems

Rules of engagement:
- Testing window: {{START_DATE}}Agreed toin {{END_DATE}}
- Notifywriting before testing: {{CONTACT}} via {{METHOD}}engagement
- Halt on critical finding: Immediately notify {{SECURITY_CONTACT}}[email protected]
- Test accounts provided: Separate test organizations in staging

3.7 Red Team Exercises

Purpose: Adversarial simulation to test detection and response capabilities, not just prevention.

PropertyValue
FrequencyAnnual for critical systems
ScopeFull kill-chain simulation (initial access → lateral movement → exfiltration)
Provider{{RED_TEAM_FIRM}}
Blue team awarenessOnly executive sponsor knows — security team is unaware (true test)
OutputRed team report + purple team exercise (joint analysis)

4. Vulnerability Classification & Remediation SLAs

4.1 Severity Classification (CVSS v3.1)

Severity CVSS Score Definition Bilko Example
Critical 9.0 – 0-10.0 Remote code execution, full auth bypass, mass data exfiltrationaccess without auth Log4ShellPrisma (CVSSraw 10.0)SQL injection exposing all orgs
High 7.0 – 0-8.9 Privilege escalation, cross-tenant IDOR, authenticated auth bypass (authenticated), significant data exposure SQLiOrg-scope withbypass auth— viewing another org's invoices
Medium 4.0 – 0-6.9 Limited data exposure, CSRF, storedRBAC XSS,bypass insecurefor confignon-critical action ReflectedViewer XSSrole accessing reports
Low 0.1 – 1-3.9 Minor informationinfo disclosure, theoretical risk VersionStack disclosuretrace in error response
Informational N/A Best practice recommendation, no direct exploitabilityrecommendation WeakMissing passwordcache-control policyheader

4.2 Remediation SLAs

Severity SLA Action on Breach of SLA
Critical Immediate containment in 4h; full remediation in 24 hours Emergency escalation — CISO + CEO. System taken offline if risk cannot be mitigated.mitigated; notify CEO
High 7 calendar days Escalate to CISO.CTO + CEO. Engineering manager mustapproves approve extension request.extension.
Medium 30 calendar days Engineering lead tracks. Exception requires security team sign-off.
Low 90 calendar days TrackedBacklog. in backlog. Reviewed in quarterlyQuarterly security review.
Informational Next sprint (best effort) Tracked but notNot SLA-bound.

SLACross-tenant tracking:IDOR (High+) is treated as Critical for Bilko All findingsfinancial indata {{ISSUE_TRACKER}}exposure withfor labelany security-findingorganization +triggers severityimmediate label.

P1

Exceptionincident process: Written request to Security Lead → CISO approval → compensating control required → max extension = 1× original SLA.response.


5. Security Code Review Checklist

Required

Everyfor every PR touching authentication, authorization,RBAC, payment,financial PII,calculations, or cryptography requires a security review:PII:

Authentication & Authorization:

  • No hardcoded credentialssecrets orin secrets
  •  Password hashing uses Argon2id or bcrypt (cost ≥ 12)code
  • JWT validation:validated: signature, expiry, issuer,iss, audienceaud
  • Authorizationorg-scoped checkedWHERE clause on EVERYevery protectedPrisma endpointquery (nottouching justmulti-tenant at route level)data
  • NoRBAC isAdminrequireRole() flagmiddleware fromapplied user-suppliedon inputevery protected endpoint
  • Session2FA tokenscode invalidatedvalidation onuses logoutconstant-time comparison

Input Validation:

  • AllZod userschema inputsfor validatedall (type,request length, format, allowed values)bodies
  • ParameterizedPrisma queries foronly all database operations (no string$queryRaw concatenation)with user input
  • File uploads: type validated by magic bytes, filename sanitized,validated, stored outsidein R2 not webroot
  • No eval(),string exec(),interpolation in SQL or shellsystem commandcommands
  • interpolation
with

Financial userIntegrity:

input
  •  All monetary values use Decimal / NUMERIC — no number or float
  • HTMLVAT outputrates properlyvalidated escapedagainst country code (nonot innerHTMLuser-supplied)
  • with
  • userDouble-entry: data)debit = credit enforced
  •  Exchange rates locked at transaction date

Cryptography:

  • No MD5, SHA-1, DES, RC4 anywhere
  • Random values generatedfrom withcrypto.randomBytes() CSPRNG— not Math.random()
  • IVs/noncesbcrypt arecost randomfactor and= never12 reusedfor any new password hashing
  • Private keys / secrets not in source code or logs

Error Handling:

  •  No stack traces or internal details in API error responses
  •  Errors logged internally but not exposed externally
  •  No user enumeration (same response for "user not found" and "wrong password")

Data Handling:

  • PIITax notIDs logged(PIB/JMBG/OIB/JIB) inand plaintextIBAN encrypted before storage
  • Sensitive data not in query parameters (useor POST body)URLs
  • NoPII unnecessarynot datain retentionSentry error reports (datause minimization).addEventProcessor() to scrub)
  • ProperLoggedAction data deletion/anonymization on account close

Dependencies:

  •  New dependency reviewedentries for knownall CVEsfinancial before adding
  •  No end-of-life dependencies introduced
  •  License compatible with project licensemutations

6. Bug Bounty Program

Status: {{ACTIVE / PLANNED / NOT_APPLICABLE}}

Platform: {{HACKERONE / BUGCROWD / INTIGRITI}} Scope: {{IN_SCOPE_URLS}} Out of scope: {{OUT_OF_SCOPE}}

Reward structure:

SeverityReward Range
Critical{{AMOUNT}}
High{{AMOUNT}}
Medium{{AMOUNT}}
LowNo reward (acknowledgment only)

Response SLAs (to researcher):

  • Acknowledgment: 1 business day
  • Triage/validation: 5 business days
  • Remediation update: Per §4.2 SLAs
  • Reward payment: Within 14 days of fix deployment

Safe harbor: Researchers acting in good faith following disclosure policy are protected from legal action.


7. Security Testing in CI/CD Pipeline

flowchart LR
    COMMIT[Developer\nCommit] -->|Pre-commit hook|commit| SAST_INC[SAST\nIncremental]ESLint\nSAST incremental]
    SAST_INC -->|Pass| PUSH[Git Push]
    PUSH --> PR[Pull Request]

    PR --> SAST_FULL[SAST\nFullESLint Scan]Full\n+ TypeScript strict]
    PR --> SCA[SCA\nDependency]npm audit\nDependabot]
    PR --> SECRET[Secret\nScanning]
    PR --> IAC[IaC\VITEST[Vitest\nSecurity Scan]tests]

    SAST_FULL & SCA & SECRET & IACVITEST -->|All pass| BUILD[Build\nContainer]Build]
    BUILD --> CONTAINER_SCAN[Container\nScan]
    CONTAINER_SCAN -->|Pass| STAGING[Deploy to\nStaging]Staging]
    STAGING --> DAST[DAST\nScan]OWASP ZAP\nDAST scan]
    STAGING --> IAST[IAST\nRuntime]E2E[Playwright\nIsolation tests]
    DAST & IASTE2E -->|Pass| GATE{Security\nGate}Security Gate}

    GATE -->|Pass| PROD[Deploy to\nProduction]Production]
    GATE -->|Fail| BLOCK[Block Deploy\nAlert Security][email protected]]

Pipeline security gate criteria:

test
Gate Tool Blocking Criteria
Pre-commit SASTESLint (incremental)security rules Critical findings in changed files
PR gate 1 SASTESLint (full)+ TypeScript strict Any Critical or High (unresolved)
PR gate 2 SCAnpm audit Critical CVE in dependencies
PR gate 3 Secret scanscanning Any detected secret
PR gate 4 IaCVitest scansecurity tests CriticalAny misconfiguration
Post-buildContainer scanCritical CVE in base image or packagesfailure
Post-staging DASTOWASP ZAP Critical or High dynamic finding
Post-stagingPlaywright E2E isolationCross-tenant access succeeds (should fail)

8. Reporting Format

8.1 Individual Finding Report

Finding ID: VULN-{{YEAR}}-{{SEQUENCE}}
Title: {{SHORT_DESCRIPTION}}
Severity: Critical / High / Medium / Low
CVSS Score: {{SCORE}} (v3.1)
CVSS Vector: {{VECTOR_STRING}}

Description:
{{DETAILED_DESCRIPTION_OF_VULNERABILITY}}

Affected Systems:
- {{SYSTEM_1}}: {{URL_OR_CODE_LOCATION}}
- {{SYSTEM_2}}: {{URL_OR_CODE_LOCATION}}

Proof of Concept:
{{STEPS_TO_REPRODUCE}}
{{SCREENSHOTS_OR_CURL_COMMANDS}}

Impact:
{{WHAT_AN_ATTACKER_CAN_DO_IF_EXPLOITED}}

Remediation:
{{SPECIFIC_REMEDIATION_STEPS}}

References:
- CWE-{{N}}: {{CWE_DESCRIPTION}}
- OWASP: {{OWASP_CATEGORY}}
- CVE-{{N}} (if applicable)

Owner: {{ASSIGNED_ENGINEER}}
SLA: {{DUE_DATE}}
Status: Open / In Progress / Resolved / Accepted Risk

8.2 Security Testing Summary Report

Produced after each: Penetration test, quarterly DAST, quarterly vulnerability assessment.

Report period: {{START_DATE}} to {{END_DATE}}
Report type: Penetration Test / DAST / Vulnerability Assessment
Conducted by: {{INTERNAL / EXTERNAL_FIRM}}

EXECUTIVE SUMMARY
Total findings: Critical: {{N}} | High: {{N}} | Medium: {{N}} | Low: {{N}}
Findings resolved within SLA: {{N}}/{{TOTAL}} ({{PERCENT}}%)
Risk trend vs previous period: Improving / Stable / Worsening

TOP FINDINGS BY SEVERITY
[Table of Critical and High findings]

REMEDIATION STATUS
[Table of all findings with status and owner]

RECOMMENDATIONS
1. {{STRATEGIC_RECOMMENDATION_1}}
2. {{STRATEGIC_RECOMMENDATION_2}}

9.7. Metrics & KPIs

Metric Target Reporting Frequency
Critical findings resolved within SLA 100% Monthly
High findings resolved within SLA 95% Monthly
Mean time to detect (MTTD)< {{N}} daysQuarterly
Mean time to remediate (MTTR)MTTR — Critical < 24 hours MonthlyPer incident
MTTR — High < 7 days Monthly
SAST false positive rate< {{PERCENT}}%Quarterly
% of deployments with passing security gate 100% Monthly
Vitest security test pass rate100%Every deployment
Open High+ findings (backlog) < {{N}}0 Monthly
npm audit — Critical CVEs0Weekly
Penetration test — criticalCritical findings closed 100% within 30 days Per test

Security metrics dashboard:tracking: {{DASHBOARD_URL}}GitHub Issues with security label Quarterly security report: Published to {{RECIPIENTS}}CEO onand {{SCHEDULE}}engineering team


Approval

Role Name Date Signature
Author Compliance Architect 2026-02-23
CISOCTO
Engineering Lead
CTO
ManagementDPO