LeetCode Solution: 1752. Check if Array Is Sorted and Rotated

Mastering Rotated Arrays: A Beginner-Friendly Look at LeetCode 1752

Hey fellow coders! 👋 Today, we’re diving into a super common and insightful LeetCode problem that’s perfect for strengthening your array manipulation skills: 1752. Check if Array Is Sorted and Rotated.

It sounds a bit tricky, but with a simple trick, we can unmask the mystery of these rotated arrays! Let’s conquer it together.

🚀 Problem Explanation

Imagine you have a perfectly sorted array of numbers in non-decreasing order (meaning numbers either go up or stay the same). For example: [1, 2, 3, 4, 5].

Now, imagine we take this array and “rotate” it. This means we move some elements from the beginning to the end, maintaining their relative order.

For instance, if we rotate [1, 2, 3, 4, 5] by 2 positions, 1 and 2 move to the end:
[3, 4, 5, 1, 2]

The problem asks us to determine: Given an array nums, can it be formed by taking some originally sorted, non-decreasing array and rotating it?

Important notes:

  • Non-decreasing: [1, 1, 2, 3] is sorted non-decreasingly. Duplicates are allowed!
  • Rotation: You can rotate by any number of positions, including zero (which means no rotation at all, just a plain sorted array).

Let’s look at the examples:

Example 1: nums = [3,4,5,1,2]

  • This can be [1,2,3,4,5] rotated by 2 positions.
  • Output: true

Example 2: nums = [2,1,3,4]

  • Can you find an originally sorted array that could become this after rotation? No.
  • Output: false

Example 3: nums = [1,2,3]

  • This is already sorted. It’s like rotating by 0 positions.
  • Output: true

Constraints are small (nums.length up to 100), so efficiency won’t be a huge bottleneck, allowing us to focus on the logic!

🤔 Intuition: The “One Break” Rule

This is where the “aha!” moment happens. Think about what happens when you rotate a sorted array.

  1. A perfectly sorted array (no rotation):
    [1, 2, 3, 4, 5]
    If you compare adjacent elements, nums[i-1] will always be less than or equal to nums[i]. There are no “breaks” where the order goes down.

  2. A sorted array rotated once:
    [3, 4, 5, 1, 2]
    Notice how most elements maintain their non-decreasing order: 3 <= 4, 4 <= 5, 1 <= 2.
    But there’s exactly one spot where this breaks: 5 > 1. This is the point where the array “wraps around” from its largest element back to its smallest.

  3. An array that’s NOT sorted and rotated:
    [2, 1, 3, 4]
    Here, 2 > 1 is a break. And then 3 <= 4 is fine. What if we consider the “wrap-around” too? If it was [1,2,3,4] rotated, it would have one break. But [2,1,3,4] has 2>1. If we trace the original array [1,2,3,4] rotated one time, it would be [4,1,2,3], which also has only one break (4>1).
    The key insight is: A valid sorted and rotated array (including 0 rotations) will have at most one point where nums[i-1] > nums[i]. This “break” signifies the point of rotation. If we find more than one such break, it means the array wasn’t originally sorted.

Let’s test this “one break” rule:

  • [1,2,3,4,5] – 0 breaks. (0 <= 1 is true) ✅
  • [3,4,5,1,2] – 1 break (5 > 1). (1 <= 1 is true) ✅
  • [2,1,3,4] – 2 breaks (2 > 1 and 4 > 2 if we consider circularity). (2 <= 1 is false) ❌

This single “break count” idea will be our guiding light!

🪜 Approach: Counting the Descents

Based on our intuition, here’s the step-by-step approach:

  1. Initialize a counter: Let’s call it count, and set it to 0. This count will keep track of how many times we find nums[i-1] > nums[i].
  2. Iterate through the array (non-circularly): Loop from the second element (i=1) up to the last element (i=n-1).
    • In each step, compare nums[i-1] with nums[i].
    • If nums[i-1] > nums[i], it means we’ve found a “descent” or a “break” in the non-decreasing order. Increment count.
  3. Check the “wrap-around” break: After the loop, we need to account for the possibility that the rotation break happens between the last element and the first element.
    • Compare nums[n-1] (the last element) with nums[0] (the first element).
    • If nums[n-1] > nums[0], it means the “wrap-around” also caused a break. Increment count.
  4. Final Decision:
    • If count is 0 or 1, return true. This means the array was originally sorted and rotated (0 rotations for count=0, 1 rotation for count=1).
    • If count is greater than 1, return false. This indicates more than one break, meaning the array wasn’t a simple sorted-and-rotated sequence.

Let’s walk through [2,1,3,4] again with this precise logic:

  • count = 0
  • n = 4
  • Loop:
    • i=1: nums[0]=2, nums[1]=1. 2 > 1 is true. count becomes 1.
    • i=2: nums[1]=1, nums[2]=3. 1 > 3 is false. count remains 1.
    • i=3: nums[2]=3, nums[3]=4. 3 > 4 is false. count remains 1.
  • After loop: count = 1.
  • Wrap-around check: nums[n-1]=4, nums[0]=2. 4 > 2 is true. count becomes 2.
  • Final Decision: count is 2. Since 2 > 1, we return false. Correct!

This approach correctly identifies all the “breaks” in the circular order and gives us the answer!

💻 Code

Here’s the C++ implementation using the approach we just discussed:

