Skip to content
Get in touch
Security ·

Our OWASP ASVS 5.0 Level 1 Self-Assessment, Line by Line

We ran our dashboard app, app.otomoai.com.my, through the OWASP Application Security Verification Standard (ASVS) 5.0.0, Level 1 (all 70 requirements) and wrote down every result. This post is that result, line by line: what passed, what didn't, and what we couldn't check from the code alone.

What ASVS is, and what Level 1 means

The OWASP ASVS project page describes the standard as providing "a basis for testing web application technical security controls and also provides developers with a list of requirements for secure development." It is a public, versioned checklist maintained by the Open Worldwide Application Security Project, not a tool or a paid audit. Version 5.0.0 is the current major version, the first release of the 5.x line.

ASVS groups its requirements into three levels of increasing depth. Level 1 is the level meant to apply to every application, not only those handling higher-risk data, and it is the level this review used in full: all 70 Level 1 requirements, none skipped.

How we assessed it

This was a read-only review of the dashboard app's code, carried out on 14 September 2026 against all 70 Level 1 requirements in ASVS 5.0.0. Every status below is tied to a named file in our code or to a named managed-service control from our database or hosting provider, never a guess. No third party took part; this is our own read of our own code, not an independent audit.

What each status means

  • Pass: The requirement is met, and a named file in our code or a named managed-service control is the evidence.
  • Not applicable: The requirement describes a mechanism the app doesn't use, so there is nothing in it to check.
  • Partial: Part of the requirement is met; the note says exactly what is covered and what is not.
  • Not verified: Checking this needs access this review didn't have, usually a setting in our database or hosting provider's dashboard, or a live scan.
  • Fail: The requirement is not met today.

The result, chapter by chapter

The file and control references behind each status are held internally, not published here, and are shown to a client on request. The tables below show the requirement, the status, and the note explaining it.

Summary of all 70 ASVS 5.0.0 Level 1 results, 14 September 2026.
StatusCount
Pass43
Not applicable12
Not verified7
Partial6
Fail2

V1: Encoding and Sanitization

V1: Encoding and Sanitization, 8 requirements.
IDRequirementStatusNote
V1.2.1Verify that output is encoded for its context (HTML, HTML attributes, CSS, HTTP headers, etc.) so the message or document structure cannot be changed.PassPage text is rendered through React, which escapes it automatically, and every API reply is sent as JSON, not raw HTML.
V1.2.2Verify that untrusted data used to build URLs is encoded for its context, and that only safe URL protocols are permitted (disallow javascript: or data:).PassLinks the chatbot can show a customer are limited to web addresses (http/https). Password-reset links are built from the app's own address, not from user-supplied text.
V1.2.3Verify that dynamically built JavaScript content, including JSON, is encoded or escaped to prevent JavaScript and JSON injection.PassEvery API reply is built with JSON.stringify, which escapes the data automatically.
V1.2.4Verify that database queries use parameterized queries, ORMs, or frameworks protected from SQL and other injection attacks, including in stored procedures.PassDatabase reads and writes go through our database provider's REST layer using encoded filter parameters, not hand-built SQL text.
V1.2.5Verify that the application protects against OS command injection, using parameterized OS calls or contextual command-line output encoding.Not applicableThe backend code runs inside our hosting provider's sandbox, which has no way to run operating-system commands at all.
V1.3.1Verify that all untrusted HTML input from WYSIWYG editors or similar is sanitized using a well-known and secure HTML sanitization library or framework feature.Not applicableThe only editable-text component in the app captures plain text, not formatted HTML, so there is no HTML content that needs sanitizing.
V1.3.2Verify that the application avoids eval() or other dynamic code execution features, and sanitizes any user input included where there is no alternative.PassThe code does not build and run JavaScript from text at runtime.
V1.5.1Verify that XML parsers use a restrictive configuration, with unsafe features like external entity resolution disabled, to prevent XXE attacks.Not applicableThe app only exchanges JSON, never XML, so there is no XML parser to configure.

V2: Validation and Business Logic

