Posts in Php (20 found)
W. Jason Gilmore 4 weeks ago

Minimum Viable Expectations for Developers and AI

We're headed into the tail end of 2025 and I'm seeing a lot less FUD (fear, uncertainty, and doubt) amongst software developers when it comes to AI. As usual when it comes to adopting new software tools I think a lot of the initial hesitancy had to do with everyone but the earliest adopters falling into three camps: don't, can't, and won't: When it comes to AI adoption, I'm fortunately seeing the numbers falling into these three camps continuing to wane. This is good news because it benefits both the companies they work for and the developers themselves. Companies benefit because AI coding tools, when used properly, unquestionably write better code faster for many (but not all) use cases . Developers benefit because they are freed from the drudgery of coding CRUD (create, retrieve, update, delete) interfaces and can instead focus on more interesting tasks. Because this technology is so new, I'm not yet seeing a lot of guidance regarding setting employee expectations when it comes to AI usage within software teams. Frankly I'm not even sure that most managers even know what to expect. So I thought it might be useful to outline a few thoughts regarding MVEs (minimum viable expectations) when it comes to AI adoption: Even if your developers refuse to generative AI tools for large-scale feature implementation, the productivity gains to be had from simply adopting the intelligent code completion features is undeniable. A few seconds here and a few seconds there add up to hours, days, and weeks of time saved otherwise spent repeatedly typing for loops, commonplace code blocks, and the like. Agentic AIs like GitHub Copilot can be configured to perform automated code reviews on all or specific pull requests. At Adalo we've been using Copilot in this capacity for a few months now and while it hasn't identified any groundshaking issues it certainly has helped to improve the code by pointing out subtle edge cases and syntax issues which could ultimately be problematic if left unaddressed. In December, 2024 Anthropic announced a new open standard called Model Context Protocol (MCP) which you can think of as a USB-like interface for AI. This interface gives organizations the ability to plug both internal and third-party systems into AI, supplementing the knowledge already incorporated into the AI model. Since the announcement MCP adoption has spread like wildfire, with MCP directories like https://mcp.so/ tracking more than 16,000 public MCP servers. Companies like GitHub and Stripe have launched MCP servers which let developers talk to these systems from inside their IDEs. In doing so, developers can for instance create, review, and ask AI to implement tickets without having to leave their IDE. As with the AI-first IDE's ability to perform intelligent code completion, reducing the number of steps a developer has to take to complete everyday tasks will in the long run result in significant amounts of time saved. In my experience test writing has ironically one of AI's greatest strengths. SaaS products I've built such as https://securitybot.dev/ and https://6dollarcrm.com/ have far, far more test coverage than they would have ever had pre-AI. As of the time of this writing SecurityBot.dev has more than 1,000 assertions spread across 244 tests: 6DollarCRM fares even better (although the code base is significantly larger), with 1,149 assertions spread across 346 tests: Models such as Claude 4 Sonnet and Opus 4.1 have been remarkably good test writers, and developers can further reinforce the importance of including tests alongside generated code within specifications. AI coding tools such as Cursor and Claude Code tend to work much better when the programmer provides additional context to guide the AI. In fact, Anthropic places such emphasis on the importance of doing so that it appears first in this list of best practices . Anything deemed worth communicating to a new developer who has joined your team is worthy of inclusion in this context, including coding styles, useful shell commands, testing instructions, dependency requirements, and so forth. You'll also find publicly available coding guidelines for specific technology stacks. For instance I've been using this set of Laravel coding guidelines for AI with great success. The sky really is the limit when it comes to incorporating AI tools into developer workflows. Even though we're still in the very earliest stages of this technology's lifecycle, I'm both personally seeing enormous productivity gains in my own projects as well as greatly enjoying seeing the teams I work with come around to their promise. I'd love to learn more about how you and your team are building processes around their usage. E-mail me at [email protected] . Developers don't understand the advantages for the simple reason they haven't even given the new technology a fair shake. Developers can't understand the advantages because they are not experienced enough to grasp the bigger picture when it comes to their role (problem solvers and not typists). Developers won't understand the advantages because they refuse to do so on the grounds that new technology threatens their job or is in conflict with their perception that modern tools interfere with their role as a "craftsman" (you should fire these developers).

0 views
iDiallo 4 weeks ago

The Modern Trap

Every problem, every limitation, every frustrating debug session seemed to have the same solution: Use a modern solution. Modern encryption algorithms. Modern deployment pipelines. Modern database solutions. The word modern has become the cure-all solution, promising to solve not just our immediate problems, but somehow prevent future ones entirely. I remember upgrading an app from PHP 5.3 to 7.1. It felt like it was cutting edge. But years later, 7.1 was also outdated. The application had a bug, and the immediate suggestion was to use a modern version of PHP to avoid this non-sense. But being stubborn, I dug deeper and found that the function I was using that was deprecated in newer versions, had an alternative since PHP 5.3. A quick fix prevented months of work rewriting our application. The word "modern" doesn't mean what we think it means. Modern encryption algorithms are secure. Modern banking is safe. Modern frameworks are robust. Modern infrastructure is reliable. We read statements like this every day in tech blogs, marketing copy, and casual Slack conversations. But if we pause for just a second, we realize they are utterly meaningless. The word "modern" is a temporal label, not a quality certificate. It tells us when something was made, not how well it was made. Everything made today is, by definition, modern. But let's remember: MD5 was once the modern cryptographic hash. Adobe Flash was the modern way to deliver rich web content. Internet Explorer 6 was a modern browser. The Ford Pinto was a modern car. "Modern" is a snapshot in time, and time has a cruel way of revealing the flaws that our initial enthusiasm blinded us to. Why do we fall for this? "Modern" is psychologically tied to "progress." We're hardwired to believe the new thing solves the problems of the old thing. And sometimes, it does! But this creates a dangerous illusion: that newness itself is the solution. I've watched teams chase the modern framework because the last one had limitations, not realizing they were trading known bugs for unknown ones. I've seen companies implement modern SaaS platforms to replace "legacy" systems, only to create new single points of failure and fresh sets of subscription fees. We become so busy fleeing the ghosts of past failures that we don't look critically at the path we're actually on. "Modern" is often just "unproven" wearing a better suit. I've embraced modern before, being on the very edge of technology. But that meant I had to keep up to date with the tools I use. Developers spend more time learning new frameworks than mastering existing ones, not because the new tools are objectively better, but because they're newer, and thus perceived as better. We sacrifice stability and deep expertise at the altar of novelty. That modern library you imported last week? It's sleek, it's fast, it has great documentation and a beautiful logo. It also has a critical zero-day vulnerability that won't be discovered until next year, or a breaking API change coming in the next major version. "Legacy" codebases have their problems, but they often have the supreme advantage of having already been battle-tested. Their bugs are known, documented, and patched. In the rush to modernize, we discard systems that are stable, efficient, and perfectly suited to their task. I've seen reliable jQuery implementations replaced by over-engineered React applications that do the same job worse, with more overhead and complexity. The goal becomes "be modern" instead of "be effective." But this illusion of "modern" doesn't just lead us toward bad choices; it can bring progress to a halt entirely. When we sanctify something as "modern," we subtly suggest we've arrived at the final answer. Think about modern medicine. While medical advances are remarkable, embedded in that phrase is a dangerous connotation: that we've reached the complete, final word on human health. This framing can make it difficult to question established practices or explore alternative approaches. Modern medicine didn't think it was important for doctors to wash their hands . The same happens in software development. When we declare a framework or architectural pattern "modern," we leave little room for the "next." We forget that today's groundbreaking solution is merely tomorrow's foundation or tomorrow's technical debt. Instead of modern, I prefer the terms "robust" or "stable". The most modern thing you can do is to look at any solution and ask: "How will this look obsolete in ten years?" Because everything we call "modern" today will eventually be someone else's legacy system. And that's not a bug, it's a feature. It's how progress actually works.

0 views
iDiallo 1 months ago

You are not going to turn into Google eventually

