The findings in full
Critical D-01 · The database downloads over the web
What it is. The app tells the web server to hand out every file in its own folder, and the database lives in that folder. A visitor who requests it by name — and the name is the usual one — downloads the whole thing.
Why it matters. That one file is the entire customer list — names, phones, addresses — their password hashes, every walker, and every booking note, which here include alarm codes and where the key is left. It is handed over with no login and no trace beyond a line in a log: the single fastest way to lose everything at once.
The fix. Serve only the folder of public front-end files, never the application folder, and keep the database outside the web root entirely. It is a one-line change to what is shared, plus moving one file.
Critical D-02 · The login cookie can be rewritten by hand
What it is. When you log in, the app gives your browser a cookie that simply spells out your user number and whether you're an admin, in a format anyone can read and change. Nothing stops a visitor writing their own cookie that says "I am user 1, and I am an admin."
Why it matters. It turns every admin-only page into a public one. The attacker doesn't need a password, a leak, or any skill beyond editing a value in their browser.
The fix. Use signed, server-verified sessions — a standard, well-tested library rather than a hand-rolled cookie — so a cookie the server didn't issue is rejected. The app already has a login step to hang this on.
Critical D-03 · The walker search runs text you type as a command
What it is. The "find a walker" box drops whatever you type straight into the instruction the app sends its database. Type the right punctuation and the rest of your text becomes a new command the database obediently runs.
Why it matters. The search needs no login, so this is open to the public, and it is not limited to searching: the same hole reads any table — users, passwords, addresses — and in the wrong setup can change or delete data too. It is the classic way databases get emptied.
The fix. Never build a database instruction by gluing text together. Use parameters, which keep what you typed as a value to match, never as a command. Every query in the app should be checked and converted the same way.
Critical D-04 · Saving your profile can make you an admin
What it is. When you edit your profile, the app saves every field the browser sends, trusting the list. But the account's permission level is a field on the same record, so a request that includes it will set it — including to "admin".
Why it matters. Any signed-up customer can promote themselves to full control, then do everything D-02's attacker could, from a real account. It needs no trickery, only knowing the field is there — and fields like this are easy to guess.
The fix. Decide explicitly which fields a user may change about themselves — name, phone, address — and ignore anything else. Permission level is set by the app, never by the request.
High D-05 · Any booking is readable by its number
What it is. Bookings are numbered 1, 2, 3, and the app will show you any of them if you ask by number. It checks that you're logged in, but not that the booking is yours.
Why it matters. In this app the booking notes hold alarm codes and where the door key is left. One logged-in customer can walk the numbers and collect the lot — a physical-security problem, not just a data one. This pattern, reading other people's records by changing a number in the address, is one of the most common serious flaws in AI-built apps.
The fix. On every record fetched by its number, check it belongs to the person asking before returning it. The ownership check is one line, and it belongs on each of these endpoints.
High D-06 · Passwords are stored with broken hashing
What it is. Passwords are stored scrambled with MD5, an old method that is both reversible in practice and fast enough to guess billions of times a second. There is no per-user salt, so identical passwords look identical in the table.
Why it matters. The point of hashing is that a stolen database still doesn't hand over the passwords. This hashing doesn't achieve that: common passwords fall in seconds, and because people reuse them, the damage spreads to your customers' other accounts. It turns one leak into many.
The fix. Store passwords with a modern, deliberately slow password hash (bcrypt, scrypt or Argon2), each with its own salt. Re-hash on next login so existing accounts migrate quietly.
High D-07 · Whether you're an admin is decided in the browser
What it is. The front-end code decides whether to show the admin features by checking the logged-in user's role — a value the browser holds and can change. The server never checks again.
Why it matters. Hiding the admin button in the browser is a lock with the key taped beside it. Anyone can reveal the hidden controls, or simply call the admin endpoints directly; the only thing stopping them was a button that wasn't drawn. The server-side hole that lets those calls through is D-02 — this finding is why fixing D-02 alone isn't enough, and why access control can never live in the page.
The fix. Decide and enforce every permission on the server. The front end may hide a button for tidiness, but must never be the thing that guards it.
High D-08 · The AI feature has no login and no limit
What it is. The "write a bio for my dog" endpoint can be called by anyone, without logging in, as often as they like — and the caller gets to say which AI model to use and how long the answer may be, the two things that set the price.
Why it matters. This is a direct line to spend money on your account, handed to the public with the size dial included. A simple loop can run a large bill overnight; this is exactly the kind of surprise that turns a cheap side-feature into a four-figure invoice.
The fix. Require a login, cap how often each user may call it, and fix the model and the limits on the server rather than taking them from the request. Decide a daily ceiling and alert when it's approached.
Medium D-09 · Any website can act as your logged-in users
What it is. The app tells browsers that any other website may make requests to it and send along the visitor's login cookie. The intent is usually "let my own front end talk to my API"; the effect is "let everyone's". The session cookie is set to be sent on cross-site requests too, so nothing catches it on the way in.
Why it matters. A customer who is logged in to Walkies and then visits a malicious page can have actions taken in their name — a booking made, their profile changed — without noticing. On its own it's awkward to exploit, which is why it's Medium; combined with the auth findings above, it widens their blast radius.
The fix. Allow only the app's own web address to make these requests, listed explicitly, rather than reflecting whatever asked.