<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JUICE</title>
    <description>The latest articles on DEV Community by JUICE (@juisick).</description>
    <link>https://dev.to/juisick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3910631%2Fa6893ca5-53ba-4967-96b0-ccd54f79dbd0.png</url>
      <title>DEV Community: JUICE</title>
      <link>https://dev.to/juisick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/juisick"/>
    <language>en</language>
    <item>
      <title>Why Your MCP Filesystem Server Needs a Guardrail (and How to Build One in Rust)</title>
      <dc:creator>JUICE</dc:creator>
      <pubDate>Wed, 07 Oct 2026 13:23:19 +0000</pubDate>
      <link>https://dev.to/juisick/why-your-mcp-filesystem-server-needs-a-guardrail-and-how-to-build-one-in-rust-5dah</link>
      <guid>https://dev.to/juisick/why-your-mcp-filesystem-server-needs-a-guardrail-and-how-to-build-one-in-rust-5dah</guid>
      <description>&lt;p&gt;AI coding assistants are evolving rapidly. With Anthropic’s Model Context Protocol (MCP), agents in Claude Desktop, Cursor, or Zed are no longer confined to chat windows - they read directories, write files, inspect diffs, and invoke terminal commands directly on our development machines.&lt;/p&gt;

&lt;p&gt;That level of agency is incredible for developer velocity, but it introduces a major security trade-off: runtime authority over sensitive files.&lt;/p&gt;

&lt;p&gt;Here is why client-side confirmation modals fail in real-world workflows, why naive pattern blocking creates a false sense of security, and how I implemented a lightweight, sub-millisecond security shim in Rust to enforce deterministic policies.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Problem: All-or-Nothing Permission Models
&lt;/h2&gt;

&lt;p&gt;When using official servers like @modelcontextprotocol/server-filesystem, the security boundary is typically guarded by client-side modals:&lt;/p&gt;

&lt;p&gt;Confirmation Fatigue: When an agent is refactoring a multi-file architecture, human-in-the-loop oversight quickly degrades into clicking "Always allow" out of sheer habit.&lt;/p&gt;

&lt;p&gt;Accidental Credential Exposure: Even in well-maintained repositories, working trees frequently contain high-risk data: .env files, .npmrc with private registry tokens, cloud credential caches (~/.aws), or generated build artifacts.&lt;/p&gt;

&lt;p&gt;Session-Wide Authority: Once a directory is granted, the MCP server will happily access any file inside that tree if the LLM requests it.&lt;/p&gt;

&lt;h2&gt;
  
  
  If an agent hallucinates a path or tries to read configuration secrets to understand your project structure, your credentials get ingested straight into the model's context window.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Trap of Naive Pattern Matching (The Symlink Bypass)
&lt;/h2&gt;

&lt;p&gt;My first instinct was simple: inspect the incoming JSON-RPC payload, search for ".env" in the argument string, and block the call if found.&lt;/p&gt;

&lt;p&gt;That approach is flawed.&lt;/p&gt;

&lt;p&gt;If an attacker (or a hallucinating agent) interacts with a workspace containing a symbolic link - say, symlink_config.txt -&amp;gt; .env - a raw string search on the arguments fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The tool call asks for read_file(path: "symlink_config.txt").&lt;/li&gt;
&lt;li&gt;A string match sees no forbidden keywords.&lt;/li&gt;
&lt;li&gt;The OS follows the link and exposes your production keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A robust security gateway must perform canonicalization before policy evaluation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intercept the JSON-RPC call over stdio.&lt;/li&gt;
&lt;li&gt;Extract the candidate path.&lt;/li&gt;
&lt;li&gt;Resolve all relative segments (../) and follow symbolic links to their real filesystem targets.&lt;/li&gt;
&lt;li&gt;Verify the canonical target strictly resides within the approved workspace boundary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - Apply block/allow pattern matching against the resolved path.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Enter Argos: An In-Process Stdio Gateway in Rust
&lt;/h2&gt;

&lt;p&gt;To solve this without adding heavy dependencies, background daemons, or runtime overhead, I wrote Argos - a single ~1 MB self-contained binary written in Rust using Tokio.&lt;/p&gt;