class Solution {
public:
    bool check(std::vector<int>& nums) {
        int count = 0; // Initialize a counter for "descents" or "breaks"
        int n = nums.size(); // Get the size of the array

        // Step 1: Iterate through the array to find internal breaks
        // We compare nums[i-1] with nums[i]
        // Example: in [3,4,5,1,2], the break 5 > 1 is found here.
        for (int i = 1; i < n; i++) {
            if (nums[i-1] > nums[i]) {
                count++; // Increment count if a descent is found
            }
        }

        // Step 2: Check the "wrap-around" break
        // This handles cases like [1,2,3] where there's no internal break,
        // but if we consider it circularly (3 -> 1), it's a "descent".
        // Also handles the actual rotation point for arrays like [4,5,1,2,3] where 5 > 1 is the break.
        // It complements the loop by checking the (last, first) pair.
        if (nums[n-1] > nums[0]) {
            count++; // Increment count if the wrap-around is a descent
        }

        // Step 3: Determine if the array is sorted and rotated
        // A truly sorted and rotated array (including 0 rotations)
        // will have at most one "break" in the non-decreasing sequence.
        // If count is 0, it's perfectly sorted.
        // If count is 1, it's sorted and rotated once.
        // If count > 1, it's not a sorted and rotated array.
        return count <= 1;
    }
};

⏱️ Time & Space Complexity Analysis

  • Time Complexity: O(N)

    • We iterate through the nums array exactly once in the for loop, which takes O(N) time where N is the number of elements in nums.
    • The additional if condition and variable assignments are constant time operations.
    • Therefore, the total time complexity is linear with respect to the input array size.
  • Space Complexity: O(1)

    • We only use a few extra variables (count, n) to store our state. These variables take up a constant amount of memory, regardless of the input array’s size.
    • Thus, the space complexity is constant.

✨ Key Takeaways

  • The “One Break” Rule: The most crucial insight for this problem is realizing that a sorted array, when rotated, will have at most one point where the non-decreasing order is broken. This single “descent” marks the transition from the largest elements back to the smallest ones.
  • Circular Thinking: Many array problems, especially those involving rotations, benefit from thinking about the array as a circular structure. Even if you don’t explicitly use the modulo operator (i+1)%n, remembering to check the (last, first) pair is vital.
  • Simple Iteration: A single pass through the array is often enough to gather all the necessary information for many array-based problems.

This problem is a fantastic way to practice identifying patterns in arrays and translating those patterns into clean, efficient code. Keep practicing, and happy coding!

Author: Vansh2710
Published: 2026-03-28 23:13:11

Claude Code Just Hit #1 on Hacker News. Here’s Everything You Need to Know.

Claude Code hit #1 on Hacker News today. The post — a deep dive into the .claude/ folder anatomy — pulled 556 points and counting. Five YouTube tutorials dropped in the last 48 hours. X is buzzing with auto-fix demos, hooks configurations, and cloud session workflows. And Anthropic just shipped conditional hooks and cloud-based auto-fix in the same week.

Something is happening. Claude Code isn’t just a developer tool anymore — it’s becoming the default way a new generation of builders creates software. Prosumers who’ve never opened a terminal are cloning repos and shipping sites. Senior engineers are restructuring their entire CI/CD pipelines around it. The adoption curve isn’t linear — it’s vertical.

This guide covers everything. Installation to advanced workflows. Whether you’re opening Claude Code for the first time or you’re ready to wire it into your CI pipeline with hooks and auto-fix, this is the resource you bookmark and come back to.

📊 The adoption signal is loud: Chase AI posted 5 Claude Code tutorials in two days. Kenny Liao dropped a beginner-to-mastery deep dive. Matthew Berman ranked Claude S-tier in his March 2026 model tier list — “unbelievable, good at everything.” This isn’t just developer content anymore. It’s prosumer builders using Claude Code as their default “build anything fast” tool.

What Is Claude Code?

Claude Code is Anthropic’s command-line AI coding agent. Unlike chat-based AI assistants that suggest code snippets, Claude Code operates directly in your terminal — reading your files, understanding your project structure, writing code, running tests, committing to git, and executing shell commands. It’s an autonomous agent, not an autocomplete engine.

Think of the difference like this: GitHub Copilot is a passenger giving directions. Claude Code is a driver who knows the roads, checks the mirrors, and parallel parks.

It runs on Anthropic’s Claude models (currently Opus 4.6 by default for Max subscribers, Sonnet 4.5 for Pro) with a massive context window — up to 1 million tokens. That means it can hold your entire codebase in its head while working.

Installation and Setup

Prerequisites

You need:

  • Node.js 18+ (Claude Code is an npm package)
  • An Anthropic account with a Max subscription ($100/month for unlimited Opus 4.6) or Pro ($20/month with Sonnet 4.5 and limited Opus)
  • A terminal — macOS Terminal, iTerm2, Windows Terminal, or any Linux terminal
  • Git installed and configured

Install Claude Code

npm install -g @anthropic-ai/claude-code

That’s it. One command. No Docker containers, no Python virtual environments, no config files to create first.

Authenticate

claude

Running claude for the first time opens a browser window for OAuth authentication with your Anthropic account. Once authenticated, the token is stored locally and you’re ready to go.

Verify Your Installation

claude --version
claude /doctor

The /doctor command checks your environment — Node version, authentication status, git configuration, and available tools.

⚡ Pro tip: If you’re on macOS, install via Homebrew for automatic updates: brew install claude-code. The npm install works everywhere, but Homebrew keeps you on the latest version without thinking about it.

Your First Project Walkthrough

Let’s build something real. Open a terminal, navigate to a project directory (or create a new one), and start Claude Code:

mkdir my-first-project && cd my-first-project
git init
claude

Claude Code launches in interactive mode. You’ll see a prompt where you can type natural language instructions. Try this:

Create a React app with TypeScript that displays a real-time 
cryptocurrency price dashboard. Use Vite for the build tool, 
Tailwind CSS for styling, and the CoinGecko free API for data. 
Include a search bar, favorites list, and auto-refresh every 30 seconds.

Watch what happens. Claude Code will:

  1. Plan — outline the architecture and file structure
  2. Scaffold — create package.json, vite.config.ts, tsconfig.json, Tailwind config
  3. Implement — write components, hooks, API integration, types
  4. Configure — set up routing, environment variables, dev scripts
  5. Test — run the dev server to verify everything works