V2: Validation and Business Logic, 4 requirements.
IDRequirementStatusNote
V2.1.1Verify that the application's documentation defines input validation rules for checking data items against an expected structure, common or internal.PartialValidation rules exist as code, with comments explaining them, but there is no separate written document listing input rules that a non-engineer could read on its own.
V2.2.1Verify that input is validated against an allow list or expected structure to enforce business rules; L1 can focus on business- or security-critical input.PassThe server checks submitted data against defined rules (allowed values, formats, and lengths) before acting on it.
V2.2.2Verify that input validation is enforced at a trusted service layer; client-side validation helps usability but must not be relied on for security.PassThe rules that matter are enforced on the server, so a user cannot bypass them just by editing the browser.
V2.3.1Verify that the application will only process business logic flows for the same user in the expected sequential step order and without skipping steps.PartialLogin enforces one clear step order server-side: you must complete two-factor verification before you get in. Other multi-step flows in the app (like appointment booking) were not individually checked for the same kind of step-skipping protection.

V3: Web Frontend Security

V3: Web Frontend Security, 8 requirements.
IDRequirementStatusNote
V3.2.1Verify that controls stop browsers rendering a response in the wrong context, via Sec-Fetch-* checks, CSP sandboxing, or Content-Disposition: attachment.PassAPI responses tell the browser never to render them as a webpage, and downloaded files are marked to save rather than open inline.
V3.2.2Verify that text-only content is rendered with safe functions such as createTextNode or textContent, to prevent unintended HTML or JavaScript execution.PassText shown to users goes through React's normal text rendering, not a raw-HTML injection path.
V3.3.1Verify that cookies have the 'Secure' attribute, and use the '__Host-' or '__Secure-' name prefix.Not applicableThe app never sets a session cookie, so the cookie-security rule does not apply here; sign-in state is instead sent explicitly as a bearer token on each request.
V3.4.1Verify that a Strict-Transport-Security header is on all responses, with a maximum age of at least one year; L2 and up must also cover all subdomains.PassBoth the app's pages and its API responses tell browsers to always use an encrypted connection, for at least two years, including subdomains.
V3.4.2Verify that CORS's Access-Control-Allow-Origin is fixed or checked against a trusted allowlist, and a wildcard '*' response never carries sensitive data.PassThe customer dashboard's API cannot be read from another website at all. The public chat-widget endpoints only share data with the specific website domains a client has registered.
V3.5.1Verify that, where CORS preflight is not relied on, sensitive requests are validated as originating from the application itself, to defend against CSRF.Not applicableBecause sign-in state lives in a header the browser never attaches automatically, a malicious website cannot silently make a signed-in user's browser take an action on their behalf.
V3.5.2Verify that, where the application relies on CORS preflight for sensitive functionality, no request can reach it without first triggering a preflight check.Not applicableSame reasoning as the row above: there is no automatic browser credential for another site to piggyback on, so this CORS-preflight-specific concern does not apply.
V3.5.3Verify that sensitive functionality uses POST, PUT, PATCH, or DELETE rather than safe methods like GET, or alternatively validates Sec-Fetch-* headers strictly.Not verifiedThe handful of routes checked use the right kind of request for actions that change data, but not every route in the app was checked one by one.

V4: API and Web Service

V4: API and Web Service, 2 requirements.
IDRequirementStatusNote
V4.1.1Verify that every HTTP response with a body has a Content-Type header matching its actual content, including a safe charset such as UTF-8.PassEvery API reply declares itself as JSON, and JSON is always UTF-8 text by specification, so no extra character-set label is needed.
V4.4.1Verify that WebSocket over TLS (WSS) is used for all WebSocket connections.PassThe app's one real-time feature (live calendar updates) only ever connects over an encrypted WebSocket, and the app's own security policy blocks plain, unencrypted WebSocket connections.

V5: File Handling

V5: File Handling, 4 requirements.
IDRequirementStatusNote
V5.2.1Verify that the application will only accept files of a size which it can process without causing a loss of performance or a denial of service attack.PassThe document-upload endpoint refuses files bigger than 15 MB before doing any real work on them.
V5.2.2Verify that accepted files have their extension checked against actual content, e.g. magic bytes; L1 can focus on business- or security-critical files.PassUploaded files are checked byte-by-byte to confirm their real format matches what they claim to be, for every file type where that check is possible.
V5.3.1Verify that files from untrusted input, stored in a public folder, are not executed as server-side code when accessed directly.PassUploaded files land in a storage bucket that only serves files back to users; it has no ability to run them as program code.
V5.3.2Verify that file paths use trusted, internally generated data rather than user-submitted filenames, to protect against path traversal, LFI/RFI, and SSRF.PassThe system decides where an uploaded file is saved using its own internal ID, not the filename the user provided, so a crafted filename cannot redirect where the file gets written.

