We Execute Code Every Day (But We Don’t Think of It That Way)
Developers run certain commands dozens of times a day. Commands like npm install, pip install, and gradle build are no longer conscious decisions. They are performed as naturally as clicking a save button or navigating directories in a terminal. For most developers, this action is merely a preparatory step, and they believe actual development begins afterward. But this assumption is already flawed. These commands we execute before we start developing are, in fact, the most critical execution points. We are already executing a vast amount of external code before we even write a single line.
If we look back, this shift becomes clearer. Development in the past was relatively closed. Most of the code consisted of what I wrote myself or what had been verified within the team. External code was used sparingly. Even when libraries were introduced, their number was limited, and understanding the entire codebase was not impossible. But today, the situation is entirely different. Modern applications are built on top of dozens, sometimes hundreds, of external libraries. It is now common for external code to far exceed the amount of code we write ourselves. Yet, we still define ourselves as “people who write code.”
This is where a disconnect in perception occurs. We believe we are developing, but in reality, we are operating a system that assembles and executes external code. More importantly, this process is mostly automated, and we rarely examine what happens inside it. The moment we type an install command, countless pieces of code are already being executed on our system, yet we are not conscious of it. This unconscious execution is the true starting point of modern development. And this starting point is the most efficient entry point for an attacker.
At this point, the question needs to change. When did we start running code before writing code? And why did we stop questioning this behavior? To answer this, we must first re-examine what the word “install” actually means in the context we use it.
What the Word “Install” Hides
The word “install” is deceptively simple. It suggests the act of downloading something and placing it in a specific location. However, what package managers actually do is far more complex and carries much greater implications. Tools like npm, pip, and gradle do not merely fetch files. They resolve dependencies, recursively download required packages, execute scripts defined during installation, and configure code according to the system environment. All of this is condensed into a single word: “install.”
The most critical part is the scripts executed during installation. Many packages perform additional tasks through hooks such as postinstall and preinstall. These tasks go beyond simple configuration and can include actual code execution. In other words, even if the developer does not intend it, installing a package can automatically execute its code. This structure was designed for convenience, but at the same time, it provides a powerful execution path that attackers can exploit.
If we examine this process more closely, installation resembles a pipeline rather than a simple download. Code is fetched, dependencies are expanded, scripts are executed, and the environment is configured in sequence. At any point in this pipeline, external code can be executed. This entire pipeline is composed of code not written by the developer, and in most cases, it is not verified. We abstract this entire process into a single command and accept only its outcome.
The problem is that this abstraction hides too much. Developers trust the result without fully understanding what installation actually does. This trust is not trust in the code itself, but blind trust in the process. Once this trust begins to break, it affects the entire system. Recent supply chain attacks are targeting exactly this point.
It is now necessary to redefine installation. Installation is no longer a safe preparatory step; it is the first stage where external code enters and executes within a system. Without this understanding, it is difficult to grasp the problems that follow. And the Axios incident is a clear example of how this structure is exploited in practice.
The Axios Incident — The Moment the Path We Depend On Was Compromised
At first glance, the Axios incident may appear to be a simple case of a compromised package. However, the core of this incident lies not in the code itself, but in the path through which the code is delivered. The attacker did not exploit a complex vulnerability. Instead, they obtained the authority to publish the package, inserted malicious code into otherwise legitimate code, and distributed it as is. The original functionality remained intact, and most users installed the package without noticing anything unusual. The problem is that the attack is already complete during the installation process.
If we follow the flow of this incident, the structure becomes clear. First, the publish authority of the package is taken over by the attacker. Then, minimal changes are made to the existing code to insert malicious logic. After that, the package is distributed through normal channels. Developers install the package as usual, and during that process, the malicious code is executed. This entire sequence does not deviate from the standard development workflow. In fact, it is so natural that it is difficult to suspect anything.
What matters here is that this attack does not rely on exploiting vulnerabilities. It does not take advantage of bugs in the code or bypass system security configurations. It simply leverages the normal processes of distribution and installation. In other words, the development pipeline we trust has itself become the attack vector. At this point, traditional security concepts become nearly ineffective. Code reviews, tests, and runtime monitoring do not prevent this issue.
This incident is not limited to a single package. Axios is merely one example, and the same structure can be applied to any other package. The countless libraries we use share the same distribution and installation mechanisms. Therefore, this is not a problem with a specific library, but a structural issue within the entire ecosystem we depend on. And this structure is already widely adopted.
Ultimately, the Axios incident leads to a single question. What exactly are we trusting right now? The code, the maintainer, or simply familiarity? If there is no clear answer to this question, then we are already developing on top of an attack surface without realizing it.
The Attack Begins Not on the Server, but in the Development Environment
In traditional security thinking, attacks were always directed at servers. The common flow involved penetrating from the outside to the inside, passing through the network, finding vulnerabilities, and ultimately compromising databases or applications. As a result, most security architectures have been built around protecting servers. Firewalls, WAFs, authentication systems, and access control mechanisms are representative examples of this approach. However, supply chain attacks like the Axios incident completely overturn this assumption. The attack no longer targets the server. Instead, it begins in the development environment.
This shift is not merely a change in target, but a transformation of the attack strategy itself. Attackers no longer need to directly assault well-defended servers. Instead, they choose environments used by developers—areas where control is relatively weaker. Developer PCs, local environments, and CI/CD pipelines are generally less strictly governed than production servers. When combined with a structure where external code is automatically executed, attackers can achieve far greater impact at a much lower cost. At this point, the nature of the attack shifts from “intrusion” to something closer to “infiltration.”