A few years back, I was running a CI/CD pipeline from a codebase that just kept failing. It pulled the code successfully, it passed the test, the docker image was built, but then it would fail. Each run took around 15 minutes to fail, meaning whatever change I made had to take at least 15 minutes before I knew if it was successful or not. Of course, it failed multiple times before I figured out a solution. When I was done, I wasn't frustrated with the small mistake I had made, I was frustrated by the time it took to get any sort of feedback. The code base itself was trivial. It was a microservice with a handful of endpoints that was only occasionally used. The amount of time it took to build was not proportional to the importance of the service. Well it took so long to build because of dependencies. Not the dependencies it actually used, but the dependencies it might use one day. The ones required because the entire build system was engineered for a fantasy future where every service, no matter how small, had to be pre-optimized to handle millions of users. This is the direct cost of building for a scale you will never reach. It’s the architectural version of buying a Formula 1 car to do your grocery shopping. It’s not just overkill, it actively makes the simple task harder, slower, and infinitely more frustrating. We operate under a dangerous assumption that our companies are inevitably on a path to become the next Google or Meta. So we build like they do, grafting their solutions onto our problems, hoping it will future-proof us. It won't. It just present-proofs us. It saddles us with complexity where none is needed, creating a drag that actually prevents the growth we're trying to engineer for. Here is why I like microservices. The concept is beautiful. Isolate a single task into a discrete, independent service. It’s the Unix philosophy applied to the web: do one thing and do it well. When a problem occurs, you should, in theory, be able to pinpoint the exact failing service, fix it, and deploy it without disrupting the rest of your application. If this sounds exactly how a simple PHP includes or a modular library works… you’re exactly right. And here is why I hate them. In practice, without Google-scale resources, microservices often create the very problems they promise to solve. You don’t end up with a few neat services; you end up with hundreds of them. You’re not in charge of maintaining all of them, and neither is anyone else. Suddenly, “pinpointing the error” is no longer a simple task. It’s a pilgrimage. You journey through logging systems, trace IDs, and distributed dashboards, hoping for an epiphany. You often return a changed man, older, wiser, and empty-handed. This is not to say to avoid microservices at all cost, but it's to focus on the problems you have at hand instead of writing code for a future that may never come. Don’t architect for a hypothetical future of billions of users. Architect for the reality of your talented small team. Build something simple, robust, and effective. Grow first, then add complexity only where and when it is absolutely necessary . When you're small, your greatest asset is agility. You can adapt quickly, pivot on a dime, and iterate rapidly. Excessive process stifles this inherent flexibility. It introduces bureaucracy, slows down decision-making, and creates unnecessary friction. Instead of adopting the heavy, restrictive frameworks of large enterprises, small teams should embrace a more ad-hoc, organic approach. Focus on clear communication, shared understanding, and direct collaboration. Let your processes evolve naturally as your team and challenges grow, rather than forcing a square peg into a round hole.

0 views
Karboosx 1 months ago

In-house parsers are easy!

Ever wanted to build your own programming language? It sounds like a huge project, but I'll show you it's not as hard as you think. In this post, we'll build one from scratch, step-by-step, covering everything from the Tokenizer and Parser to a working Interpreter, with all the code in clear PHP examples.

0 views
Grumpy Gamer 1 months ago

Comments are back

“But? Wait?” I can hear you saying, “Isn’t grumpygamer.com a static site built by Hugo? What dark magic did you use to get comments on the static site?” No dark magic. But it does involve a small php script. You can embed php in a hugo page and since grumpygamer.com is hosted on my server and it’s running php it wasn’t that hard. No tricky javascript and since it’s all hosted by me, no privacy issues. All your comments stay on my server and don’t feed Big Comment. Comments are stored in flat files so no pesky SQL databases. It only took me about a day, so all in all not bad. I may regret this. I’m only turning on comments for future posts. P.S. I will post the code and a small guide in a few days, so you too can invite the masses to critic and criticize your every word. Good times.

0 views
Brain Baking 1 months ago

Indispensable Cloud It Yourself Software: 2025 Edition

It’s been too long since this blog published a meaningless click-bait list article so here you go. Instead of simply enumerating frequently used apps such as app defaults from late 2023 , I thought it might be fun to zoom in on the popular self-hosted branch and summarize what we are running to be able to say Fuck You to the Big Guys. Below is a list of software that we depend on categorized by usage. I’m sure you can figure out for yourself how to run these in a container on your NAS. We still have a Synology, and while I strongly dislike the custom Linux distribution’s tendency to misplace configuration files, the DSM software that comes with it is good enough to cover a lot of topics. The list excludes typical Linux sysadmin stuff such as fail2ban, ssh key setup, Samba, … Photos : PhotoPrism . It comes with WebDAV support to easily sync photos from your phone. My wife’s iPhone uses PhotoSync which works flawlessly. I’d rather also use SyncThing on iOS like I do on Android (or the latest Android client fork). SyncThing is amazing and I use it for much more than photo syncing. Streaming videos : Synology’s built-in Video Station . It’s got a lot of flaws and Jellyfin is clearly the better choice here. As for how to get the videos on there: rip & tear using good old DVDShrink on the WinXP Retro Machine! We still use the old DS Video Android app on our smart box to connect to the NAS as we don’t have a smart TV. Streaming music : Navidrome —see How To Stream Your Own Music: Reprise for more info on which clients we use and why caching some albums locally is good enough. As for how to get the music on there: rip & tear using A Better CD Encoder ; or for Win98 lovers; WinGrab. Backups : Restic —see Verify Your Backup Strategy to see how this automatically works from multiple machines. Smart Home : Home Assistant with a HomeWizard P1 meter that monitors our gas/electricity usage locally instead of sending it god knows where. We only use the bare minimum features, I’m not a big Smart Home fan. I suppose WireGuard should also be in this category but for now I refuse to enable the possibility to dial home . Ads/DHCP : Pi-Hole . That wonderful piece of software blocks almost 15% of our daily traffic—see Six Months With A Pi-Hole . We also use it as a DHCP server to have more control over DNS. Wi-Fi : TP-Link Omada Controller that provisions and secures our access points locally instead of poking through the real cloud for no reason at all. Git : Gitea although I should really migrate to Forgejo. The NAS hosts my private projects, I have another instance on the VPS for public ones. RSS : FreshRSS . Until recently, just NetNewsWire as an RSS client did just fine but I sometimes caught myself doomscrolling on my phone so figured instead I’d scroll on other people’s blogs. NetNewsWire supports it so my reading behaviour doesn’t change on the computer. Pair with Readrops on Android that also caches entries so if I’m disconnected from the home network I can still read interesting stuff. I do not see the appeal of cloud-based office software so simply rely on LibreOffice to do its thing locally—no need for NextCloud, but it’s there if you want to. Speaking of which, I still use DEVONthink and of course Obsidian to manage my personal files/databases that hook into the above using SyncThing and Restic. Abandoned software: RSS Bridge (no longer needed), Watchtower (too complex for my simple setup), some kind of PHP-based accounting software I already forgot about. Software running publicly on the VPS: Radicale CardDAV/CalDAV server (I want this to be accessible outside of the NAS), another Gitea instance, Nginx (I really need to migrate to Caddy) et al. Related topics: / self-hosted / NAS / lists / By Wouter Groeneveld on 21 August 2025.  Reply via email .

0 views
James Stanley 3 months ago

The Story of Max, a Real Programmer