The entire process takes 3-5 minutes.

The .claude/ Folder: Your Project’s Brain

This is what hit #1 on Hacker News. The .claude/ folder is where Claude Code stores its understanding of your project. Think of it as the configuration layer between “generic AI” and “AI that knows your codebase.”

Here’s the anatomy:

.claude/
├── CLAUDE.md           # Project instructions (the big one)
├── settings.json       # Claude Code configuration
├── settings.local.json # Local overrides (gitignored)
├── commands/           # Custom slash commands
│   ├── review.md       # /review command
│   └── deploy.md       # /deploy command
├── skills/             # Reusable capabilities
│   └── my-skill/
│       └── skill.md
└── rules/              # Constraints and patterns
    ├── no-any.md
    └── error-handling.md

CLAUDE.md — The Most Important File

CLAUDE.md is the instruction manual for Claude Code in your project. When Claude starts a session, it reads this file first. Everything in it shapes how Claude understands and works with your code.

A good CLAUDE.md includes:

# Project: CryptoDash

## Architecture
- React 18 + TypeScript + Vite
- State management: Zustand (NOT Redux — we migrated away in v2.1)
- API layer: TanStack Query with custom hooks in src/hooks/api/
- Styling: Tailwind CSS with custom design tokens in tailwind.config.ts

## Conventions
- All components use named exports (not default exports)
- API hooks follow the pattern: useGet{Resource}, useMutate{Resource}
- Error boundaries wrap every route-level component
- Tests colocate with source: Component.tsx → Component.test.tsx

## Do NOT
- Use any type — use unknown with type guards instead
- Import from barrel files (index.ts) in the same package
- Add dependencies without checking bundle size impact first

The CLAUDE.md Hierarchy

Claude Code reads multiple CLAUDE.md files in priority order:

  1. ~/.claude/CLAUDE.md — Global instructions (your personal coding style, always applied)
  2. ./CLAUDE.md or ./.claude/CLAUDE.md — Project root (team-shared, committed to git)
  3. ./src/CLAUDE.md — Directory-specific (instructions for specific parts of the codebase)

Deeper files override shallower ones. Think of CLAUDE.md files like .gitignore — they cascade from general to specific.

The Hooks System: Automating Everything

Hooks are where Claude Code transforms from “AI assistant” to “development platform.” They’re user-defined commands that execute automatically at specific points in Claude Code’s lifecycle.

Hook Events

Event When It Fires Common Use
SessionStart Session begins/resumes Load environment, inject context
PreToolUse Before a tool call Block dangerous commands, validate
PostToolUse After a tool call succeeds Auto-format, lint, notify
Notification Claude needs attention Desktop alerts, Slack messages
SubagentStart Subagent spawned Log, resource management
PreCompact Before context compaction Save important context
SessionEnd Session ends Cleanup, report generation

Your First Hook: Auto-Format on Save

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write "$CLAUDE_FILE_PATH""
          }
        ]
      }
    ]
  }
}

Every time Claude writes a file, Prettier formats it automatically.

Conditional Hooks with if

This just shipped this week — the if field enables conditional hook execution:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Blocked: no direct DB access in production'",
            "if": "echo $CLAUDE_TOOL_INPUT | grep -q 'psql.*prod'"
          }
        ]
      }
    ]
  }
}

Prompt-Based Hooks: AI Reviewing AI

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Review this file change for security vulnerabilities. If you find any, return BLOCK. If safe, return ALLOW.",
            "model": "claude-haiku-4"
          }
        ]
      }
    ]
  }
}

A cheaper, faster model (Haiku) reviews every file write for security issues. AI auditing AI.

Auto-Fix: Claude Code Meets CI/CD

Auto-fix lets Claude Code automatically monitor your pull requests and fix CI failures — linting errors, type errors, failing tests — without you lifting a finger.

The magic: this now runs in the cloud. Your laptop can be closed. Claude Code cloud sessions monitor your PRs and fix them autonomously.

Setting Up Auto-Fix

{
  "autoFix": {
    "enabled": true,
    "github": {
      "ciChecks": ["test", "lint", "typecheck", "build"],
      "maxAttempts": 3,
      "branchPattern": "feat/*"
    }
  }
}

The CI Integration Pattern

name: Claude Auto-Fix
on:
  check_suite:
    types: [completed]

jobs:
  autofix:
    if: github.event.check_suite.conclusion == 'failure'
    runs-on: ubuntu-latest
    steps:
      - uses: anthropic/claude-code-action@v1
        with:
          mode: auto-fix
          max-attempts: 3

⚡ Real-world usage pattern: Start with auto-fix on lint and type errors only. Graduate to test failures once you trust the workflow. Never auto-fix security scans automatically.

Cloud Sessions and Remote Tasks

Anthropic shipped Remote Tasks — the ability to run Claude Code sessions on Anthropic’s cloud infrastructure, triggered on schedules or events.

  • Scheduled maintenance — “Every Monday at 9 AM, audit dependencies and open PRs for outdated packages”
  • Event-driven workflows — “When a new issue is labeled bug, create a branch, investigate, and open a draft PR”
  • Continuous documentation — “After every merge to main, update the API docs and changelog”

Advanced Workflows

Multi-Agent Architecture with Subagents

Claude Code can spawn subagents — isolated Claude instances that handle specific subtasks:

Main Agent (Opus 4.6)
├── Subagent 1: "Implement the API endpoints" (Opus)
├── Subagent 2: "Write tests for the API" (Sonnet)
├── Subagent 3: "Update documentation" (Haiku)
└── Subagent 4: "Review all changes" (Opus)

The Harness Engineering Pattern

The most sophisticated Claude Code users build harnesses around it — CLAUDE.md files, hooks, custom commands, MCP integrations, review pipelines, and CI workflows.