V6: Authentication

V6: Authentication, 13 requirements.
IDRequirementStatusNote
V6.1.1Verify that documentation defines how rate limiting and anti-automation defend against credential stuffing and brute force, without malicious account lockout.PassThere is a written note describing the rate-limiting approach for this app, and the approach used (a refilling request budget) slows repeated login attempts without ever locking a real user out of their own account.
V6.2.1Verify that user set passwords are at least 8 characters in length although a minimum of 15 characters is strongly recommended.PassNew account passwords must be at least 12 characters, above the 8-character minimum this rule requires.
V6.2.2Verify that users can change their password.PassSigned-in users have a working "change password" feature.
V6.2.3Verify that password change functionality requires the user's current and new password.PassChanging a password requires proving you know the old one first.
V6.2.4Verify that new or changed passwords are checked against at least the top 3000 common passwords matching the application's password policy.FailThe live app does not yet check a new password against a breached-password list. A check against the Have I Been Pwned range API was built on 14 September 2026 and ships with the next app deploy; this line changes to Pass then.
V6.2.5Verify that passwords of any composition can be used, with no rule requiring a minimum count of upper case, lower case, numeric, or special characters.FailThe live app still requires an uppercase letter, a digit and a symbol, which ASVS 5.0 forbids. The rules were removed in code on 14 September 2026 (12 to 128 characters only) and ship with the next app deploy; this line changes to Pass then.
V6.2.6Verify that password fields use type=password to mask entry, though apps may let users temporarily reveal the password.PassPassword fields are masked by default; an eye icon lets the user reveal what they've typed if they choose to.
V6.2.7Verify that "paste" functionality, browser password helpers, and external password managers are permitted.PassNothing in the code stops users from pasting a password or from using their browser's or a third-party password manager.
V6.2.8Verify that the application verifies the user's password exactly as received from the user, without any modifications such as truncation or case transformation.PassThe password a user types is sent to the authentication service exactly as typed, without being shortened or altered.
V6.3.1Verify that controls preventing credential stuffing and password brute force are implemented as the application's security documentation describes.PassThe rate limits described in the security documentation are actually wired into the login-adjacent endpoints in the code.
V6.3.2Verify that default user accounts (e.g., "root", "admin", or "sa") are not present in the application or are disabled.PassNo built-in "admin" or "root" account with a known password ships with the app.
V6.4.1Verify that system-generated initial passwords or activation codes are securely random, follow the password policy, and expire shortly after issue or first use.Not applicableThere is no "temporary starter password" in this system that could linger unexpired: accounts either set their own password at sign-up or receive a one-time reset link instead.
V6.4.2Verify that password hints or knowledge-based authentication (so-called "secret questions") are not present.PassThe app has no "secret question" or password-hint recovery option.

V7: Session Management

V7: Session Management, 6 requirements.
IDRequirementStatusNote
V7.2.1Verify that the application performs all session token verification using a trusted, backend service.PassThe server checks that a session is genuine by asking the authentication service directly on every request, rather than trusting the browser's claim about who is signed in.
V7.2.2Verify that the application uses dynamically generated self-contained or reference tokens for sessions, not static API secrets or keys.PassSession tokens come from our sign-in provider's managed service and are unique per login. The app never uses one fixed shared key as a session credential.
V7.2.3Verify that reference session tokens, if used, are unique, generated by a CSPRNG, and carry at least 128 bits of entropy.Not verifiedWhether the session token has enough built-in randomness is decided inside our sign-in provider's service; that detail is not visible in this codebase.
V7.2.4Verify that the application generates a new session token on user authentication, including re-authentication, and terminates the current session token.PassChanging your password signs out any other devices that were logged in, and every new sign-in gets a brand-new session token from the authentication service.
V7.4.1Verify that session termination, such as logout or expiry, disallows further use of that session, by invalidating backend data or tracking terminated tokens.PassSigning out, or being deactivated by an admin, is checked again on the very next action. An old session cannot keep being used.
V7.4.2Verify that the application terminates all active sessions when a user account is disabled or deleted (such as an employee leaving the company).PassWhen an admin deactivates a staff account, that account loses dashboard access on its very next request, not just at its next login.