&lt;p&gt;Instead of running an MCP server directly, the client invokes Argos, which launches the target server as a supervised child process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Argos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bin/argos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-filesystem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/workspace"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Argos acts as a zero-overhead pipe between stdin and stdout.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Key Implementation Highlights
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Intercepting tools/call Without Breaking JSON-RPC State
If a policy violation occurs, simply terminating the process would crash the MCP transport and display an unhelpful Server Disconnected error to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead, Argos catches the call on stdin and synthesizes an immediate standard MCP tool error response back to the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;error_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;json!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rpc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[SECURITY POLICY VIOLATION] Action rejected by Argos Gateway: {reason}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="s"&gt;"isError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the response complies with the JSON-RPC 2.0 specification, the AI client remains operational. The model receives clear, structured feedback that the action was blocked and continues its reasoning without context loss.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resolving Paths and Symlinks
Here is the core verification logic applied before any policy evaluation:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_path_str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Block explicit traversal markers immediately&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.filesystem.block_path_traversal&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path_val&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;path_val&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;PolicyDecision&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Explicit path traversal ('../') attempt detected"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path_val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Resolve symlinks and normalize to real disk location&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canonical_target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="nf"&gt;.canonicalize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cand_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canonical_target&lt;/span&gt;&lt;span class="nf"&gt;.to_string_lossy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clean_target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cand_str&lt;/span&gt;&lt;span class="nf"&gt;.strip_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r"\\?\"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cand_str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Ensure the resolved destination does not escape the workspace&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canonical_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workspace_root&lt;/span&gt;&lt;span class="nf"&gt;.canonicalize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;root_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canonical_root&lt;/span&gt;&lt;span class="nf"&gt;.to_string_lossy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clean_root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root_str&lt;/span&gt;&lt;span class="nf"&gt;.strip_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r"\\?\"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;root_str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;clean_target&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clean_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;PolicyDecision&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Resolved target path escapes workspace: {clean_target}"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Evaluate allow/deny rules against the resolved file&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;is_allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.filesystem.allowed_patterns&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.any&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;clean_target&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_allowed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="py"&gt;.filesystem.blocked_patterns&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;clean_target&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;PolicyDecision&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Resolved target matches blocked pattern '{pattern}'"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion &amp;amp; Next Steps
&lt;/h2&gt;

&lt;p&gt;Local AI coding agents are becoming the standard development interface, but safety cannot rely purely on prompt engineering or manual click-through dialogs. Deterministic, low-level policy enforcement at the communication boundary provides the safety margin needed for true peace of mind.&lt;/p&gt;

&lt;p&gt;Argos is open-source, runs completely locally with zero telemetry, and is available for Linux, Windows, and macOS.&lt;/p&gt;

&lt;p&gt;GitHub Repository: [&lt;a href="https://github.com/JUSICK/Argos-mcp-guardrail" rel="noopener noreferrer"&gt;https://github.com/JUSICK/Argos-mcp-guardrail&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;Pre-built Binaries: Available on the Releases page&lt;/p&gt;

&lt;p&gt;If you are exploring runtime security for AI tooling, I would love to hear your thoughts, feedback, or any edge cases you've encountered with agent file operations!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>mcp</category>
      <category>security</category>
      <category>tooling</category>
    </item>
    <item>
      <title>IT (cybersecurity) portfolio - Impact or Variety?</title>
      <dc:creator>JUICE</dc:creator>
      <pubDate>Wed, 06 May 2026 09:20:38 +0000</pubDate>
      <link>https://dev.to/juisick/it-cybersecurity-portfolio-impact-or-variety-4g0d</link>
      <guid>https://dev.to/juisick/it-cybersecurity-portfolio-impact-or-variety-4g0d</guid>
      <description>&lt;p&gt;Hey everyone,&lt;br&gt;
​I'm currently reviewing my portfolio strategy and I'd love some feedback from hiring managers and senior (junior too) devs.&lt;/p&gt;

&lt;p&gt;​Solo the Dilemma is: Is it better to have one or two robust projects that are actually used by people worldwide (like solving real-world problems), or a dozen mini-projects that showcase a wider range of technologies (e.g., a React app, a Python automation script, a Go backend)?&lt;/p&gt;

&lt;p&gt;I've been focusing on an interesting Google Chrome extension, and I'm really struggling with it, but that's actually a good thing - it means I'm learning and all that. However, I worry that an employer might see a smaller number of repos and think my "tech width" is too narrow.&lt;br&gt;
​What do you prioritize when looking at a GitHub profile? Impact or Variety?&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>frontend</category>
      <category>sideprojects</category>
      <category>portfolio</category>
    </item>
  </channel>
</rss>