The same model scored 78% with one harness and 42% with another. The harness matters more than the model.

Pricing

Plan Price Model Access
Pro $20/month Sonnet 4.5 (default), limited Opus
Max 5x $100/month Opus 4.6 (default), unlimited Sonnet
Max 20x $200/month Opus 4.6 extended, priority access
API Pay-per-token Any model

Tips That Actually Matter

  1. Write Your CLAUDE.md Before Writing Code — 15 minutes saves hours.
  2. Use /compact Strategically — Run it when Claude starts making mistakes.
  3. Start Conversations with Context — Reference specific files and patterns.
  4. Git Commit Frequently — Gives you rollback points.
  5. Trust But Verify — Always review the diff before merging.
  6. Use the Right Model for the Job — Opus for architecture, Sonnet for features, Haiku for docs.
  7. Hooks Are Your Guardrails — PreToolUse to block, PostToolUse to format, Notification to alert.

Start Building

npm install -g @anthropic-ai/claude-code

Create a CLAUDE.md. Set up your first hook. Push a PR and let auto-fix handle the lint errors. Build something that would have taken you a week — in an afternoon.

Originally published on ComputeLeap. For more on the Claude Code ecosystem, check out our coverage of Remote Tasks and Cloud Sessions and Harness Engineering.

Your Security Scanner Was the Weapon: Inside the Trivy Supply Chain Attack

TL;DR: Trivy, the most widely used container scanning action in GitHub Actions, was compromised on March 19, 2026. A threat actor poisoned 76 of its 77 version tags. Every pipeline that ran a scan silently handed over SSH keys, cloud credentials, Kubernetes tokens, and more. The scan appeared to succeed. You’d never know.

The Setup

I’ve had Trivy in my pipelines for years. Container scanning on every PR, every merge, every deploy. It’s one of those things you set up once and stop thinking about, which is exactly what makes this attack so effective.

On March 19, 2026, a threat actor group called TeamPCP force-pushed malicious commits to 76 of the 77 version tags in the aquasecurity/trivy-action GitHub repository. All 7 tags in aquasecurity/setup-trivy were also compromised. If your workflow referenced Trivy by a tag (which is how basically everyone references GitHub Actions), you were running their code.

The scanner still ran. Your pipeline still went green. You had no idea.

How It Actually Happened

This attack didn’t start on March 19. It started weeks earlier.

Late February 2026: An automated bot called “hackerbot-claw” exploited a misconfigured GitHub Actions workflow and stole a privileged Personal Access Token from Aqua Security’s CI environment. The attacker used this to push malware to the Trivy VS Code extension on Open VSX.

March 1: Aqua Security disclosed the incident publicly via a GitHub discussion and rotated credentials. Except the rotation was incomplete. One service account, one PAT, one residual access path, still live.

March 19, 17:43 UTC: Using the still-valid credentials, TeamPCP force-pushed malicious commits to 76 of 77 tags in trivy-action and all 7 tags in setup-trivy. The compromised commits spoofed legitimate maintainer identities. GitHub itself flagged them with “This commit does not belong to any branch on this repository” but that warning is easy to miss in a workflow log.

March 19, 18:22 UTC: A rogue commit published a malicious Trivy binary as v0.69.4 across every distribution channel simultaneously: GitHub Releases, GHCR, Docker Hub, ECR Public, deb/rpm repositories, and get.trivy.dev.

March 20, 05:40 UTC: Aqua remediated the trivy-action tags. The window was roughly 12 hours.

March 22: The attacker pushed additional malicious Docker Hub images (v0.69.5, v0.69.6, latest) using separately compromised Docker Hub credentials, bypassing all GitHub controls. Same day, 44 repositories in Aqua’s aquasec-com GitHub org were defaced using a stolen service account token that bridged both orgs.

March 24: The campaign expanded to Checkmarx KICS and LiteLLM PyPI packages (1.82.7, 1.82.8).

The takeaway here is not just that a tool got compromised. It’s that incomplete remediation turned a single breach into a three-week campaign.

What the Payload Did

This is the part that should make you uncomfortable.

The malicious entrypoint.sh prepended about 105 lines of attack code before the legitimate Trivy scanner logic. The scan completed normally. Your logs looked fine.