V8: Authorization

V8: Authorization, 4 requirements.
IDRequirementStatusNote
V8.1.1Verify that authorization documentation defines rules restricting function-level and data-specific access by permission and resource attribute.PartialThe rules for who can do what are written down, but as comments inside the code rather than as a separate policy document a non-engineer could review on its own.
V8.2.1Verify that the application ensures that function-level access is restricted to consumers with explicit permissions.PassAlmost every dashboard action checks that the caller is signed in and holds the specific permission for that action; the few routes that skip this check either verify a signature from a trusted webhook sender instead, or are public-facing on purpose.
V8.2.2Verify that data-specific access is restricted to consumers with explicit permission to that data, to mitigate IDOR and BOLA.PartialAny staff member with the right permission can open or edit any record belonging to their own company. There is no further check tying a specific record to a specific staff member. That matches how a shared company CRM is meant to work, since all data in one deployment belongs to one customer, but it means permission rather than per-record ownership is the actual boundary.
V8.3.1Verify that authorization rules are enforced at a trusted service layer, not in controls an untrusted consumer could manipulate, such as client-side JavaScript.PassWho-can-do-what is decided on the server, so it cannot be bypassed by editing the browser.

V9: Self-contained Tokens

V9: Self-contained Tokens, 4 requirements.
IDRequirementStatusNote
V9.1.1Verify that self-contained tokens are validated using their digital signature or MAC to protect against tampering before accepting the token's contents.PassToken contents are only trusted after our sign-in provider's service has confirmed the token's signature is genuine on a live call.
V9.1.2Verify that only allowlisted algorithms, never 'None', can create or verify self-contained tokens, with extra controls if both algorithm types are supported.Not verifiedThis is a setting inside our sign-in provider's own project configuration, which this review could not inspect from the code.
V9.1.3Verify that key material used to validate tokens comes only from trusted pre-configured sources, with JWT headers like 'jku' checked against an allowlist.Not verifiedSame as above: this lives inside our sign-in provider's managed configuration, not in this codebase.
V9.2.1Verify that a token is only accepted within its stated validity time span, such as JWT's 'nbf' and 'exp' claims.PassBecause the app re-checks the token with our sign-in provider on every single request instead of trusting a locally stored copy, an expired token stops working immediately.

V10: OAuth and OIDC

V10: OAuth and OIDC, 5 requirements.
IDRequirementStatusNote
V10.4.1Verify that the authorization server validates redirect URIs based on a client-specific allowlist of pre-registered URIs using exact string comparison.Not applicableThis rule is for a system that hands out OAuth login tokens to other apps. This app doesn't do that. It only lets users sign in through Google and our sign-in provider.
V10.4.2Verify that an OAuth authorization code can be used only once for a token request, and reuse causes the server to reject the request and revoke related tokens.Not applicableSame as above: this rule applies to an OAuth login-issuing service, which this app is not.
V10.4.3Verify that the OAuth authorization code is short-lived: up to 10 minutes for L1/L2, up to 1 minute for L3.Not applicableSame as above.
V10.4.4Verify that an OAuth client is only allowed the grants it needs, and that the Implicit and Resource Owner Password Credentials grants are no longer used.Not applicableSame as above.
V10.4.5Verify that the OAuth server mitigates refresh token replay for public clients, preferably with sender-constrained tokens or rotation invalidating used tokens.Not applicableSame as above.

V11: Cryptography