This is a story about Imagebin. Imagebin is the longest-lived software project that I still maintain. I'm the only user. I use it to host images, mainly to include in my blog, sometimes for sharing in other places. Imagebin's oldest changelog entry is dated May 2010, but I know it had already existed for about a year before I had the idea of keeping a changelog. Here's an image hosted by Imagebin: For years Imagebin was wide open to the public and anybody could upload their own images to it. Almost nobody did. But a couple of years ago I finally put a password on it during a paranoid spell. But actually this is not a story about Imagebin. This is a story about the boy genius who wrote it, and the ways of his craft. Lest a whole new generation of programmers grow up in ignorance of this glorious past, I feel duty-bound to describe, as best I can through the generation gap, how a Real Programmer wrote code. I'll call him Max, because that was his name. Max was a school friend of mine. He didn't like to use his surname on the internet, because that was the style at the time, so I won't share his surname. Max disappeared from our lives shortly after he went to university. We think he probably got recruited by MI6 or something. This weekend I set about rewriting Max's Imagebin in Go so that my server wouldn't have to run PHP any more. And so that I could rid myself of all the distasteful shit you find in PHP written by children 15+ years ago. I don't remember exactly what provoked him to write Imagebin, and I'm not even certain that "Imagebin" is what he called it. That might just be what I called it. I was struck by how much better Max's code is than mine! For all its flaws, Max's code is simple . It just does what it needs to do and gets out of the way. Max's Imagebin is a single 233-line PHP script, interleaving code and HTML, of which the first 48 lines are a changelog of what I have done to it since inheriting it from Max. So call it 185 lines of code. At school, Max used to carry around a HP 620LX in his blazer pocket. Remember this was a time before anyone had seen a smartphone. Sure, you had PDAs, but they sucked because they didn't have real keyboards. The HP 620LX was a palmtop computer , the height of coolness. My Go version is 205 lines of code plus a 100-line HTML template, which is stored in an entire separate file. So call it 305 lines plus complexity penalty for the extra file. And my version is no better! And my version requires a build step, and you need to leave the daemon running. With Max's version you just stick the PHP file on the server and it runs whenever the web server asks it to. And btw this is my third attempt at doing this in Go. I had to keep making a conscious effort not to make it even more complicated than this. And some part of me doesn't even understand why my Go version is so much bigger. None of it looks extraneous. It has a few quality-of-life features, like automatically creating the directories if they don't already exist, and supporting multiple files in a single upload, But nothing that should make it twice as big. Are our tools just worse now? Was early 2000s PHP actually good? While I was writing this, I noticed something else: Max's code doesn't define any functions! It's just a single straight line. Upload handling, HTML header, thumbnail code, HTML footer. When you put it like that, it's kind of surprising that it's so large . It hardly does anything at all! Max didn't need a templating engine, he just wrote HTML and put his code inside <?php tags. Max didn't need a request router, he just put his PHP file at the right place on the disk. Max didn't need a request object, he just got query parameters out of $_GET . Max didn't need a response writer, he just printed to stdout . And Max didn't need version control, he just copied the file to index.php.bak if he was worried he might want to revert his changes. You might think that Max's practices make for a maintenance nightmare. But I've been "maintaining" it for the last 15 years and I haven't found it to be a nightmare. It's so simple that nothing goes wrong. I expect I'd have much more trouble getting my Go code to keep running for the next 15 years. And yeah we all scoff at these stupid outdated practices, but what's our answer? We make a grand effort to write a simpler, better, modern replacement, and it ends up twice as complicated and worse? The reason the Go code is so much bigger is because it checks and (kind of) handles errors everywhere (?) they could occur. The PHP code just ignores them and flies on through regardless. But even if you get rid of checking for the more unlikely error cases, the Go version is longer. It's longer because it's structured across several functions, and with a separate template. The Go version is Designed . It's Engineered . But it's not better . I think there are lessons to (re-)learn from Max's style. You don't always have to make everything into a big structure with lots of moving parts. Sometimes you're allowed to just write simple straight-line code. Sometimes that's fine. Sometimes that's better. Longevity doesn't always come from engineering sophistication. Just as often, longevity comes from simplicity . To be perfectly honest, as a teenager I never thought Max was all that great at programming. I thought his style was overly-simplistic. I thought he just didn't know any better. But 15 years on, I now see that the simplicity that I dismissed as naive was actually what made his code great. Whether that simplicity came from wisdom or from naivety doesn't matter. The result speaks for itself. So I'm not going to bother running my Go version of the Imagebin. I'm going to leave Max's code in place, and I'm going to let my server keep running PHP. And I think that's how it should be. I didn't feel comfortable hacking up the code of a Real Programmer.

0 views
James Stanley 11 months ago

Prompts as source code: a vision for the future of programming