Once an attack begins in the development environment, it naturally expands outward. Malicious code executed in a developer’s local environment can extract credentials or tokens. These can then be used to access Git repositories or cloud services. From there, CI/CD pipelines build artifacts that are deployed into production without suspicion. In this process, the attacker can penetrate deep into the system without any explicit intrusion step. Because the flow is so natural, it often goes unnoticed that an attack has even occurred.
Ultimately, what matters is that the origin of attacks has changed. Protecting servers alone is no longer sufficient. The development environment itself has become an attack surface. And this environment is built on top of tools and commands that we use every day. In other words, our routine development actions can now become part of an attack path. Without accepting this shift in perspective, it becomes difficult to understand the trust issues that follow.
Where Does Trust Actually Exist
Developers form trust in various ways. When choosing a package, they look at download counts, GitHub stars, and whether it is widely used within the community. On the surface, this appears to be a rational process. There is an implicit assumption that widely used libraries must have been validated. However, this trust is barely connected to actual security. It simply converts the fact that something is “widely used” into the conclusion that it is “safe.”
At this point, it is necessary to reconsider the nature of trust itself. We do not verify the code directly. We do not personally audit the maintainers. Instead, we rely on the default structures provided by the platform. And this trust is largely based on experience. As long as nothing has gone wrong, we gradually stop questioning the process. But incidents like Axios demonstrate how fragile this trust actually is. The compromise of a single account can collapse the entire trust chain.
The problem is that this trust does not clearly reside anywhere. It is not embedded in the code, nor in the platform, nor fully attributable to any individual. It exists only because we collectively assume it does. In this state, trust becomes less of an objective standard and more of a psychological comfort. And in a real attack scenario, this comfort provides no defense. In fact, it becomes one of the easiest elements for an attacker to exploit.
This trust model has accelerated development speed, but at the cost of accumulating risk. We can build faster, but we have lost control in the process. And this loss of control becomes even more problematic when combined with the structure of dependencies. At this point, it is no longer just about choosing a package—it is about executing code that we are not even aware of.
The Amount of Code We Cannot Control Has Already Crossed the Threshold
Modern software is built on top of dependencies. A single project may include dozens of direct dependencies and hundreds of indirect ones beneath them. This structure expands in a tree-like form, where external code vastly outweighs the code written by developers themselves. The problem is that it is practically impossible for a developer to understand or verify all of this code. We are only aware of a handful of top-level dependencies, while everything beneath them remains largely invisible.
This structure has moved beyond mere complexity into the realm of uncontrollability. Adding a single library can automatically introduce dozens of additional packages. Each of those packages, in turn, brings its own dependencies. This recursive expansion makes it extremely difficult to determine exactly what code is being executed in the end. Yet we generate this entire structure with a single command and accept the result without question.
At the core of this issue is the concept of control. A developer can take responsibility for code they have written. But it is nearly impossible to take responsibility for an entire dependency chain. Despite this, all of this code executes with the same level of privilege. In other words, code we do not understand runs in our system with the same authority as code we wrote ourselves. Structurally, this creates a highly vulnerable environment.
In such a situation, an attacker does not need to compromise everything. They only need to select a single point within the system. Even manipulating one package deep within the dependency tree can be sufficient. Once that package is installed, the malicious code executes. And tracing that path afterward becomes extremely difficult. At this point, the dependency structure is no longer just a convenience—it becomes a medium for attack propagation.
Ultimately, we have already crossed a critical threshold. It is no longer realistic to understand or control all the code within our systems. Yet we continue to build software on top of this structure. And this contradiction naturally leads into the next stage: the structural inevitability of such attacks.
Why Supply Chain Attacks Succeed
When we connect the structures examined earlier into a single view, it becomes clear that supply chain attacks do not succeed by accident, but rather operate on top of an environment where success is structurally inevitable. Many developers try to interpret these incidents as exceptional security breaches, but in reality, the opposite is closer to the truth. The current development ecosystem inherently contains pathways that attackers can exploit. And those pathways are naturally embedded within the tools and processes we use every day. In other words, an attack is not something that requires special conditions; it simply needs to ride along a flow that is already prepared.
The most critical element in this structure is “automatic execution.” When a package is installed, certain scripts may be executed, and this execution proceeds without any explicit verification step. Developers accept this process as a convenience feature, but from an attacker’s perspective, it is the simplest way to obtain code execution privileges. In addition, the near absence of centralized validation mechanisms is another crucial factor. The moment a package is published, it is distributed globally, and the contents of that package are, by default, left to the responsibility of the author. In such a structure, the compromise of a single account can affect the entire ecosystem.