V11: Cryptography, 3 requirements.
IDRequirementStatusNote
V11.3.1Verify that insecure block modes (e.g., ECB) and weak padding schemes (e.g., PKCS#1 v1.5) are not used.PassThe one place the app encrypts data (saved calendar-connection tokens) uses a modern authenticated encryption mode, not an outdated or weak one.
V11.3.2Verify that only approved ciphers and modes such as AES with GCM are used.PassSame evidence as the row above: encryption uses AES with GCM, which is on the current approved list.
V11.4.1Verify that only approved hash functions are used for cryptographic purposes such as signatures, HMAC, and KDFs, and disallowed ones like MD5 are never used.PassEvery place the code hashes or signs data uses SHA-256, not an outdated algorithm like MD5.

V12: Secure Communication

V12: Secure Communication, 3 requirements.
IDRequirementStatusNote
V12.1.1Verify that only the latest recommended TLS versions, such as TLS 1.2 and 1.3, are enabled, with the latest preferred.Not verifiedThis is a setting inside our hosting provider's own dashboard for the domain, which this review could not inspect from the code.
V12.2.1Verify that TLS is used for all client connections to external-facing HTTP services, with no fallback to unencrypted communication.PassThe app tells browsers to always use an encrypted connection for at least two years, and the hosting platform serves the app over HTTPS by default.
V12.2.2Verify that external facing services use publicly trusted TLS certificates.PassThe certificate that proves the site's identity to a browser is issued automatically by our hosting provider and is trusted by every standard browser.

V13: Configuration

V13: Configuration, 1 requirement.
IDRequirementStatusNote
V13.4.1Verify that the application is deployed without source control metadata such as .git or .svn, or with those folders made inaccessible.PassOur hosting provider only serves the compiled website files from the build output folder, not the project's raw source folder, so the .git history folder is never published.

V14: Data Protection

V14: Data Protection, 2 requirements.
IDRequirementStatusNote
V14.2.1Verify that sensitive data is only sent in the HTTP body or headers, never in the URL or query string.Not verifiedThe endpoints checked send sensitive data in headers or the message body, not the web address itself, but not every single endpoint in the app was checked one by one.
V14.3.1Verify that authenticated data is cleared from client storage after the session ends, whether via the Clear-Site-Data header or client-side cleanup.PartialLogging out removes the sign-in token from the browser, but a few other pieces of saved data (like bank details cached for the payslip tool) are not explicitly wiped when someone signs out of a shared device.

V15: Secure Coding and Architecture

V15: Secure Coding and Architecture, 3 requirements.
IDRequirementStatusNote
V15.1.1Verify that documentation defines risk-based remediation time frames for vulnerable third-party components and for updating libraries in general.PassWe publish how fast we fix a confirmed vulnerability by severity, and the same targets apply to vulnerable dependencies flagged by our weekly scan.
V15.2.1Verify that the application only contains components which have not breached the documented update and remediation time frames.Not verifiedChecking this would need an actual scan of the app's dependencies for known vulnerabilities, compared against a written remediation policy, and neither the scan nor the policy exists yet to compare.
V15.3.1Verify that the application returns only the required subset of fields from a data object, not the entire object.PartialSome parts of the app pick out only the fields they need before sending data back (like the employee profile shown after login). Others, like the contact-edit endpoint, fetch and return the whole database row. A full check across the roughly 85 route handlers was not completed.

The two Fails

Two requirements fail today, both in password rules (V6.2.4 and V6.2.5). The live app does not yet check new passwords against a breached-password list, and it still requires an uppercase letter, a digit and a symbol, which ASVS 5.0.0 says an application must not require. Both fixes were written in code on 14 September 2026 and ship with the next app deploy; both lines change to Pass once that deploy goes out.

Not verified, and Not applicable

Seven requirements are marked Not verified: not fail, not unknown, just outside what a code review can see. Most of them are settings inside our database and hosting providers' own project dashboards (token signing algorithms, minimum TLS version, key sources), which this review had no access to. The rest need a live check this pass didn't run: an actual dependency-vulnerability scan, and a one-by-one audit of every route file rather than the sample checked here. A full check needs those three things: read access to the database and hosting project dashboards, a live dependency scan, and a route-by-route audit of the roughly 85 API route files.

Twelve requirements are marked Not applicable. Each one describes a mechanism the app simply doesn't have: session cookies, an XML parser, an OS shell, an OAuth authorization-server role. There is nothing in that row for the app to fail or pass; the note on each one says why.

What this is not

  • It is a self-assessment, not an ASVS certification. OWASP does not certify applications, and we do not claim to be certified.
  • No third party verified any of these results. This was our own read of our own code.
  • It covers app.otomoai.com.my only: our dashboard app, not the marketing site or anything else.
  • It is a point-in-time result, dated 14 September 2026. We re-run it when the code changes materially, and the two Fails above change to Pass once the fix already written ships in the next app deploy.

Sources