I'm going to present a vision for the future of programming in which programmers don't work with source code any more. The idea is that prompts will be to source code as source code is to binaries. In the beginning (I claim) there were only binaries, and without loss of generality, assembly language. (If you think binaries and assembly language are too far apart to lump together: keep up grandad, you're thinking too low-level; just wait until the further future where source code and binaries are too close together to distinguish!). Then somebody invented the compiler . And now it was possible to write code in a more natural language and have the machine automatically turn it into binaries! And we saw that it was good. As hardware resources grew, the compilers' capabilities grew, and now the idea that there was programming before compilers is pretty weird to new developers. Almost noone is writing assembly language and even fewer write bare machine code. Now take LLMs. If you create software using an LLM today, you probably give an initial prompt to get started, and then you refine the generated source code by giving follow-up prompts to ask for changes, and you never revisit your initial prompt. It's just a series of "patches" created by follow-up prompts. This is like programming by writing source code once, compiling it, and then throwing the source code away and working directly on the binary with incremental patches! Which is just obviously crazy. So here's my outline for "prompts as source code": The prompts will be committed to git, the generated source code will not. The prompts will be big, and split across multiple files just like source code is now, except it's all freeform text. We just give the LLM a directory tree full of text files and ask it to write the program. The prompts will be unimaginably large by today's standards. Compare the size of the Linux or Firefox source trees to the total amount of machine code that had ever been written in the entire history of the world before the first compiler was invented. (To spell it out: the future will contain LLM prompts that are larger than all of the source code that humanity combined has ever written in total up to this point in time.) Our build system will say which exact version of the LLM you're using, and it will be evaluated deterministically so that everybody gets the same output from the same prompt (reproducible builds). The LLMs will be bigger than they are today, have larger context windows, etc., and as the LLMs improve, and our understanding of how to work with them improves, we'll gain confidence that small changes to the prompt have correspondingly small changes in the resulting program. It basically turns into writing a natural language specification for the application, but the specification is version-controlled and deterministically turns into the actual application. Human beings will only look at the generated source code in rare cases (how often do you look at assembly code today?). Normally they'll just use their tooling to automatically build and run the application directly from the prompts. You'll be able to include inline code snippets in the prompt, of course. That's a bit like including inline assembly language in your source code. And you could imagine the tooling could let you include some literal code files that the LLM won't touch, but will be aware of, and will be included verbatim in the output. That's a bit like linking with precompiled object files. Once you have a first version that you like, there could be a "backwards pass" where an LLM looks at the generated source code and fills in all the gaps in the specification to clarify the details, so that if you then make a small change to the prompt you're more likely to get only a small change in the program. You could imagine the tooling automatically running the backwards pass every time you build it, so that you can see in your prompts exactly what assumptions you're baking in. That's my vision for the future of programming. Basically everything that today interacts with source code and/or binaries, we shift one level up so that it interacts with prompts and/or source code. What do you think? Although we could make an initial stab at the tooling today, I feel like current LLMs aren't quite up to the job: context windows are too small for all but toy applications (OK, you might fit your spec in the context window, but you also want the LLM to do some chain-of-thought before it starts writing code) as far as I know, it's not possible to run the best LLMs (Claude, gpt4o) deterministically, and even if it was they are cloud-hosted and proprietary, and that is an extremely shaky foundation for a new system of programming you could use Llama 405b, but GPUs are too expensive and too slow we'd need the LLMs to be extraordinarily intelligent and able to follow every tiny detail in the prompt, in order for small changes to the prompt not to result in random bugs getting switched on/off, the UI randomly changing, file formats randomly changing, etc. I haven't quite figured out how you "update" the LLM without breaking your program; you wouldn't want to be stuck on the same version forever, this feels similar to but harder than the problem of switching PHP versions for example

0 views
W. Jason Gilmore 1 years ago

Technical Due Diligence - Relational Databases

Despite the relative popularity of NoSQL and graph databases, relational databases like MySQL, SQL Server, Oracle, and PostgreSQL continue to be indispensable for storing and managing software application data. Because of this, technical due diligence teams are practically guaranteed to encounter them within almost any project. Novice team members will gravitate towards understanding the schema which is of course important but only paints a small part of the overall risk picture. A complete research and risk assessment will additionally include information about the following database characteristics: I identify these three characteristics because technical due diligence is all about identifying and quantifying risk assessment , and not about nerding out over the merit of past decisions. The importance of quantifying risk assessment is in most cases no greater than when evaluating the software product's data store, for several reasons: Be sure to confirm all database licenses are in compliance with the company's use case, and if the database is commercially licensed you'll need to additional confirm the available features and support contract are in line with expectations. To highlight the importance of this verification work I'll point out a few ways in which expectations might not be met: All mainstream databases (MySQL, Oracle, PostgreSQL, etc) will have well-defined end-of-life (EOL) dates associated with each release. The EOL date identifies the last date in which that particular version will receive security patches. Therefore it is critical to determine what database versions are running in production in order to determine whether the database has potentially been running in an unpatched state. For instance MySQL 5.7 has an EOL date of October 25, 2023, and therefore if the seller's product is still running MySQL 5.7 after that date then it is in danger of falling prey to any vulnerabilities identified after that EOL date. Of course, the EOL date isn't the only issue at play here. If the database version hasn't reached its EOL date then you should still determine whether the database has been patched appropriately. For instance as of the time of this writing MySQL 8.2 was released only 9 months ago (on October 12, 2023) and there are already 11 known vulnerabilities . It's entirely possible that none of these vulnerabilities are exploitable in the context of the seller's product, however its nonetheless important to catalog these possibilities and supply this information to the buyer. In my experience where there is smoke there is fire and unpatched software is often symptomatic of much larger issues associated with technical debt and a lack of developer discipline. Enterprise web applications will typically run in an N-Tier architecture, meaning the web, data, caching, and job processing components can all be separately managed and scaled. This configuration means each tier will often run on separate servers and therefore a network connection between the database and web application tiers will need to be configured. Most databases can be configured to allow for connections from anywhere (almost invariably a bad idea), which is precisely what you don't want to see when that database is only intended to be used by the web application because it means malicious third-parties have a shot at successfully logging in should they gain access to or guess the credentials. Connecting users will be associated with a set of privileges which define what the user can do once connected to the database. It is considered best practice to assign those users the minimum privileges required to carry out their tasks. Therefore a database user which is intended to furnish information to a data visualization dashboard should be configured with read-only privileges, whereas a customer relationship management (CRM) application would require a database user possessing CRUD (create, retrieve, update, delete) database privileges. Therefore when examining database connectivity and privileges you should at a minimum answer the following questions: Satisfying this review requirement is relatively straightforward, and completed in two steps: Performance Disaster Recovery Poor security practices open up the possibility of application data having already been stolen, or in danger of being imminently stolen, placing the buyer in legal danger. Poor performance due to inadequate or incorrect indexing, insufficient resourcing, or a combination of the two might result in disgruntled customers who are considering cancelling their subscription. Some of these customers may be major contributors to company revenue, severely damaging the company's outlook should they wind up departing following acquisition. A lack of disaster recovery planning outs the buyer in greater short-term risk following acquisition due to an outage which may occur precisely at a time when personnel are not available or are not entirely up to speed. The buyer requires the data to be encrypted at-rest due to regulatory issues, however the product data is in fact not encrypted-at-rest due to use of the Heroku Essential Postgres tier which does not offer this feature. There could possibly be an easy fix here which involves simply upgrading to a tier which does support encryption-at-rest, however you should receive vendor confirmation (in writing) that encryption is indeed possible as a result of upgrading, and whether any downtime will be required to achieve this requirement. The buyer's downtime expectations are more strict than what is defined by the cloud service provider's SLA. TODO TALK ABOUT MEMSQL What users are defined and active on the production databases, and from what IP addresses / hostnames are they accessible? Is the database server accessible to the wider internet and if so, why? What privileges do the defined database users possess, and why? To what corporate applications are production databases connected? This includes the customer-facing application, business intelligence software, backup services, and so forth. What other non-production databases exist? Where is production data replicated? Are these destinations located within jurisdictions compliant by the laws and SLA under which the buyer's target IP operates? From a security standpoint, data is often defined as being encrypted at-rest and in-transit , the former referring to its encryption state when residing in the database or on server, and the latter referring to its encryption state when being transferred from the application to the requesting user or service. You'll want to determine whether these two best practices are implemented. If the data is not encrypted at-rest (which is typical and not necessarily an issue for many use cases), then how is sensitive data like passwords encrypted (or hashed)? You often won't be able to determine this by looking at the database itself; web frameworks will typically dictate the password hashing scheme, such as Laravel's use of the Bcrypt algorithm for this purpose.

0 views
W. Jason Gilmore 1 years ago

Minimal SaaS Technical Due Diligence

For more than six years now I've been deeply involved in and in recent years leading Xenon Partners ' technical due diligence practice. This means that when we issue an LOI (Letter of Intent) to acquire a company, it's my responsibility to dig deep, very deep, into the often arcane technical details associated with the seller's SaaS product. Over this period I've either been materially involved in or led technical due diligence for DreamFactory , Baremetrics , Treehouse , Packagecloud , Appsembler , UXPin , Flightpath Finance , as well as several other companies. While I've perhaps not seen it all, I've seen a lot, and these days whenever SaaS M&A comes up in conversation, I tend to assume the thousand-yard stare , because this stuff is hard . The uninitiated might be under the impression that SaaS technical due diligence involves "understanding the code". In reality, the code review is but one of many activities that must be completed, and in the grand scheme of things I wouldn't even put it in the top three tasks in terms of priority. Further complicating the situation is the fact that sometimes due to circumstances beyond our control we need to close a deal under unusually tight deadlines, meaning it is critically important that this process is carried out with extreme efficiency. Due to the growing prevalence of SaaS acquisition marketplaces like Acquire.com and Microassets , lately I've been wondering what advice I would impart to somebody who wants to acquire a SaaS company yet who possesses relatively little time, resources, and money. What would be the absolute minimum requirements necessary to reduce acquisition risk to an acceptable level? This is a really interesting question, and I suppose I'd focus on the following tasks. Keep in mind this list is specific to the technology side of due diligence; there are also financial, operational, marketing, legal, and HR considerations that also need to be addressed during this critical period. I am not a lawyer, nor an accountant, and therefore do not construe anything I say on this blog as being sound advice. Further, in this post I'm focused on minimal technical due diligence here, and largely assuming you're reading this because you're interested in purchasing a micro-SaaS or otherwise one run by an extraordinarily small team. For larger due diligence projects there are plenty of other critical tasks to consider, including technical team interviews. Perhaps I'll touch upon these topics in future posts. Please note I did not suggest asking for architectural diagrams. Of course you should ask for them, but you should not believe a single thing you see on the off chance they even exist. They'll tell you they do exist, but they likely do not. If they do exist, they are almost certainly outdated or entirely wrong. But I digress. On your very first scheduled technical call, open a diagramming tool like Draw.io and ask the seller's technical representative to please begin describing the product's architecture. If they clam up or are unwilling to do so (it happens), then start drawing what you believe to be true, because when you incorrectly draw or label part of the infrastructure, the technical representative will suddenly become very compelled to speak up and correct you. These diagrams don't have to be particularly organized nor aesthetically pleasing; they just need to graphically convey as much information as possible about the application, infrastructure, third-party services, and anything else of relevance. Here's an example diagram I created on Draw.io for the purposes of this post: Don't limit yourself to creating a single diagram! I suggest additionally creating diagrams for the following: We have very few requirements that if not met will wind up in a deal getting delayed or even torpedoed, however one of them is that somebody on our team must successfully build the development environment on their local laptop and subsequently successfully deploy to production. This is so important that we will not acquire the company until these two steps are completed . These steps are critical because in completing them you confirm: Keep in mind you don't need to add a new feature or fix a bug in order to complete this task (although either would be a bonus). You could do something as simple as add a comment or fix a typo. Keep in mind at this phase of the acquisition process you should steadfastly remain in "do no harm" mode, and are only trying to confirm your ability to successfully deploy the code, not make radical improvements to the code. This isn't strictly a technical task, but it's so important that I'm going to color outside the lines and at least mention it here. The software product you are considering purchasing is almost unquestionably built atop the very same enormous open source (OSS) ecosystem upon which our entire world has benefited. There is nothing at all wrong with this, and in fact I'd be concerned if it wasn't the case, however you need to understand that there are very real legal risks associated with OSS licensing conflicts. As I've already made clear early in this post, I am not a lawyer so I'm not going to offer any additional thoughts regarding the potential business risks other than to simply bring the possibility to your attention. The software may additionally very well rely upon commercially licensed third-party software, and it is incredibly important that you know whether this is the case. If so, what are the terms of the agreement? Has the licensing fee already been paid in full, or is it due annually? What is the business risk if this licensor suddenly triples fees? There are actually a few great OSS tools that can help with dependency audits. Here are a few I've used in the past: That said for reasons I won't go into here because again, IANAL, it is incumbent upon the seller to disclose licensing issues . The buyer should only be acting as an auditor, and not the investigatory lead with regards to potential intellectual property conflicts. You should always retain legal counsel for these sorts of transactions. Finally, if the software relies on third-party services (e.g OpenAI APIs) to function (it almost certainly does), many of the same aforementioned questions apply. How critical are these third-party services? At some point down the road could you reasonably swap them out for a better or more cost-effective alternative? A penetration test (pen test) is an authorized third-party cybersecurity attack on a computer system. In my experience for SaaS products these pen tests cost anywhere between $5K and $10K and take 1-2 weeks to complete once scheduled. A lengthy report is typically delivered by the pen tester, at which point the company can dispute/clarify the findings or resolve the security issues and arrange for a subsequent test. Also in my experience, if you're interested in purchasing a relatively small SaaS with no employees other than the founder, it's a practical certainty the product has never been pen tested. Further, if the SaaS is web-based and isn't using a web framework such as Ruby on Rails or Laravel, for more reasons than I could possibly enumerate here I'd be willing to bet there are gaping security holes in the product (SQL injection, cross-site scripting attack, etc) which may have already been compromised. Therefore you should be sure to ask if a pen test has recently been completed, and if so ask for the report and information about any subsequent security-related resolutions. If one has not been completed, then it is perfectly reasonable to ask (in writing) why this has not been the case, and whether the seller can attest to the fact that the software is not known to have been compromised. If the answers to these questions are not satisfactory, then you might consider asking the seller to complete a pen test, or ask if you can arrange for one on your own dime. If you're sufficiently technical and have a general familiarity of cybersecurity concepts such as the OWASP Top Ten , then you could conceivably lower the costs associated with this task by taking a DIY approach. Here is a great list of bug bounty tools that could be used for penetration test purposes. That said, please understand that you should in no circumstances use these tools to test a potential seller's web application without their written permission! If you think the SaaS you're considering buying doesn't have any technical debt, then consider the fact that even the largest and most successful software products in the world are filled with it: That said, due to perfectly reasonable decisions made literally years ago, it is entirely possible that this "UI change" isn't fixable in 3 months, let alone 3 days. And there is a chance it can't ever be reasonably fixed, and anybody who has built sufficiently complicated software is well aware as to why. Technical debt is a natural outcome of writing software, and there's nothing necessarily wrong with it provided your acknowledge its existence and associated risks. But there are limits to risk tolerances, and if the target SaaS is running on operating systems, frameworks, and libraries that have long since been deprecated and are no longer able to receive maintenance and security updates, then I think it is important to recognize that you're probably going to be facing some unwelcome challenges in the near term as you update the software and infrastructure instead of focusing on the actual business. Of everything that comprises technical due diligence there is nothing that makes me break out into a sweat more than this topic. Any SaaS product will rely upon numerous if not dozens of credentials. GSuite, AWS, Jenkins, Atlassian, Trello, Sentry, Forge, Twitter, Slack... this list is endless. Not to mention SSH keys, 2FA settings, bank accounts, references to founder PII such as birthdates and so forth. In a perfect world all of this information would be tidily managed inside a dedicated password manager, but guess what it's probably not. I cannot possibly impress upon you in this post how important it is for you to aggressively ask for, review, and confirm access to everything required to run this business because once the paperwork is signed and money transferred, it's possible the seller will be a lot less responsive to your requests. Ensuring access to all credentials is so critical that you might consider structuring the deal to indicate that part of the purchase price will be paid at some future point in time (90 days from close for example) in order to ensure the founder remains in contact with you for a period of time following acquisition. This will give you the opportunity to follow up via email/Zoom and gain access to services and systems that were previously missed. This blog post barely scratches the surface in terms of what I typically go through during a full-fledged due diligence process, but I wanted to give interested readers a basic baseline understanding of the minimum requirements necessary to assuage my personal levels of paranoia. If you have any questions about this process, feel free to hit me up at @wjgilmore , message me on LinkedIn , or email me at [email protected] . Cloud infrastructure: For instance if the seller is using AWS then try to identify the compute instance sizes, RDS instances, security groups, monitoring services, etc. The importance of diagramming the cloud infrastructure becomes even more critical if Kubernetes or other containerized workload solutions are implemented, not only due to the additional complexity but also because frankly in my experience these sorts of solutions tend to not be implemented particularly well. Deployment strategy: If CI/CD is used, what does the deployment process look like? What branch triggers deployments to staging and production? Is a test suite run as part of the deployment process? How is the team notified of successful and failed deployments? You're able to successfully clone the (presumably private) repository and configure the local environment. You're able to update the code, submit a pull request, and participate in the subsequent review, testing, and deployment process (if one even exists) You've gained insights into what technologies, processes, and services are used to manage company credentials, build the development environment, merge code into production, run tests, and trigger deployments. LicenseFinder

0 views
W. Jason Gilmore 1 years ago

Disabling SSL Validation in Bruno

I use Laravel Herd to manage local Laravel development environments. Among many other things, it can generate self-signed SSL certificates. This is very useful however modern browsers and other HTTP utilities tend to complain about these certificates. Fortunately it's easy to disable SSL validation by opening Bruno, navigating to under the menu heading, and unchecking .

0 views
W. Jason Gilmore 2 years ago

Blitz Building with AI

In August, 2023 I launched two new SaaS products: EmailReputationAPI and BlogIgnite . While neither are exactly moonshots in terms of technical complexity, both solve very real problems that I've personally encountered while working at multiple organizations. EmailReputationAPI scores an email address to determine validity, both in terms of whether it is syntactically valid and is deliverable (via MX record existence verification), as well has the likelihood there is a human on the other end (by comparing the domain to a large and growing database of anonymized domains). BlogIgnite serves as a writing prompt assistant, using AI to generate a draft article, as well as associated SEO metadata and related article ideas. Launching a SaaS isn't by itself a particularly groundbreaking task these days, however building and launching such a product in less than 24 hours might be somewhat more notable accomplishment. And that's exactly what I did for both products, deploying MVPs approximately 15 hours after writing the first line of code. Both are written using the Laravel framework , a technology I happen to know pretty well . However there is simply no way this self-imposed deadline would have been met without leaning heavily on artificial intelligence. I am convinced AI coding assistants are opening up the possibility of rapidly creating, or blitzbuilding , new software products. The goal of blitzbuilding is not to create a perfect or even a high-quality product! Instead, the goal is to minimize business risk incurred via a prolonged development cycle by embracing AI to assist with the creation of a marketable product in the shortest amount of time. The term blitzbuilding is a tip of the cap to LinkedIn founder Reid Hoffman's book, "Blitzscaling: The Lightning-Fast Path to Building Massively Valuable Companies", in which he describes techniques for growing a company as rapidly as possible. The chosen technology stack isn't important by itself, however it is critical that you know it reasonably well otherwise the AI will give advice and offer code completions that can't easily be confirmed as correct. In my case, EmailReputationAPI and BlogIgnite are built atop the Laravel framework, use the MySQL database, with Redis used for job queuing. They are hosted on Digital Ocean, and deployed with Laravel Forge. Stripe is used for payment processing. The common thread here is I am quite familiar with all of these technologies and platforms. Blitz building is not a time for experimenting with new technologies, because you will only get bogged down in the learning process. The coding AI is GitHub Copilot with the Chat functionality. At the time of this writing the Chat feature is only available in a limited beta, but it is already extraordinarily capable insomuch that I consider it indispensable. Among many things it can generate tests, offer feedback, and even explain highlighted code. GitHub Copilot Chat runs in a VS Code sidebar tab, like this: Notably missing from these products is JavaScript (to be perfectly accurate, there are miniscule bits of JavaScript found on both sites due to unavoidable responsive layout behavior) and custom CSS. I don't like writing JavaScript, and am terrible at CSS and so leaned heavily on Tailwind UI for layouts and components. 24 hours will be over before you know it, and so it is critical to clearly define the minimum acceptable set of product requirements. Once defined, cut the list in half, and then cut it again. To the bone. Believe me, that list should be much smaller than you believe. For EmailReputationAPI, that initial list consisted of the following: There are now plenty of additional EmailReputationAPI features, such as a Laravel package , but most didn't make the list critical to the first release and so were delayed. It is critical to not only understand but be fine with the fact you will not be happy with the MVP . It won't include all of the features you want, and some of the deployed features may even be broken. It doesn't matter anywhere near as much as you think. What does matter is putting the MVP in front of as many people as possible in order to gather feedback and hopefully customers. I hate CSS with the heat of a thousand suns, largely because I've never taken the time to learn it well and so find it frustrating. I'm also horrible at design. I'd imagine these traits are shared by many full stack developers, which explains why the Bootstrap CSS library was such a huge hit years ago, and why the Tailwind CSS framework is so popular today. Both help design-challenged developers like myself build acceptable user interfaces. That said, I still don't understand what most of the Tailwind classes even do, but fortunately GitHub Copilot is a great tutor. Take for example the following stylized button: I have no idea what the classes , , etc do, but can ask Copilot chat to explain: I also use Copilot chat to offer code suggestions. One common request pertains to component alignment. For instance I wanted to add the BlogIgnite logo to the login and registration forms, however the alignment was off: I know the logo should be aligned with Tailwind's alignment classes, but have no clue what they are nor do I care. So after adding the logo to the page I asked Copilot . It responded with: After updating the image classes, the logo was aligned as desired: Even when using the AI assistant the turnaround time is such that your product will inevitably have a few bugs. Focus on fixing what your early users report, because those issues are probably related to the application surfaces other users will also use. Sometime soon I'd love to experiment with using a local LLM such as Code Llama in conjunction with an error reporting tool to generate patches and issue pull requests. At some point in the near future I could even see this process as being entirely automated, with the AI additionally writing companion tests. If those tests pass the AI will merge the pull request and push to production, with no humans involved! Have any questions or thoughts about this post? Contact Jason at [email protected] . Marketing website Account management (register, login, trial, paid account) Crawlers and parsers to generate various datasets (valid TLDs, anonymized domains, etc) Secure API endpoint and documentation Stripe integration

0 views
W. Jason Gilmore 4 years ago

Laravel Jetstream: Changing the Login Redirection URL

By default Laravel Jetstream will redirect newly logged in users to the route. A side project I'm building didn't have a need for a general post-authentication landing page, and so I needed to figure out how to redirect the user elsewhere. Turns out it's pretty easy. Open and locate this line: Change it to: Of course you'll want to swap out with your desired route name. You can retrieve a list of route names for your application by running:

0 views
Neil Madden 4 years ago

How do you use a bearer URL?

In “Towards a standard for bearer token URLs” , I described a URL scheme that can be safely used to incorporate a bearer token (such as an OAuth access token) into a URL. That blog post concentrated on the technical details of how that would work and the security properties of the scheme. But as Tim Dierks commented on Twitter , it’s not necessarily obvious to people how you’d actually use this in practice. Who creates these URLs? How are they used and shared? In this follow-up post I’ll attempt to answer that question with a few examples of how bearer URLs could be used in practice. A classic use-case for these kinds of URLs with an authorization token in them are the links that get sent to your email address when you ask to reset your password on some online service. This is a simple way of confirming that the person requesting the reset at least has access to your email inbox. (I won’t go into the security pros and cons of this, just that it is a very widespread pattern). Typically such a link looks something like With widespread support for bearer URLs this would simply become This is already adding security benefits as the token will be sent in an Authorization header when the linked is clicked rather than in an easily-leaked query parameter. Nothing else about the scenario has to change: the server still generates tokens exactly as it currently does and processes them the same way. The same applies to other scenarios that already use unguessable URLs for (partial) access control, such as Google Drive link sharing or Dropbox chooser/saver expiring file links . In a world where bearer URLs were widely implemented (or a good polyfill available) then these services could simply move to using them instead of encoding authorization tokens directly into https URLs and benefit from the security advantages without really changing the fundamental architecture much at all. Many people already use OAuth to control access to APIs and other resources on the web, so it makes a good example of how bearer URLs would work in practice when access is not already URL-based. With OAuth, a client (such as a JavaScript single page app) obtains consent from a user to access their data and gets an access token that it can then use to call APIs to retrieve that data. In the most basic integration of bearer URLs, the Authorization Server (AS) would return a bearer URL instead of (or as well as) a “bare” access token, something like the following: The client can then simply issue a GET request to that URL and their bearer-URL-aware HTTP client library will extract the access token and send it as an Authorization: Bearer header automatically. If web browsers adopted the bearer URL scheme then using OAuth to make API calls becomes as simple as just (or your fancy-pants async/await version). That’s the most basic use-case. Hopefully it’s clear that use of bearer URLs here doesn’t really change how things like OAuth operate at a fundamental level. This is a conscious design principle of the bearer URL scheme: adopting it shouldn’t require an enormous shift in architecture or complete rewrite of all the things. I’m not advocating that we boil the oceans and radically reinvent all web security overnight. In many cases, an access token authorizes access to multiple different APIs. For example, Google Cloud docs list hundreds of APIs that a client can be authorized to access, each with one or more associated OAuth scopes. In principle you can get a single access token that authorizes access to every single one of these APIs. Although this is unlikely in the case of Google Cloud Platform, it is certainly the case that developers often prefer to obtain a single access token that grants access to multiple APIs to avoid having to juggle multiple tokens and keep track of which token is meant for which API. To put it mildly, this is not great from a security point of view. For example, it is quite common to request OpenID Connect scopes in addition to other API scopes in a single request, allowing the client to authenticate the user and gain access to APIs in a single call. But this means that every API they call with that access token can turn around and use it to access the user’s profile information from the OIDC UserInfo endpoint . Maybe this is intended, but it’s more often accidental. You can solve this problem by getting multiple finer-scoped access tokens, for example using a background refresh token flow (you request every scope in the authorization call and then use the refresh token to get individual access tokens for subsets of that scope). My colleagues at ForgeRock created a nice JavaScript library to do just this and handle the necessary juggling of access tokens for each API endpoint. With bearer URLs, the token encoded into the URL is only intended to be used to access the one particular endpoint identified by the URL. So, if access is granted to multiple APIs then the AS would have to return multiple bearer URLs. On the face of it, this sounds like a nightmare for a developer: they’d be forced to juggle all these URLs for different APIs. But if you think about it, developers already have to juggle different URLs for different APIs because separate APIs almost always have different URLs anyway. There is also often a very clear relationship between OAuth scopes and API URLs. For example, if you look again at the Google scope list , you can see that most of the scope are URLs (or at least URL-like), and each scope grants access to a small handful of related API URLs—often just one URL. It would be quite natural for the AS to return a bearer URL for each scope, granting access to that particular API: From a developer’s point of view, this seems more convenient: not only do I get finer-grained access tokens (better for security), but the AS has actually told me where these APIs are, so I don’t need to go trawling through voluminous API documentation. I just have to follow the links. (You can see an idea similar to this in GNAP’s support for returning multiple access tokens ). You may be thinking, what if the access token grants access to a large collection of URLs? Good question! For example, suppose Dropbox wanted to provide bearer URL access to individual files in the user’s storage. Surely the AS can’t return hundreds of URLs in the response, one for each file? This sounds really messy. But if you think about it, web developers already have a solution for this problem. If I want to access a user’s files I will first access a top-level URL that lists all the files, say https://api.dropboxapi.com/2/files/list_folder and then that will return details of the individual files and URLs to access those files (or enough information to be able to construct such URLs). The REST aficionados have a (terrible) name for this: HATEOAS , which is really just the idea that navigating an API should be driven by links rather than knowledge of API endpoints “baked-in” to every client. This has obvious benefits in terms of loose coupling between clients and servers. In a world where the authority to access a resource comes from a bearer URL, then Dropbox would return bearer URLs as the links to access individual files. So the client would get a single bearer URL back from the AS that provides access to the list_folder endpoint. The response from that endpoint would then contain further bearer URLs to access individual files. I go into more detail about this in chapter 9 of my book , with worked code examples (a semi-shameless plug but I did spend a lot of time thinking through those examples). So how does the API construct these new bearer URLs? Won’t it need to keep calling back into the AS to get new access tokens? Will the user be involved every time? In short, no. In the simplest implementation of this approach a single access token continues to provide access to all files in a particular folder. When creating the links for access to each individual file, the server simply copies the same access token from the request into the bearer URL for the file. For example, the request: Results in a response that looks something like this: The same token is used to authorize access to every file, which is how most OAuth deployments and session cookies work today. If you’ve done web development for a while, you may recognise this as a similar pattern to the URL rewriting done by frameworks like Java Servlets ( JSESSIONID ) or PHP ( PHPSESSID ) to keep track of a user session without cookies. These approaches are discouraged these days because of the risk of session fixation . Session fixation attacks don’t apply to the intended use of bearer URLs because the token in the URL doesn’t establish (or alter) any session state on the server. Anything the attacker could gain access to by getting a user to click on a link they could just as easily access themselves through the same link. But it does highlight that if a token is used in a bearer URL then the access granted by that token should never increase after the token is created. A more sophisticated implementation would generate fresh tokens for each of the links created in response to a request. Each new token would provide access to just that one resource (file). This is more secure because if any one bearer URL leaks or is stolen then the access gained by the attacker is more limited, whereas in the previous implementation an attacker could extract the token and craft different URLs using it. Creating new tokens can be expensive, but you can use techniques such as Macaroons to derive more restricted tokens from the request token, as I previously described . For example, if the original token provides read-only access to every file in a particular folder then the server can derive tokens for each individual file by appending a caveat restricting access to that one file, as is the case in these two example URLs: The second URL was efficiently derived from the first without needing access to any shared secret key or database access. Although by no means essential to the use of bearer URLs, Macaroons do fit remarkably well with this approach. Hopefully this blog has clarified how bearer URLs could be incorporated into existing or new web applications. In many cases only very minimal changes would be needed, with raw bearer tokens replaced with bearer URLs to gain the security benefits described in the last blog post . In some cases, such as password reset links, they could be adopted with hardly any changes at all. New frameworks could be developed that offer more comprehensive integration of bearer URLs along the lines I’ve described above. Such frameworks would be closer to implementing the capability-security discipline of pioneering frameworks like Waterken , and I believe this would be a positive development for the web. But I’ve refrained from using that terminology in these two articles, because I think bearer URLs have many positive security and usability advantages even if you completely reject the arguments for capability security, or think it is too radical a change from existing security architectures. My aim is to show that bearer URLs are flexible and can compliment and enhance mainstream approaches to application security too.

0 views
codedge 4 years ago

Auto-generate a social media image in Statamic

When writing blog posts you likely want to share your post on Twitter or any other social network to give your posts an audience. Let’s stay with Twitter for this post. While writing blog posts might be the main task, the title and content is clear, there is often an image used to and pulled by social media. Instead of searching the web for meaningful backgrounds, why not just putting the title of your post and the name of your blog on an uni-color background. For creating this image we need to hook into Statamics event system to generate the image after saving the blog post. To create a new listener that actually runs the image generation we run which creates a new listener at . Next the listener needs to be run, when an entry is saved. For this the EventSaved event comes in handy. So let’s register our listener by putting in it . Now, whenever an entry is saved, our listener is called and can do its magic, generating a pixel image for Twitter. For creating the images I am going to use the Intervention Image library . Back to our listener file we need to load the Intervention Image library and generate the image. Here is complete file: The requirement for Intervention is PHPs extension. Alternatively you can use gd too. I restricted the generation to: I suspect there is more than one libray to use for this use-case, but I am pretty happy with Intervention. I never used it before, but it seems to be stable and does exactly what it should do. Imagick or GD? First I started with using but it turned out I cannot properly render text. When using gd you can specify built-in fonts 1 to 5, which wasn’t my intention. I wanted to use my own font. With I was able to render text properly with a custom file. posts in the collections only published items

0 views
codedge 4 years ago

Run Statamic on Vercel

When Tailwindcss 2 was released I decided to give my blog a new update and also write about some more topics I had in mind. The blog ran on Hugo hosted on Netlify. While I never had issues with Netlify for some reasons Hugo wasn’t just not made for me. It took me a fair amount of time to get Tailwindcss 2 up and running there, then I found some issues with generating my content,… all in all not a refreshing experience when you just want to write some new blog posts. In the meantime I feel in love with Statamic as I used it in another freelance project, so I thought why not give it a try for my own blog. There are some very good blog post by Rias about running Statamic 2 and 3 on Netlify that I took as a base. When converting my blog from Hugo to Statamic and changing settings on Netlify I ran into bigger problems getting all the php composer packages installed in Netlify. The error gave me a bad day and I eventually found others had issues ( Github issue ) with this as well. While it seemed to be a problem with Composer 2 in my case I just wasn’t able to get this fixed. Even an update from Composer 2.0.7 to 2.0.8 did not help. Unfortunately you cannot specify the version of Composer running when Netlify builds your blog. Vercel , which is the former ZEIT , is an equally opponent to Netlify build JAMSTACK application. There are already huge comparisons between these two ( Google ) so I won’t bother you with this. You need to use Statamic SSG package to publish your site on Vercel. There is a separate section describing which steps to take, including a build script, and what settings you need. Just fantastic. Additionally I found some more settings can be set using a ( see mine ) in your root project folder: When switching to Statamic I found a couple of things missing: Create a feed Create a new route in your under which your feed should be reachable. In my case the feed can be found under : Next you need to set up a template in which the feed is generated. So I created a file at ( see on Github ). To let the SSG package of Statamic know, that it needs to generate your feed route, specify this in in the section. Create a sitemap For generating a sitemap I use Spaties laravel-sitemap package. All you need to do is setting up a command that is run when deploying your site. To let the sitemap build on deployment create a script in your and let run when deploying to Vercel. This should already be included in the build script from Statamics SSG package. Vercel, by default, checks for inside the output folder of the application. So the easiest solution is to just create a in your folder and let this file being copied to the final static folder by setting it in your config file. The bad thing with this is, you cannot use the already existing layout as it just skips the whole Statamic magic. I hope the open issue in the SSG repository gets fixed more or less soon, so that Statamic is automatically emitting a . Setting the correct headers for your sitemap and feed Deny any iframe embedding of your content Set cache controls for your assets Disable the now bot comments on Github for your commits an atom feed a 404 page.

0 views
codedge 4 years ago

Create proper database dumps for Magento 2

When developing a Magento2 you might be needing database dumps from time to time from the production system. Normally you either dump the complete database - which, to me, bring a lot of negative side effects. Instead of now writing your own script to dump only the files you need and exclude those, you don’t want, just use the fantastic n98-magerun2 . To dump a database without logs, sessions, trade data, admin users and index tables use: Check the extended documentation for further groups and how to create your own groups. Size : Depending on the shop and the logging settings, the tables can grow up to multiple gigabytes. Sensitive (customer) data : Getting all the (hashed) passwords, addresses and names from customers does not feel like a good idea. Depending on the companies compliance guideline you may not even allowed to have them. Old/outdated data : Tables with logs or reports that are not necessary for your local development.

0 views
Pinaraf's website 12 years ago

Review – “Instant PostgreSQL Starter”

Thanks to Shaun M. Thomas , I have been offered a numeric copy of the “ Instant PostgreSQL Backup ” book from Packt publishing, and was provided with the “ Instant PostgreSQL Starter ” book to review. Considering my current work-situation, doing a lot of PostgreSQL advertising and basic teaching, I was interested in reviewing this one… Like the Instant collection ditto says, it’s short and fast. I kind of disagree with the “focused” for this one, but it’s perfectly fine considering the aim of that book. Years ago, when I was a kid, I discovered databases with a tiny MySQL-oriented book. It teaches you the basis : how to install, basic SQL queries, some rudimentary PHP integration. This book looks a bit like its PostgreSQL-based counterpart. It’s a quick travel through installation, basic manipulation, and the (controversy) “Top 9 features you need to know about”. And that’s exactly the kind of book we need. So, what’s inside ? I’d say what you need to kick-start with PostgreSQL. The installation part is straight forward : download, click, done. Now you can launch pgadmin, create an user, a database, and you’re done. Next time someone tells you PostgreSQL ain’t easy to install, show him that book. The second part is a fast SQL discovery, covering a few PostgreSQL niceties. It’s damn simple : Create, Read, Update, Delete. You won’t learn about indexes, functions, advanced queries here. For someone discovering SQL, it’s what needs to be known to just start… The last part, “Top 9 features you need to know about”, is a bit more hard to describe. PostgreSQL is a RDBMS with included batteries, choosing 9 features must have been a really hard time for the author, and I think nobody can be blamed for not choosing that or that feature you like : too much choice… The author spends some time on pg_crypto, the RETURNING clause with serial, hstore, XML, even recursive queries… This is, from my point of view, the troublesome part of the book : mentioning all these features means introducing complicated SQL queries. I would never teach someone how to do recursive queries before teaching him joins, it’s like going from elementary school to university in fourty pages. But the positive part is that an open-minded and curious reader will have a great teaser and nice tracks to follow to increase his knowledge of PostgreSQL. Mentioning hstore is really cool, that’s one of the PostgreSQL feature one have to know… To sum up my point of view about this book : it’s a nice book for beginners, especially considering the current NoSQL movement and people forgetting about SQL and databases. It’s a bit sad we don’t have more books like this one about PostgreSQL. I really hope Packt publishing will try to have a complete collection, from introduction (this book) to really advanced needs ( PostgreSQL High Performance comes to mind) through advanced SQL queries, administration tips and so on… They have a book about PostgreSQL Server Programming planned next month, I’m really looking forward to this one.

0 views
W. Jason Gilmore 14 years ago

How I Learned to Stop Worrying and Love Zend_Form

I’ve long been a fan of the Zend Framework, insomuch that I’ve penned a book on the topic, introduced dozens of students to the framework via several full day workshops, and have contributed well over a framework-related articles to Developer.com and PHPBuilder.com, among others (a compilation of which you can view here). It is a fantastically productive framework, one which I happen to use almost every single day. There was however one feature which absolutely drove me crazy. The Zend_Form component’s uses the dd, dl, and dt elements as the default form markup decorators, meaning that even a simple contact form consisting of name, email, and message fields and a submit button looks like this: While I am not disputing the wisdom of this decision, because there is technically nothing wrong with the approach. However it goes without saying that the overwhelming majority of developers do not use these elements to mark up their forms, with the sheer number of questions posted to StackOverflow and elsewhere about getting rid of these decorators backing this assertion. Now for the record you can override these decorators in your form models using the setDecorators() method, and you can even create custom decorators, however given my very questionable design skills I’d rather leave all layout decisions to the designer (whether it be choice of markup or CSS stylings). So what’s the solution? I had previously been using the removeDecorator() method to rip out all of the default decorators and then using the setDecorators() method to identify a custom view script (the latter of which is demonstrated below), however during a Zend Framework workshop I was running in Milwaukee a few months ago Joel Clermont identified a far more succinct method. This method is described below. As it turns out, there is a way to continue taking advantage of all of the great features Zend_Form has to offer while wielding total control over your form layout! Returning to the aforementioned contact form, I’ll begin the task of overriding all of the decorator defaults by creating a custom form layout file, calling it something like , and placing it within the project path. This file looks like this: Now for the magic. Open your contact form model, and add just two lines to the method, both of which are commented below: The call will tell the Zend Framework to use the script as the form’s render template, and the setElementDecorators() method will strip all of the default layout decorators which are otherwise rendered. There are numerous variations in terms of exactly what defaults you can override, but I’m trying to stay on topic and so won’t get into them here. What is clear is that you must set before calling , and you must call after calling . With the updated model in place, the rendered form looks like this: And that’s how I learned to stop worrying and love Zend_Form.

0 views
W. Jason Gilmore 14 years ago

Deploying PHP Websites with Capistrano

Have you ever had hugely ambitious plans for a website project, only to let it fall by the wayside soon after deployment because pushing incremental improvements was a total pain in the behind? If so, I'll venture a guess that you were using FTP to update the website files. FTP is like the bag of sand Indiana Jones used to try and swap for the monkey statue at the beginning of "Raiders of the Lost Ark". The bag feels so good and familiar in your hands, and with just a little tweaking you're absolutely convinced it's going to do the job. But things didn't go as expected and now the giant but incredibly smooth boulder is rolling directly towards you. FTP is an unacceptable solution because ongoing, incremental improvements to a website will only occur if the deployment barrier is sufficiently low. Because FTP is slow, stupid, and lacking an undo feature, I can guarantee your updates will be occasional, tentative, and painful. Deploying framework-based websites is even more tenuous because many solutions require additional tweaking after the files have been updated. For instance, a Zend Framework-driven website will run in development mode on your laptop but in production mode on your server. The mode is typically set in an .htaccess file via a variable named APPLICATION_ENV. If you're using FTP to blindly transfer the files over, then you'll need to SSH in following deployment and update that variable. Every single time. Fortunately, an alternative deployment solution exists called Capistrano which resolves all of FTP's issues quite nicely. Not only can you use Capistrano to securely and efficiently deploy changes to your Zend Framework website, but it's also possible to rollback your changes should a problem arise. I'll show you how to configure your development environment to use Capistrano, freeing you from ever having to worry again about a botched website deployment. Capistrano is an open source automation tool originally written for and primarily used by the Rails community. However it is perfectly suitable for use with other languages, PHP included. But because Capistrano is written in Ruby, you'll need to install Ruby on your development machine. If you're running OS X or most versions of Linux, then Ruby is likely already installed. If you're running Windows, the easiest way to install Ruby is via the Ruby Installer for Windows. Once installed, you'll use the RubyGems package manager to install Capistrano and another application called Railsless Deploy which will hide many of the Rails-specific features otherwise bundled into Capistrano. Although Railsless Deploy is not strictly necessary, installing it will dramatically streamline the number of Capistrano menu options otherwise presented, all of which would be useless to you anyway because they are intended for use in conjunction with Rails projects. RubyGems is bundled with Ruby, meaning if you've installed Ruby then RubyGems is also available. Open up a terminal window and execute the following command to install Capistrano: Next, install Railsless Deploy using the following command: Once installed you should be able to display a list of available Capistrano commands: The final general configuration step you'll need to take is configuring key-based authentication. Key-based authentication allows a client to securely connect to a remote server without requiring the client to provide a password, by instead relying on public-key authentication to verify the client's identity. Public-key cryptography works by generating a pair of keys, one public and another private, and then transferring a copy of the public key to the remote server. When the client attempts to connect to the remote server, the server will challenge the client by asking the client to generate a unique signature using the private key. This signature can only be verified by the public key, meaning the server can use this technique to verify that the client is allowed to connect. As you might imagine, some fairly heady mathematics are involved in this process, and I'm not even going to attempt an explanation; the bottom line is that configuring public-key authentication is quite useful because it means you don't have to be bothered with supplying a password every time you want to SSH into a remote server. Configuring public-key authentication is also important when setting up Capistrano to automate the deployment process, because otherwise you'll have to configure Capistrano to provide a password every time you want to deploy the latest changes to your website. If you're running a Linux/Unix-based system, creating a public key pair is a pretty simple process. Although I won't be covering the configuration process for Windows or OSX-based systems, I nonetheless suggest carefully reading this section as it likely won't stray too far from the steps you'll need to follow. Start by executing the following command to generate your public and private key: Unless you have good reasons for overriding the default key name and location, go ahead and accept the default. Next you'll be greeted with the following prompt: Some tutorials promote entering an empty passphrase (password), however I discourage this because should your private key ever be stolen, the thief could use the private key to connect to any server possessing your public key. Instead, you can have your cake and eat it to by defining a passphrase and then using a service called to cache your passphrase, meaning you won't have to provide it each time you login to the remote server. Therefore choose a passphrase which is difficult to guess but one you won't forget. Once you've defined and confirmed a passphrase, your public and private keys will be created. You'll next want to securely copy your public key to the remote server. This is probably easiest done using the scp utility: You'll need to replace username and remote with the remote server's username and address, respectively. Next SSH into the server and add the key to the file: You should now be able to login to the remote server, however rather than provide your account password you'll provide the passphrase defined when you created the key pair: Of course, entering a passphrase each time you login defeats the purpose of using public-key authentication to forego entering a password, doesn't it? Thankfully, you can securely store this passphrase , which will automatically supply it when the client connects to the server. Cache your passphrase by executing the following commands: Try logging into your remote server again and this time you'll be whisked right to the remote terminal, with no need to enter your passphrase! However, in order to forego having to manually start ssh-agent every time your client boots you'll want to configure it so that it starts up automatically. If you happen to be running Ubuntu, then ssh-agent is already configured to automatically start. This may not be the case on other operating systems, however in my experience configuring ssh-agent to automatically start is a very easy process. A quick search should turn up all of the information you require. With these general configuration steps out of the way, it's time to ready your website for deployment. You'll only need to carry out these steps once per project, all of which are thankfully quite straightforward. The first step involves creating a file called (no extension) which resides in your project's home directory. The is essentially Capistrano's bootstrap, responsible for loading needed resources and defining any custom deployment-related tasks. This file will also retrieve any project-specific settings, such as the location of the project repository and the name of the remote server which hosts the production website. I'll explain how to define these project-specific settings in a moment. Capistrano will by default look for the in the directory where the previously discussed cap command is executed, and if not found will begin searching up the directory tree for the file. This is because if you are using Capistrano to deploy multiple websites, then it will make sense to define a single in your projects' root directory. Just to keep things simple, I suggest placing this file in your project home directory for now. Also, because we're using the Railsless Deploy gem to streamline Capistrano, our looks a tad different than those you'll find for the typical Rails project: Notice the third line of the refers to a file called deploy.rb which resides in a directory named . This file contains the aforementioned project-specific settings, including which version control solution (if any) is used to manage the project, the remote server domain, and the remote server directory to which the project will be deployed, among others. The deploy.rb file I use to deploy my projects is presented next. I've commented the code, however if you're looking for a thorough explanation consider picking up a copy of my book, Easy PHP Websites with the Zend Framework. As I mentioned at the beginning of this chapter, one of the great aspects of Capistrano is the ability to rollback your deployment to the previous version should something go wrong. This is possible because (when using the copy strategy) Capistrano will store multiple versions of your website on the remote server, and link to the latest version via a symbolic link named current which resides in the the directory defined by the :deploy_to setting found in your deploy.rb file. These versions are stored in a directory called releases, also located in the :deploy_to directory. Each version is stored in a directory with a name reflecting the date and time at the time the release was deployed. For instance, a deployment which occurred on February 24, 2011 at 12:37:27 Eastern will be stored in a directory named (these timestamps are stored using Greenwich Mean Time). Additionally, Capistrano will create a directory called shared which also resides in the :deploy_to directory. This directory is useful for storing custom user avatars, cache data, and anything else you don't want overwritten when a new version of the website is deployed. You can then use Capistrano's task to create symbolic links just as was done with the file. Therefore given my directory is set to , the directory contents will look similar to this: Capistrano can create the releases and shared directories for you, something you'll want to do when you're ready to deploy your website for the first time. Create these directories using the deploy:setup command, as demonstrated here: Now comes the fun part. To deploy your project, execute the following command: If you've followed the instructions I've provided so far verbatim, remember that Capistrano will be deploying your latest committed changes. Whether you've saved the files is irrelevant, as Capistrano only cares about committed files. Presuming everything is properly configured, the changes should be immediately available via your production server. If something went wrong, Capistrano will complain in the fairly verbose status messages which appear when you execute the deploy command. Notably you'll probably see something about rolling back the changes made during the current deployment attempt, which Capistrano will automatically do should it detect that something has gone wrong. One of Capistrano's greatest features is its ability to revert, or rollback, a deployment to the previous version should you notice something just isn't working as you expected. This is possible because as I mentioned earlier in the chapter, Capistrano stores multiple versions of the website on the production server, meaning returning to an earlier version is as simple as removing the symbolic link to the most recently deployed version and then creating a new symbolic link which points to the previous version. To rollback your website to the previously deployed version, just use the command: If this is your first encounter with Capistrano, I'd imagine reading this blog post is akin to finding a pot of gold at the rainbow (or perhaps a monkey statue). Putting all of the pieces in place can be a bit confusing at first, however once they are I guarantee you'll never let a project fall to the wayside again (at least because of deployment hindrances). If you have any questions, I'm happy to help! E-mail me at wj AT wjgilmore.com.

0 views