Another important factor lies in the way updates and dependencies are managed. Many projects are configured to automatically pull in the latest versions, and in this process, new code flows into the system without verification. This automation contributes to faster development, but at the same time, it provides a pathway for malicious code to spread rapidly. An attacker only needs to publish a version containing malicious logic at a specific moment, and without any further intervention, that code can be executed across countless environments. This process unfolds quietly, and in most cases, it is only recognized after damage has already occurred.
Ultimately, supply chain attacks are not the result of sophisticated technical tricks. On the contrary, they operate on top of a simple structure. In an environment where code is automatically executed, validation is weak, distribution is rapid, and dependencies are deeply nested, it is natural for such attacks to succeed. At this point, one thing must be clearly recognized. This problem cannot be resolved by fixing individual vulnerabilities; it will inevitably repeat unless the entire structure is reconsidered.
This Is Not a Security Problem, but a Development Paradigm Problem
Many organizations categorize these incidents as security issues and attempt to respond by strengthening security tools or policies. While such responses are partially necessary, they do not address the root cause. This is because the problem does not originate from a specific security vulnerability, but from the development methodology itself. As the way we write and execute code has evolved, new risks have been embedded into the process, and these risks cannot be fully controlled by traditional security approaches.
In the past, development operated within a relatively simple structure. Most code was written internally, and the use of external code was limited. As a result, security efforts were primarily focused on the application itself or the network boundary. However, modern development heavily depends on external code. Developers no longer primarily write code; instead, they assemble and connect code that already exists. In this process, the origin and execution path of code become increasingly complex, making it significantly harder to maintain control.
This shift is not merely a technological evolution but a paradigm transformation. We are no longer “developers who write code,” but rather closer to “operators who select and execute code.” Yet, our security mindset remains anchored in the past. We continue to focus on protecting servers, validating inputs, and controlling access. Meanwhile, how code enters and executes within the system is treated as a secondary concern. This imbalance is precisely why supply chain attacks continue to occur.
At this point, the question must be reframed. Instead of asking how to protect code, we must first ask what code we are executing. This question forces us to rethink the development process itself. Issues such as how to verify the origin of code, how to restrict execution privileges, and how to manage dependencies are no longer optional—they are essential. And this transformation cannot be solved by simply introducing new tools. It requires a fundamental shift in development culture and processes.
We Are Already Developing Within an Attack Path
Following the flow up to this point leads to a single conclusion. We have not necessarily been attacked yet, but we are already developing within a structure where attacks are entirely possible. This structure does not apply to a specific environment or organization; it is uniformly present across the entire modern development ecosystem. Any environment that uses package managers, depends on external libraries, and relies on automated build and deployment shares the same conditions. In other words, this is not a problem of a subset, but of the whole.
The most dangerous element in this situation is familiarity. We use the same commands every day, retrieve code in the same way, and follow the same build and deployment processes. This repetitive workflow provides a sense of stability, but at the same time, it lowers our level of vigilance. And this lowered vigilance is precisely what attackers rely on. They do not need to create new attack paths. They simply leverage the existing flow. This simplicity is the most powerful characteristic of supply chain attacks.