Stage 1: Process enumeration. The script scanned /proc/*/environ across all runner processes, extracting environment-level secrets, filtering for anything with env or ssh in the name.

Stage 2: Memory scraping. Here’s where it gets clever. On GitHub-hosted runners, a base64-encoded Python script located the Runner.Worker process, read its memory maps via /proc/{PID}/maps, and scraped raw process memory via /proc/{PID}/mem. It was targeting GitHub Actions secrets specifically, looking for JSON structures matching "name":{"value":"...","isSecret":true}.

This matters because it bypassed GitHub Actions’ secret masking. Values that would show as *** in your logs were extracted in plaintext from process memory. There’s no log artifact. Nothing looked wrong.

On self-hosted runners, a different payload did broad filesystem harvesting across 50+ target paths:

  • SSH private keys and configs
  • Cloud credentials (AWS, GCP, Azure)
  • Kubernetes configs and service account tokens
  • Docker registry credentials
  • Database credentials (MySQL, PostgreSQL, MongoDB, Redis)
  • Terraform state files and variables
  • Ansible vaults and Helm values
  • .env files and API keys
  • Git credentials and shell history
  • Cryptocurrency wallet keys

Stage 3: Encryption. Collected data was encrypted with AES-256-CBC using a randomly generated session key, then the session key was wrapped with a hardcoded 4096-bit RSA public key. Only the attacker’s server could decrypt it.

Stage 4: Exfiltration. Primary channel was an HTTPS POST to scan.aquasecurtiy[.]org, a typosquatted domain designed to look like Aqua Security’s legitimate domain (note the extra ‘i’ in ‘security’). Resolves to 45.148.10.212 in Amsterdam.

Fallback: if the C2 was unreachable and a GitHub PAT was in the environment, the malware created a public repository named tpcp-docs and uploaded stolen data as release assets, turning GitHub itself into the exfiltration channel.

Are You Affected?

Check these specific exposure windows:

Component Affected Versions Exposure Window Safe
trivy binary v0.69.4 ~3h (Mar 19) v0.69.3 or earlier
trivy Docker Hub v0.69.5, v0.69.6, latest ~10h (Mar 22–24) v0.69.3 or earlier
trivy-action Tags 0.0.1–0.34.2 ~12h (Mar 19–20) v0.35.0+ or SHA-pinned
setup-trivy All 7 tags ~12h (Mar 19–20) SHA-pinned
LiteLLM PyPI 1.82.7, 1.82.8 Mar 24+ 1.82.6 or earlier

If you ran Trivy in any pipeline during those windows and weren’t pinning to a commit SHA, you have to assume secrets were stolen. All of them. Every secret accessible from that runner environment.

What You Need to Change

This is the remediation checklist, ordered by priority.

1. Rotate first, investigate second

If you were in the exposure window, rotate everything the runner could have touched. Don’t wait for confirmation. Treat every secret as compromised:

  • AWS access keys and IAM roles
  • GCP service account keys
  • Azure service principals
  • Kubernetes service account tokens
  • Docker registry credentials
  • SSH keys
  • Database credentials
  • GitHub PATs and tokens

2. Pin actions to commit SHAs

This is the single most effective structural change. Tags are mutable. Commit SHAs are not.

# Bad — this is what everyone does, and what got compromised
- uses: aquasecurity/[email protected]

# Good — SHA-pinned, immutable
- uses: aquasecurity/trivy-action@57a97c7843d7da7a7b4f8ce2a0c4e3b7f0c2e1d  # 0.35.0

Yes, it’s more work to update. That’s the point. Renovatebot or Dependabot can automate SHA updates if you configure them for Actions.

3. Switch to OIDC for cloud authentication

Long-lived cloud credentials in CI are a liability. OIDC lets your runner authenticate to AWS, GCP, or Azure without storing static keys:

# AWS example
- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::ACCOUNT:role/github-actions-role
    aws-region: us-east-1

Nothing to steal if there’s nothing stored. The credentials are ephemeral and scoped to the job.

4. Restrict runner permissions

GitHub Actions runners get GITHUB_TOKEN by default. Scope it down:

permissions:
  contents: read
  security-events: write
  # Nothing else

Most workflows need far less than the default. Less permission means smaller blast radius.

5. Audit non-human identities

The Trivy attack persisted because one service account credential wasn’t rotated. Audit all machine identities in your org:

  • GitHub PATs: Who issued them? When do they expire? Are they scoped minimally?
  • Service accounts: Which ones have write access to release infrastructure?
  • Bot accounts: Are any shared across orgs or repositories?

Long-lived, over-privileged service accounts are how a one-time breach becomes a three-week campaign.

6. Use secret scanning

GitGuardian, GitHub’s native secret scanning, or both. The Trivy attacker used GitHub as a fallback exfiltration channel. If your credentials ever end up in a public repo, you want to know in minutes, not days.

7. Verify binaries before running them

For direct binary downloads (not GitHub Actions), verify checksums:

# Download the official checksums
curl -sSL https://github.com/aquasecurity/trivy/releases/download/v0.69.3/trivy_0.69.3_checksums.txt -o checksums.txt

# Verify your binary
sha256sum -c checksums.txt --ignore-missing

If your pipeline downloads and runs binaries from the internet, add checksum verification as a step.

The Real Lesson

The Trivy attack was technically sophisticated, but the root cause is unglamorous: incomplete credential rotation.

Aqua disclosed the initial breach on March 1 and rotated credentials. One PAT, one service account, one residual access path was left active. That’s what TeamPCP used on March 19. The March 22 Docker Hub compromise used yet another separate credential that wasn’t in scope of the original remediation.

When you rotate secrets after a breach, you need to be exhaustive. Enumerate every credential that could have been exposed, every service account that had access, every integration that used a compromised token. Rotation is not a task you do until it feels complete. It’s a task you do until you’ve verified every access path is severed.

The other lesson: the attack surface for CI/CD is enormous. Your pipeline runs with access to secrets, cloud credentials, internal infrastructure. When you add a third-party action, you’re trusting that maintainer’s entire security posture, including their CI, their service accounts, and their credential management practices. SHA pinning doesn’t eliminate that trust but it gives you a stable, auditable point you can reason about.

Immediate Checklist

[ ] Check pipeline logs for trivy-action usage between March 19–20
[ ] Check pipeline logs for trivy binary v0.69.4 usage on March 19
[ ] Check for Docker image usage of v0.69.5, v0.69.6, or latest between Mar 22–24
[ ] Rotate all secrets accessible from affected runners
[ ] Update trivy-action to v0.35.0 or pin to SHA
[ ] Check for LiteLLM usage of 1.82.7 or 1.82.8
[ ] Switch cloud auth to OIDC
[ ] Pin all third-party actions to commit SHAs
[ ] Restrict workflow permissions to minimum required
[ ] Audit service accounts and PATs for expiry and scope
[ ] Enable secret scanning on your org

References:

The command-line interface (CLI) is a powerful tool that allows developers to interact with their operating system and execute commands to p

Introduction

The command-line interface (CLI) is a powerful tool that allows developers to interact with their operating system and execute commands to perform various tasks. As a beginner to intermediate developer, learning how to use the CLI is an essential skill that can help you become more efficient and effective in your work. In this tutorial, we will cover the basics of getting started with the CLI and provide you with hands-on experience through practical examples.

The CLI is a text-based interface that allows you to type commands to execute specific tasks. It provides a lot of flexibility and can be used to perform tasks such as file management, system configuration, and debugging. With the CLI, you can automate tasks, create scripts, and even build custom tools to simplify your workflow. Whether you are working on a Windows, macOS, or Linux system, the CLI is an essential tool that you should be familiar with.

In this tutorial, we will focus on the basics of the CLI and provide you with a comprehensive guide on how to get started. We will cover the prerequisites, basic commands, navigation, and file management. By the end of this tutorial, you will have a solid understanding of the CLI and be able to use it to perform various tasks with confidence.

Prerequisites

Before you start using the CLI, you need to have the following prerequisites:

  • A computer with a Windows, macOS, or Linux operating system
  • A terminal emulator or command prompt (e.g., Command Prompt on Windows, Terminal on macOS or Linux)
  • Basic knowledge of computer concepts and terminology

Main Content

Section 1: Basic Commands

To get started with the CLI, you need to know some basic commands. Here are a few essential commands to get you started:

  • cd: change directory
  • pwd: print working directory
  • ls: list files and directories
  • mkdir: make a new directory
  • rm: remove a file or directory

Let’s try some examples:

# Create a new directory
mkdir mydirectory

# Change into the new directory
cd mydirectory

# List the files and directories in the current directory
ls

# Print the current working directory
pwd

Section 2: Navigation

Navigation is a critical aspect of using the CLI. Here are some essential navigation commands:

  • cd ~: change to the home directory
  • cd ..: change to the parent directory
  • cd ./: change to the current directory
  • cd ../../: change to the grandparent directory

Let’s try some examples:

# Change to the home directory
cd ~

# Change to the parent directory
cd ..

# Change to the current directory
cd ./

# Change to the grandparent directory
cd ../../

Section 3: File Management

File management is another essential aspect of using the CLI. Here are some basic file management commands:

  • touch: create a new empty file
  • cp: copy a file
  • mv: move or rename a file
  • rm: remove a file

Let’s try some examples:

# Create a new empty file
touch myfile.txt

# Copy the file
cp myfile.txt mycopy.txt

# Move or rename the file
mv myfile.txt mynewfile.txt

# Remove the file
rm mynewfile.txt

Section 4: Advanced Commands

As you become more comfortable with the CLI, you can start using more advanced commands. Here are a few examples:

  • grep: search for a pattern in a file
  • sed: stream editor for filtering and transforming text
  • awk: pattern scanning and text processing language

Let’s try some examples:

# Search for a pattern in a file
grep "pattern" myfile.txt

# Use sed to replace a pattern in a file
sed "s/pattern/replacement/" myfile.txt

# Use awk to print the first column of a file
awk '{print $1}' myfile.txt

Troubleshooting

As you start using the CLI, you may encounter some errors or issues. Here are some common troubleshooting tips:

  • Check the command syntax: Make sure you are typing the command correctly, including any options or arguments.
  • Check the file permissions: Make sure you have the necessary permissions to read, write, or execute the file.
  • Check the directory: Make sure you are in the correct directory and that the file or directory exists.

Some common error messages include:

  • command not found: The command is not recognized or installed.
  • permission denied: You do not have the necessary permissions to perform the action.
  • file not found: The file or directory does not exist.

Conclusion

In this tutorial, we covered the basics of getting started with the CLI. We introduced you to basic commands, navigation, file management, and advanced commands. We also provided you with some troubleshooting tips to help you overcome common errors or issues. With practice and experience, you will become more comfortable using the CLI and be able to perform various tasks with confidence. Remember to always check the command syntax, file permissions, and directory before performing any action. Happy coding!

Sponsor & Subscribe

Want weekly practical tutorials and collaboration opportunities?

How I Built an AI-Powered Business Brain in One Day Using Notion MCP

What I Built

Video Demo

This is a submission for the Notion MCP Challenge

What I Built

The Jesse System AI Brain — a fully connected personal and business productivity system powered by Notion MCP and Claude AI.

The Jesse System is a small business automation consulting service that helps small business owners stop doing repetitive tasks manually. Today I built the entire infrastructure for it in a single session using Notion MCP as the backbone.

Here’s what the system includes:

  • A structured Notion workspace with five interconnected sections: AI Conversations, Personal To-Dos, Market Research, Game Project, and Small Business
  • Claude connected to Notion via MCP — reading, writing, updating, and logging entries in real time through natural language
  • A live business website deployed at dulcet-churros-460036.netlify.app with an interactive Zapier explainer, pricing, and contact form
  • A real-time AI conversation logger — so nothing important discussed with any AI tool ever gets lost again
  • A business model built and documented inside Notion — with real 2026 market pricing data
  • A game concept developed and stored — Nurses vs. Zombies, a sci-fi comedy FPS (“Doom meets Scrubs”)

The core idea: Notion becomes the brain that any AI can read from and write to — solving the real problem of losing context between AI sessions.

Video Demo

Show Us the Code

The live site is a single deployable HTML file hosted on Netlify:
dulcet-churros-460036.netlify.app

Claude Desktop was connected to Notion MCP using this config at %APPDATA%Claudeclaude_desktop_config.json:

{
  "mcpServers": {
    "notionMCP": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.notion.com/mcp"]
    }
  }
}

The Claude.ai web connector was also used via https://mcp.notion.com/mcp with full OAuth authorization and read/write access.

How I Used Notion MCP

Notion MCP was the core of everything built today. Here’s exactly what it did:

Real-time read/write via natural language — Claude read and wrote to Notion pages throughout the entire session without me touching Notion manually. I’d say “log today’s session” and it appeared in my AI Conversations database instantly.

Living business documentation — The Jesse System’s business description, pricing model, market research, and game concept were all built and stored directly into Notion through Claude. The workspace is now a working knowledge base any AI can reference.

Context persistence across sessions — The biggest problem I was solving for myself: I kept forgetting what I’d told which AI. Notion MCP means Claude can now look at my workspace and immediately know my projects, my business, and my history — no re-explaining required.

To-do management — Tasks were added to my Personal To-Dos page mid-conversation without interrupting the flow of work.

The integration dropped near the deadline due to a session conflict, but was fully functional throughout development with 13 Notion tools active including search, fetch, create-page, update-page, and move-pages.

“Someone running a business isn’t looking for showmanship. They’re looking for simplicity and clarity.” — That’s the philosophy behind The Jesse System, and behind using Notion MCP as the foundation.

Fire and Ashes — our new immersive horror that hears you

**Fire and Ashes **is an immersive horror experience where madness and reality intertwine, and every sound can give you away. The monsters hear your screams… even beyond the hospital.

📌 1977. Henry Grey wakes up in the Paul Staderbeiger Psychiatric Hospital. Haunted by the ghosts of his past, he finds himself in a place where everything around him becomes harder and harder to explain.

The hospital is only the beginning. Henry descends into distorted worlds where fear, memories, and reality merge into one.


Exploring ruined corridors and the strange spaces beyond them, he gradually uncovers the truth about his past — a truth that may be worse than any nightmare.

Key Features:

  • Advanced voice integration — the game reacts to your breathing, whispers, and screams
  • Use your voice to survive — hide, distract enemies, or provoke them
  • Constant tension — enemies can hear you and will hunt you down
  • Journey beyond the hospital into worlds where logic breaks down
  • Survival and combat using whatever you can find
  • Exploration, puzzles, and gradual story discovery
  • Uncertainty of reality — is this still real, or pure madness?
  • The demo is already available.

Modifying Tables in SQL using ALTER

1. Making a Column NOT NULL

Suppose we already created a customers table, but now we want to ensure that email is always provided.

ALTER TABLE customers ALTER COLUMN email SET NOT NULL;

2. Adding UNIQUE Constraint

To make sure no two users have the same username:

ALTER TABLE users
ADD CONSTRAINT unique_username UNIQUE (username);

3. Adding CHECK Constraint

We want to ensure product price is always greater than 0:

ALTER TABLE products
ADD CONSTRAINT check_price CHECK (price > 0);

4. Setting DEFAULT Value

To make status = 'pending' by default:

ALTER TABLE orders
ALTER COLUMN status SET DEFAULT 'pending';

5. Adding a Column with Constraints

Now we add a salary column to employees:

ALTER TABLE employees
ADD COLUMN salary DECIMAL(10,2) NOT NULL CHECK (salary > 10000);

6. Modifying Foreign Key with CASCADE

To automatically delete employees when a department is deleted:

ALTER TABLE employees
DROP CONSTRAINT employees_department_id_fkey;

ALTER TABLE employees
ADD CONSTRAINT employees_department_id_fkey
FOREIGN KEY (department_id)
REFERENCES departments(id)
ON DELETE CASCADE;

7. Dropping a CHECK Constraint

To remove the balance restriction:

ALTER TABLE accounts
DROP CONSTRAINT accounts_balance_check;
d accounts;

8. Adding Composite UNIQUE Constraint

To ensure a combination is unique:

ALTER TABLE payments
ADD CONSTRAINT unique_user_transaction
UNIQUE (user_id, transaction_id);

I was tired of manual SQL introspection, so I built Legacy Lens (MCP Server)

We’ve all been there. You’re staring at a legacy database. 400 tables. Cryptic column names like FLG_Z_99. No documentation. You try to explain it to an AI agent like Claude or Cursor, and it starts hallucinating JOINs that don’t exist.

I was tired of copy-pasting DDL exports and manually explaining schemas. So, I built Legacy Lens.

The Problem: The “Context Gap”

AI models are smart, but they are “blind” to your local infrastructure. To make an LLM actually useful for database engineering, it needs more than just a connection string; it needs a semantic map of the data.

In a world of “AI Wrappers,” we often forget that the bottleneck isn’t the AI’s intelligence—it’s the precision of the context we provide.

The Solution: Model Context Protocol (MCP)

I decided to leverage the Model Context Protocol. Instead of a simple wrapper, I built a dedicated introspection engine that acts as the AI’s “eyes” inside the database.

What Legacy Lens does:

  • Deep Introspection: It doesn’t just list tables. It maps foreign keys, schemas, and constraints across PostgreSQL, MySQL, SQLite, and MS SQL Server.
  • Smart Sampling: It provides 5-10 sample rows per call. This gives the AI the “vibe” of the data (date formats, nullability, categorical values) without blowing up the context window.
  • Security First: I implemented a “Least Privilege” approach. The server defaults to READ ONLY transactions and provides SQL snippets to help users create restricted database roles.

Architecture: Why I split the logic

I care about modularity and reusability. I didn’t just build one monolithic script. I split the project into two scoped NPM packages:

  1. @legacy-lens/db-introspect: A standalone, dialect-agnostic library. You can use it for migrations, custom dashboards, or any tool that needs to “understand” a DB structure without the AI part.
  2. @legacy-lens/mcp-server: The bridge. It uses the introspection layer to expose tools (list_tables, describe_table, sample_rows) to any MCP-compliant client like Cursor, Claude Desktop, or custom agents.

Try it out

If you’re dealing with a database that makes you want to quit your job, give this a spin:

npx @legacy-lens/mcp-server

I’m currently looking for feedback on which engine to support next. Oracle? (Only if I’m feeling particularly brave).

Check out the project on GitHub:

GitHub logo

baqqa
/
LegacyLens

An MCP that allows to search you’re db with you’re AI

Legacy Lens

Legacy Lens is an MCP (Model Context Protocol) server that lets AI assistants in Cursor and other hosts inspect legacy SQL databases safely. It answers questions about structure and sample data: schemas, tables, columns, foreign keys, and a small number of sample rows per request (default 5, maximum 10). It is meant for understanding old systems before you change them—not for arbitrary read/write SQL from the model.

Supported databases: PostgreSQL, MySQL / MariaDB, SQLite, Microsoft SQL Server. Oracle is not supported.

Install (npm)

You need Node.js 20+.

npm install -g @legacy-lens/mcp-server

If the package is not yet on the public registry, install from your team’s tarball, GitHub Packages, or private registry the same way you install other scoped packages.

After install, the legacy-lens-mcp command is available. The MCP host must run it with no arguments (stdio mode). Subcommands are only for setup from a…

What’s missing in your current JSON log filtering setup?

I debug microservice logs daily and kept running into the same gaps with existing tools — hl, humanlog, lnav all pretty-print JSON well but none of them let me
trace a full request flow across services with a single pattern, or save my filter setup between sessions.

Built something that solves these specifically and wanted to get feedback from people who deal with this daily before I invest more time into it.

Three things I built around:

  • Hier path filter: –hier “payment.**” to trace an entire flow across services instead of grepping service by service
  • Named profiles: tired of retyping –service X –level warn –hier “Y” every session
  • TTL noise suppression: kills repeated log spam by fingerprinting message patterns, not exact strings

Genuinely curious whether these match real pain points or if I’m solving the wrong problems. What does your log debugging workflow actually look like? What do you reach for and what frustrates you about it?

If anyone wants to poke at it and tell me what’s broken or missing, happy to share access. it would be helpful to get real feedback from people who live in logs.
I’m doing a beta right now and it’s completely free at https://bearking11.gumroad.com/l/logr.
Would really appreciate any feedback on whether it actually helps the devs.

How to Store Secrets in the Mac Keychain (and Use Them Like Environment Variables)

I used to keep API keys in .env because it was fast. Then I caught myself grepping my home folder for something unrelated and watching paths scroll past that file, or almost committing a backup copy. None of that is catastrophic every time, but it’s a bad habit. On a Mac you already have a place meant for secrets: Keychain.

This isn’t a pitch for a fancy secrets vault. It’s about the security tool that ships with macOS—handy for local dev tokens, DB URLs, signing keys, that sort of thing.

One thing to get straight up front: Keychain doesn’t literally store “environment variables.” It stores items (usually generic passwords) keyed by service name and account. You pull the value out with security and export it when you need it. Day to day it behaves like env vars; under the hood it’s a lookup, not a magic .env replacement.

You’ll need a Mac, a terminal, and if you want secrets to load automatically, willingness to touch ~/.zshrc or similar.

Save and read a secret

Add a generic password (Terminal may ask for Keychain permission the first time):

security add-generic-password 
  -a "$USER" 
  -s "myapp-dev-api-key" 
  -w "sk_live_xxxxxxxx"
  • -a — account; often your macOS username or an app name.
  • -s — service string; this is the handle you’ll use later. Make it unique, e.g. myapp-dev-api-key.
  • -w — the secret. The problem is anything after -w can land in shell history (see below).

Read it (prints to stdout):

security find-generic-password -s "myapp-dev-api-key" -w

Use it as an env var for this shell only:

export MYAPP_API_KEY="$(security find-generic-password -s "myapp-dev-api-key" -w)"

After that, anything that reads MYAPP_API_KEY behaves as if you’d sourced a .env—except the value never had to live in a plaintext file on disk.

Avoiding shell history when you add the secret

Apple’s own usage text says bluntly that -p / -w on the command line is insecure. Two patterns that actually work:

Built-in prompt (often the nicest): put -w last and omit the value. security will prompt for the password; that path doesn’t shove the secret into your history the way -w "secret" does.

security add-generic-password -a "$USER" -s "myapp-dev-api-key" -w

(When it asks, type or paste the secret; it’s the usual “no echo” style prompt.)

Shell-side prompt if you prefer to stay in bash/zsh:

printf 'Paste secret (hidden): '
read -rs SECRET
echo
security add-generic-password -a "$USER" -s "myapp-dev-api-key" -w "$SECRET"
unset SECRET

Keychain Access still counts: File → New Password Item… and line up the fields with whatever you pass as -a / -s so find-generic-password can find the item later.

Wiring it into how you actually work

Every new terminal (Zsh snippet in ~/.zshrc):

export MYAPP_API_KEY="$(security find-generic-password -s "myapp-dev-api-key" -w 2>/dev/null)"

2>/dev/null just keeps noise down if the item doesn’t exist yet.

Per project, a small scripts/load-secrets.sh that exports what that repo needs is reasonable—don’t commit values; document the service names in the README so someone else can add their own copy to Keychain.

One shot:

MYAPP_API_KEY="$(security find-generic-password -s "myapp-dev-api-key" -w)" uv run python -m myapp

What you gain, what you’re signing up for

You lose the plaintext secret sitting in .env (and in a lot of accidental greps and backups). It’s built in, no extra install, and Keychain can nudge or block access per app if you tune Access Control on the item. For “this laptop only” dev keys, that’s often plenty.

The flip side is boring but real: Linux and Windows won’t help you here—those teammates need 1Password CLI, Doppler, SOPS, cloud IAM, whatever your team standard is. There’s no automatic team sync; new machine usually means re-adding items (iCloud Keychain exists but that’s a deliberate trust choice). If you echo secrets or log them, you’ve undone the point. CI wants provider-native secrets, not interactive Keychain. Onboarding is also slightly worse than “copy .env.example”—people need the exact service strings and commands written down somewhere.

I still reach for this for solo Mac dev and small personal projects where I want fewer sensitive files lying around. For shared, audited, rotated secrets at work, use the thing your platform team points you at.

So: you store with security, load with export and $(security find-generic-password … -w), and you get env-var ergonomics without a plaintext .env for that value. The cost is macOS-only, a bit more documentation, and discipline around history and logging.