At this point, we need to redefine what it means to be a developer. A developer is no longer just someone who writes code. A developer is part of the flow through which external code enters and executes within a system. Within this flow, the developer is both the starting point, an intermediate node, and sometimes the final execution point. If this role is not properly recognized, we will continue to repeat the same risks. And those risks will only grow over time.
What matters now is not fear, but clarity. We are already inside the attack path. The moment we acknowledge this fact, we can begin to move forward. Questions about which code to trust, how to restrict execution, and how to sustain development practices are no longer optional considerations—they are unavoidable. The answers to these questions will determine the direction of everything that follows.
What Must Change Now
If you have followed the flow up to this point, it is no longer possible to stop at simply recognizing the problem. We have already established that we are developing within an attack path. And this structure is not something that can be easily changed. Then the question becomes: what can realistically be changed? Many people, at this point, try to look for specific tools or solutions. However, this is not a problem of tools. It is not about which security scanner to use or which policy to add, but about changing the way developers fundamentally perceive and handle code itself.
The first thing that must change is our attitude toward trust. Until now, we have developed with a default stance of trusting external code. We have relied on metrics such as popularity, download counts, and community activity, and based on those signals, we have executed code without hesitation. But these criteria are no longer sufficient. External code should no longer be treated as something to trust by default, but as something that requires verification. This shift is larger than it seems. Because at some point, it inevitably requires sacrificing development speed. However, without making this choice, we will continue to build systems on top of the same risks.

<image role: a pipeline illustrating external code being filtered and verified before entering the system, image generation prompt: dark theme, pipeline with checkpoints filtering incoming code, green check and red block icons, minimal and clean style>
The next aspect that must be reconsidered is control over execution. We have allowed code execution far too easily. Scripts that run automatically during installation, and various processes executed during builds, typically operate without any meaningful restrictions. In such a structure, there is almost no control over what code is executed or under what level of privilege it runs. We must now shift our thinking toward restricting execution itself. Instead of allowing all code to run with equal permissions, we need to grant only the minimum necessary privileges and isolate execution environments. This approach introduces friction, but that friction is precisely where control begins.
Another important change lies in how we perceive dependencies. Until now, dependencies have been viewed primarily through the lens of convenience. They allow faster development and enable the reuse of already implemented functionality. However, dependencies must now also be understood in terms of risk. Adding a single library is not just an expansion of functionality, but an act of introducing a new execution path into the system. Without this awareness, we will continue to increase the amount of code we cannot control.
These changes cannot be completed through technical measures alone. Development culture and processes must evolve alongside them. Code reviews must extend beyond internally written code, and build and deployment processes must be restructured into flows that can be verified, rather than simply automated. Most importantly, developers must constantly recognize that their environment can become part of an attack path. Without this awareness, every subsequent decision will remain unchanged.
Ultimately, what must change converges into a single point. We can no longer make decisions based solely on what is convenient for development. We must also consider what kind of execution those decisions trigger, what paths they open, and how far their consequences can propagate. Without this perspective, no tool adoption will resolve the underlying issue.
At the beginning of this article, we revisited a simple command. The familiar command npm install is no longer just an installation step. It is the first stage through which external code enters our system, executes, and propagates. And for far too long, we have accepted this process as something natural. Now, we must begin to question that assumption. Because that question is the starting point for everything that follows.