🔥 trailofbits / skills - Trail of Bits Claude Code skills for security research, vuln
GitHub热门项目 | Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows | Stars: 6,363 | 18 stars today | 语言: Python
找到 1123 篇相关文章
GitHub热门项目 | Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows | Stars: 6,363 | 18 stars today | 语言: Python
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . You are migrating a 50,000-message Slack workspace to Zulip. Somewhere around message 31,000 the import dies with KeyError: 'ts' . Annoying, but here is the uncomfortable part: that is the lucky outcome. The unlucky one is "ts": "NaN" , where nothing dies, nothing warns, and your company's message history quietly comes out in the wrong order. TL;DR: Zulip's Slack importer used float(message["ts"]) unguarded, both as a sort key and as date_sent . One message with a missing or malformed ts aborted the entire import; a non-finite value like "NaN" did not even raise, it silently broke the sort. My fix ( zulip/zulip#39813 ) skips such messages with a warning and requires ts to parse to a finite float via math.isfinite . The regression test fails with KeyError: 'ts' on the old code. Project Overview Zulip is an open-source team chat server (Django/Python, ~25k stars) with an unusually strict engineering culture: near-total backend test coverage, strict mypy, and a commit discipline of "each commit is a minimal coherent idea". The code I touched lives in zerver/data_import/ : the subsystem that converts exports from Slack, Microsoft Teams, and Mattermost into Zulip's format. This subsystem has one property that should shape every line in it: the input is another tool's output. Import is a long batch process over data of arbitrary quality, and the admin running the migration has no way to "fix" what Slack's export tool produced. A pipeline that dies on record 31,207 of 50,000 is strictly worse than one that skips record 31,207 with a warning. Bug Fix or Performance Improvement get_messages_iterator() in zerver/data_import/slack.py streams every message of the export, sorting each day's messages by timestamp: yield from sorted ( messages_for_one_day , key = get_timestamp_from_message ) where the sort key was simply: def get_timestamp_from_message ( message : ZerverFieldsT ) -> float : retur
A few days ago I fixed update_article , one of the tools in this repo's MCP server, because it had a nasty shape: it took a bare integer article_id , PUT whatever fields you gave it straight to the DEV.to API, and if the id was wrong or hallucinated, it would silently overwrite a live published post with nothing left behind to show it had happened. The fix added a fetch-before-write diff and a JSONL audit log: _ARTICLE_UPDATE_LOG = " logs/article_updates.jsonl " def _log_article_update ( article_id , before , fields_changed , after ): os . makedirs ( os . path . dirname ( _ARTICLE_UPDATE_LOG ), exist_ok = True ) entry = { " article_id " : article_id , " fields_changed " : sorted ( fields_changed ), " url " : after . get ( " url " )} for field in fields_changed : entry [ f " { field } _before " ] = before . get ( field ) entry [ f " { field } _after " ] = after . get ( field ) with open ( _ARTICLE_UPDATE_LOG , " a " ) as f : f . write ( json . dumps ( entry ) + " \n " ) The whole point of that function is durability. "Zero trace" was the bug; a JSONL file that records before/after state on every write was the fix. I verified the logging logic itself with an offline unit test against fake before/after states and moved on, same as the diff field. What I never checked is whether logs/article_updates.jsonl outlives the process that writes it. Checking whether the trace actually exists anywhere logs/ isn't in .gitignore — I checked, it's not there. So nothing is actively hiding it. But not-hidden isn't the same as tracked: $ git log --all --oneline -- 'logs/*' $ ls logs/ ls: cannot access 'logs/': No such file or directory Empty output from the first command, across every branch and every commit this repo has ever had (50 commits, not a shallow clone — git rev-parse --is-shallow-repository is false ). Nothing has ever touched logs/ . The directory doesn't even exist right now. Not because anything deleted it — because nothing has ever run update_article in an environment
Gradio is a Python library for wrapping any ML model in a web interface, ready to deploy and scale as an app. This guide builds a GFPGAN-powered face-restoration demo with Gradio on Ubuntu 22.04, runs it as a systemd service, and exposes it through Nginx with TLS. Prerequisites: a GPU-enabled Ubuntu 22.04 server, a domain A record (e.g. gradio.example.com ), non-root sudo access, Nginx installed. Set Up the Server 1. Install dependencies: $ pip3 install realesrgan gfpgan basicsr gradio realesrgan — background restoration gfpgan — face restoration basicsr — provides RRDBNet , the super-resolution architecture GFPGAN relies on gradio — the web interface 2. GFPGAN's pandas dependency needs jinja2 >= 3.1.2: $ pip show jinja2 Upgrade if it's older: $ pip install --upgrade jinja2 3. Create the project directory: $ sudo mkdir -p /opt/gradio-webapp/ $ sudo chown -R : $( id -gn ) /opt/gradio-webapp/ $ sudo chmod -R 775 /opt/gradio-webapp/ Build the Gradio App Uploads a face image and returns two enhanced outputs. $ cd /opt/gradio-webapp/ $ nano app.py import gradio as gr from gfpgan import GFPGANer from basicsr.archs.rrdbnet_arch import RRDBNet from realesrgan import RealESRGANer import numpy as np import cv2 import requests def enhance_image ( input_image ): arch = ' clean ' model_name = ' GFPGANv1.4 ' gfpgan_checkpoint = ' https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth ' realersgan_checkpoint = ' https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth ' rrdbnet = RRDBNet ( num_in_ch = 3 , num_out_ch = 3 , num_feat = 64 , num_block = 23 , num_grow_ch = 32 , scale = 2 ) bg_upsampler = RealESRGANer ( scale = 2 , model_path = realersgan_checkpoint , model = rrdbnet , tile = 400 , tile_pad = 10 , pre_pad = 0 , half = True ) restorer = GFPGANer ( model_path = gfpgan_checkpoint , upscale = 2 , arch = arch , channel_multiplier = 2 , bg_upsampler = bg_upsampler ) input_image = input_image . astype ( np . uint8 ) cropped_fa
If your script suddenly started printing this: >>> import yfinance as yf >>> df = yf . download ( " NG=F " , period = " 1mo " ) 1 Failed download : [ ' NG=F ' ]: YFPricesMissingError ( ' possibly delisted; no price data found ' ) …you didn't break anything. NG=F (the natural gas futures ticker on Yahoo Finance) periodically stops returning data for everyone, and futures tickers get hit harder than stocks. This post covers why it happens and the three fixes that actually work, ordered from "quick patch" to "never deal with this again." 1. What the error actually means yfinance is not an official API . It's a (great) community library that scrapes Yahoo Finance's internal endpoints — the same ones Yahoo's own website uses. Yahoo doesn't document them, doesn't promise they'll keep working, and changes them whenever it suits their frontend. When Yahoo changes something — an endpoint, a rate limit, a response format — yfinance breaks until its maintainers reverse-engineer the change. Futures symbols like NG=F and GC=F are the most fragile: they've had recurring gaps and failures reported over the years, for example #2620 (missing recent data for NG=F/GC=F) , #2635 (whole missing days in futures history) and the evergreen #865 "Futures only work sometimes" . So: "possibly delisted" almost never means delisted. It means "the scrape came back empty." 2. Fix #1 — the quick patches (works today, breaks tomorrow) Three things fix most transient failures: Upgrade first. The maintainers usually patch Yahoo changes within days: pip install -U yfinance Retry with backoff. Failures are often intermittent rate-limiting, not hard breaks: import time import yfinance as yf def download_with_retry ( ticker , retries = 3 , wait = 5 , ** kwargs ): for attempt in range ( 1 , retries + 1 ): df = yf . download ( ticker , progress = False , ** kwargs ) if not df . empty : return df print ( f " attempt { attempt } came back empty, retrying in { wait } s… " ) time . sleep ( wait * attempt ) rai
When building modern Python applications—whether scraping web pages, fetching data from external APIs, or querying databases—IO-bound operations often slow down execution. Python’s concurrent.futures module provides a high-level, elegant interface for running tasks asynchronously. In this guide, we'll break down what Futures are, why you need them, and how to use them effectively using a practical e-commerce product service. What is a Future? A Future represents an eventual result of an asynchronous operation. When you launch an expensive, long-running task concurrently, your program doesn't pause to wait for the output. Instead, it instantly gets back a Future object —a low-cost proxy or standard "claim ticket." The Future acts as a placeholder for a result that hasn't been computed yet. It keeps track of the task's execution state ( PENDING , RUNNING , CANCELLED , or FINISHED ). Once the task finishes, the Future stores the return value or any exception thrown during execution. Why are Futures Needed? In standard synchronous Python execution, calling a function blocks your main thread until that function finishes: Task 1 (2s) ──> Task 2 (3s) ──> Task 3 (1s) = 6 seconds total When dealing with IO-bound operations (like waiting for network responses or reading disks), your CPU sits completely idle during those delays. By offloading tasks into background threads or processes via Futures, your application can run multiple IO operations simultaneously: Task 1 (2s) [████████] Task 2 (3s) [████████████] Task 3 (1s) [████] ----------------------------------------- Total Time: 3 seconds (time of longest task) When Should You Use Futures? IO-Bound Workloads: Scraping multiple web pages, batch-calling microservices, querying multiple databases, or fetching images concurrently ( ThreadPoolExecutor ). CPU-Bound Parallelism: Performing heavy mathematical operations or image processing across multiple CPU cores ( ProcessPoolExecutor ). Decoupled Workflows: When you want to trigg
On a Tuesday morning in March, a chief executive asked a question that should have taken thirty seconds to answer: have we ever agreed to a liability cap below one million dollars? The answer existed. It was written down, signed, filed, and sitting on the shared drive the whole time. Finding it took three days, and not finding it in time cost forty thousand dollars. Every organization has a version of that Tuesday. The knowledge is real, it survived, and it is spread across a million files in a hundred formats, organized by whoever was closest to the filing cabinet that day. An organization knows more than anyone in it. The hard part is getting at it. Keyword search fails for a specific, fixable reason The obvious first fix is to index every word and search it. Type "liability cap," get every document containing "liability" and "cap." This fails, and it fails in ways worth naming precisely, because each failure points at what the real fix has to do. The contract does not say "liability cap." It says "limitation of liability." Two phrases, one meaning, zero shared keywords. Your search returns nothing and you conclude the document does not exist. The search bar cannot tell the difference between "we have no such contract" and "we have it, filed under different words." Matching words is not matching meaning. Search "termination" across an employee handbook and a supplier agreement and you get firing, contract expiry, and possibly a paragraph about ending a software license, ranked by nothing more meaningful than word frequency. People ask questions, not keywords. Nobody thinks in search terms. They think "have we ever agreed to a liability cap below a million?" A keyword engine has no idea that this is a question, let alone which words in it matter. What actually closes the gap The fix is to stop comparing words and start comparing meanings, which requires turning text into something you can measure distance in. An embedding model reads a passage and returns a list of
Are you a "data hoarder" when it comes to your health? Between Apple Health exports, Oura Ring logs, and Garmin CSVs, I found myself sitting on nearly 10 million rows of biometric data. Trying to analyze a multi-year trend of Heart Rate Variability (HRV) or Resting Heart Rate (RHR) in Excel is a one-way ticket to "Application Not Responding" hell. 📉 In this tutorial, we are diving into the world of Quantified Self data engineering. We will leverage DuckDB —the Swiss Army knife of OLAP—and Apache Superset to build a lightning-fast, local-first biometric dashboard. We'll explore how to turn messy JSON/CSV exports into high-performance insights using dbt for modeling and DuckDB for compute. If you've been looking for a way to master Data Engineering for personal use, this is the ultimate "learn in public" project! 🚀 The Architecture: From Raw Export to Real Insights Before we write a single line of SQL, let’s look at how the data flows. We want a system that is modular, fast, and stays entirely on our local machine (privacy first, right? 🥑). graph TD A[Raw Data: Apple Health / Oura / Garmin] -->|CSV/JSON| B(DuckDB Storage) B --> C{dbt Transformation} C -->|Cleaned Views| D[DuckDB Analytical Layer] D --> E[Apache Superset / Grafana] E -->|Visualization| F[Personal Biometric Dashboard] style B fill:#fff,stroke:#333,stroke-width:2px style D fill:#fbbf24,stroke:#333,stroke-width:2px Prerequisites 🛠️ To follow along, ensure you have the following in your tech stack: DuckDB : Our ultra-fast in-process analytical database. dbt-duckdb : For data modeling and transformations. Apache Superset : For the "wow factor" visualizations. Python 3.10+ : To glue it all together. Step 1: Ingesting the "Mess" with DuckDB DuckDB is incredible because it can query CSV and JSON files directly without an ingestion step. Let’s say you have a massive heart_rate.csv from an Apple Health export. Instead of waiting for a traditional DB to "load" the data, we can create a view instantly: -- Create a
Delete a Telegram bot and https://t.me/your_deleted_bot keeps returning HTTP 200 with a page that looks completely normal. Every link checker I know of — CI actions, directory scripts, monitoring cron jobs — reports it as healthy forever. If you maintain anything that lists Telegram bots, some fraction of your list is already dead and your checks are telling you it is fine. Reproducing it Pick a username that has never existed: curl -s -o /dev/null -w "%{http_code} \n " https://t.me/nonexistent_test_bot_77712 # 200 Two hundred. No redirect, no 404, no soft-404 marker in the body that a status check would catch. Where the truth is The status code is useless here, but the Open Graph title is not. I measured four usernames — two live bots, two that do not exist: URL og:title t.me/BookClassBot (live) BookClass t.me/instanavy_bot (live) StoryViewer - anonymous instagram story viewer tool t.me/nonexistent_test_bot_77712 Telegram: Contact @nonexistent_test_bot_77712 t.me/zzz_definitely_not_a_real_bot_9182 Telegram – a new era of messaging A live bot puts its own display name in og:title . A dead one gets one of two Telegram placeholders: Telegram: Contact @<username> , or — if the username is not even syntactically valid — the generic Telegram – a new era of messaging . That is the whole signal. curl -s https://t.me/some_bot | grep -o '<meta property="og:title" content="[^"]*"' The check Standard library only, no dependencies: import re import urllib.request UA = " Mozilla/5.0 (compatible; linkcheck/1.0) " DEAD_EXACT = { " Telegram – a new era of messaging " , " Telegram " } DEAD_PREFIX = " Telegram: Contact @ " def telegram_bot_exists ( url : str ) -> bool : """ True if the bot behind a t.me URL still exists. HTTP status is not usable here: Telegram serves 200 with a placeholder page for usernames that were deleted or never existed. The Open Graph title is what actually differs. """ req = urllib . request . Request ( url , headers = { " User-Agent " : UA }) with urllib .
I just made Whizz, an esoteric programming language that is full of capability and possible experimentation. Before I interest you in that, I'll explain to you something. What is an esoteric programming language? An esoteric language (or an esolang, colloquially), is a programming language designed to not fit the coding 'norms' or conventions. Take an example: BF ('BF' is an abbreviation and euphemism of brainf***). A standard language would notate a 'Hello, World!' program as something like: print ( " Hello, World! " ) BF, on the other hand, requires something like this: ++++++++ [ > ++++ [ > ++ > +++ > +++ > + <<<< - ] > + > + > - >> + [ < ] < - ] >> . > ---.+++++++..+++. >> . < -. < .+++.------.--------. >> +. > ++. As you can see, BF, like most esolangs, is different: it's hard to write and a puzzle. Whizz is inspired by BF, as its incrementing, decrementing and looping are inspired by it. I made Whizz because I thought languages like BF were way too monotonous to write. Esolangs should be hard and puzzling to write, but not laborious. BF requires you type '+' as many times you want to increment (without loops): so you have to find shortcuts and unscalable solutions, just to achieve your goal. In Whizz, just type that incrementation repetition count before the '+' sign, and there you have it! These wonderful features that Whizz boasts keep the challenge in esolang-ing, but contradictorily makes it more 'scalable'. Another notable feature is functions: the epitome of order. An example of a Whizz program would be: zeroToNine { [ create variables ] counter 10+ [ track state ] char 48+ [ print this one ] space 32+ [ space char ] ( char!+ [ print and increment char ] counter-; [ decrement counter and end if zero ] space! [ print space ] ) } zeroToNine* This, self explanatorily, outputs '0 1 2 3 4 5 6 7 8 9'. Again, in minimized form: c10+n48+s32+(n!+c-;s!) I genuinely hope you experiment with Whizz, and solve puzzles & challenges with it, as if it were BF! Install it
I went looking for a small improvement to an open-source tool. I found a number that pointed the wrong way, and then I found out why it had to. Live demo — paste your own file and watch it happen: https://pin-on-expand.onrender.com The setup Paritok is a 4B model that compresses AI coding-agent context. It sits between your agent and Anthropic or OpenAI, squeezes the file reads and tool output, and tells you what it saved. It's genuinely good work. Trained on 45,000 real agent trajectories, so it knows a function signature matters more than a debug line. Apache 2.0. Runs on a consumer GPU. Their benchmark numbers hold up. I wanted to build a policy improvement on top of it. To prove my improvement helped, I first had to measure what stock Paritok cost. That measurement is the whole story. Two numbers that disagree One coding-agent session. One 20,005-token file in context. Paritok's own /stats endpoint: 64.0% of input tokens saved. What the provider was actually POSTed: 69.2% more than sending the file with no compression at all. Same session. Same file. Both numbers correct. Where the missing tokens went Paritok is non-destructive by design, which is the good part. Compressed content gets tagged [REF:id] , and when the model needs the exact original it calls an injected expand_context tool to pull it back. Lossy on the wire, recoverable when it counts. The proxy answers that call itself . It appends the full original to a proxy-local thread and POSTs that thread upstream a second time. And stats is computed once, in process_request — before that loop runs. post 0: 6,919 tokens compressed request ← counted by /stats post 1: 26,924 tokens carries the full original ← never counted ───────── billed: 33,843 Then it compounds. The proxy conceals the virtual exchange from the client, so your agent never sees it. Next turn the agent re-sends the original file, Paritok re-compresses it to the same reference, and the model expands it again. Every turn. Forever. In fairness:
Most "beginner" Python courses aren't actually beginner courses. Lesson one usually opens with variables or print(), quietly assuming you already know what a terminal is, how to install something, or what a .py file even means. That assumption is exactly where most self-taught learners bounce — not because Python is hard, but because the ten minutes of orientation that would've made everything after it make sense got skipped. I built Codes Are Simple to start there instead. Session 1, Level 1: what is code, where do you type it, how do you open Command Prompt, how do you install Python and verify it worked. Nothing assumed. What it actually is A 45-session, self-paced curriculum — Python: The Universal Language, Zero to Pro — split into 10 tiers, from absolute foundations through OOP, files/errors, practical CLI projects, web/APIs, databases, and a final professional capstone. It's the first of a planned multi-track catalog on the same platform (web dev, AI, cyber, and data are mapped and coming next). Every lesson — all of them, across all 45 sessions — follows the same repeating shape: explanation → main example → 2 extra examples (variations/edge cases) → common mistake (shown alongside its fix) → practice → extra practice That "common mistake" section is the part I actually care about most. Almost every course I looked at shows only the correct code. This one shows what actually breaks for a beginner and why — because for someone learning alone with no instructor in the room, the error message is usually where the real learning happens, not the clean solution. Checkpoints land at fixed points mid-session (after Level 2, Level 6, and Level 9 — not just at the end), and every session closes with a real capstone project, not a toy exercise. The stack Cloudflare Workers + Pages + R2 — the site and all curriculum content, served as versioned JSON per session GitHub — version control for the whole content pipeline That last point is the part I think this community wil
I built a small Flask example that turns voicemail into a routing workflow. Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/voicemail-smart-router-python The app accepts either: a voicemail transcript an uploaded voicemail audio file For audio, it transcribes the voicemail first. Then it uses Telnyx AI Inference to classify the message and decide where it should go. Categories The app classifies voicemails into: urgent billing support sales spam routine Each category maps to a route: urgent -> Slack alert billing -> email support -> ticket queue sales -> CRM lead spam -> blocklist + archive routine -> daily digest Run it git clone https://github.com/team-telnyx/telnyx-code-examples.git cd telnyx-code-examples/voicemail-smart-router-python cp .env.example .env pip install -r requirements.txt python app.py Configure .env : TELNYX_API_KEY=your_telnyx_api_key AI_MODEL=zai-org/GLM-5.2 FALLBACK_MODEL=meta-llama/Llama-3.3-70B-Instruct HOST=127.0.0.1 Optional Slack webhook for urgent messages: SLACK_WEBHOOK=https://hooks.slack.com/... Classify a transcript curl -X POST http://localhost:5000/voicemails/transcript \ -H "Content-Type: application/json" \ -d '{ "transcript": "This is an emergency. Our production system is down and we need help immediately.", "caller_number": "+17177247292" }' Example response: { "category" : "urgent" , "confidence" : 1.0 , "priority" : "high" , "reason" : "The caller reports a production system outage requiring immediate attention." , "suggested_action" : "Escalate immediately to the on-call engineering team." , "route" : "slack" , "routed_to" : "#oncall-alerts" , "routing_status" : "delivered" } Process voicemail audio curl -X POST http://localhost:5000/voicemails/process \ -F "file=@voicemail.wav" \ -F "caller_number=+17177247292" For audio, the app calls: POST /v2/ai/audio/transcriptions using: distil-whisper/distil-large-v2 Then it calls: POST /v2/ai/chat/completions to classify the transcript. Routes included POST /voic
GitHub热门项目 | Python scraper based on AI | Stars: 28,792 | 44 stars today | 语言: Python
GitHub热门项目 | Native and Compact Structured Latents for 3D Generation | Stars: 9,511 | 412 stars today | 语言: Python
Use one key prefix per user, delete from the application every key you can name, and leave lifecycle rules to sweep the old temporary images nobody will ever ask for again. That's the whole design, and I've watched teams get it wrong in the same two ways for years: they either try to make the storage layer clever enough to know what a user is, or they hand the entire deletion problem to a lifecycle policy and then wonder why an account-deletion request took nine days to actually remove anything. I design data layers for a living, so I'm going to be blunt about the durability and consistency side of this rather than the upload-a-file-in-five-minutes side. Why the key layout matters more than the backend you pick Object storage has no folders. There's a flat keyspace and a delimiter convention, and every "folder" you see in a console is the UI grouping keys that share a prefix — which is good news, because it means the layout is yours to design and costs nothing to enforce. The layout I keep landing on is users/{userId}/generations/{yyyy-mm}/{uuid}.png , with a sibling users/{userId}/scratch/ prefix for renders that only exist so the browser can show a preview. Four properties come out of that shape, and they're the reason I don't get creative here. Listing a tenant's images is a single prefix query rather than a metadata scan, which matters because object stores generally don't let you search metadata server-side — you filter by prefix or you keep an index in your own database. Deleting an account becomes "enumerate one prefix, delete what's under it," so the compliance clock is something you control. The month segment keeps any single listing page from growing without bound, and it gives you a cheap way to write an age-based rule later. And the opaque UUID means the key never leaks a filename, a prompt, or an email address into a URL that might end up in a log or a referrer header. One thing I'd push back on if I saw it in review: don't put the user's email or usern
Build a Local LLM Chatbot with Ollama and Python tags: python, ai, llm, tutorial tags: python, ai, llm, tutorial Build a Local LLM Chatbot with Ollama and Python Imagine typing a question into your chatbot and getting a response in milliseconds, completely offline, with zero data leaving your machine. No API keys, no monthly subscription fees, and no privacy concerns about your data being sent to a cloud server. This isn’t a futuristic dream—it’s the reality of running a Local Large Language Model (LLM) on your own computer. With the rise of tools like Ollama , building a private AI chatbot in Python has become as simple as installing a few packages and writing a short script. Let’s dive in and build one together. Why Go Local? Before we write any code, it’s worth understanding why running an LLM locally is a game-changer. Cloud-based AI services like OpenAI or Anthropic are powerful, but they come with trade-offs: you pay per token, your data is processed on their servers, and you’re dependent on their uptime. A local LLM flips this model. You download the model once, run it on your hardware, and you have full control. Ollama is the engine that makes this accessible. It’s a lightweight, open-source tool that simplifies running LLMs like Llama 3, Phi 3, or Mistral on macOS, Linux, and Windows. It handles model downloads, memory management, and inference, exposing a simple API that Python can easily interact with [1][2]. Step 1: Install Ollama and Pull a Model The first step is getting Ollama on your machine. Visit ollama.com , click Download , and install the version for your operating system [2]. Once installed, verify it’s working by opening your terminal or Command Prompt and running: ollama --version If you see a version number, you’re ready to go. Next, you need a model. Ollama supports dozens of open-source models, but for a beginner-friendly chatbot, Llama 3.2 is a great choice. It’s small, fast, and surprisingly capable. To download it, run: ollama pull llam
Build a Dependency Vulnerability Scanner with Python tags: python, security, devops, tools tags: python, security, devops, tools Build a Dependency Vulnerability Scanner with Python Your requirements.txt looks clean, but one of those dependencies might be a ticking time bomb waiting to expose your users to a data breach. You don’t need to wait for a security audit to find out—you can build your own lightweight vulnerability scanner in Python today and integrate it directly into your workflow. Security isn’t just about writing secure code; it’s about knowing what’s running in your environment. With thousands of Python packages available, the odds that you’re using a library with a known CVE (Common Vulnerabilities and Exposures) are high. Instead of relying solely on third-party tools like pip-audit or safety (which are excellent, but sometimes opaque), building your own scanner gives you full control over how vulnerabilities are detected, reported, and acted upon. Let’s build a practical, working dependency vulnerability scanner from scratch. Why Build Your Own Scanner? Existing tools like pip-audit [13], safety [10], and PySentry [4] are powerful, but they come with limitations: They may not support your specific output format (e.g., custom JSON for CI). They might not integrate cleanly with your private PyPI registry. You can’t easily tweak the logic to match your team’s risk tolerance. Building your own scanner lets you: Query the NVD (National Vulnerability Database) API directly. Parse requirements.txt , pyproject.toml , or poetry.lock files flexibly. Generate reports in any format you need (Markdown, JSON, SARIF). Fail your CI pipeline automatically when critical CVEs are found. Plus, it’s a great learning exercise in cybersecurity, API integration, and Python parsing. Step 1: Set Up Your Environment Before writing code, prepare a clean virtual environment to avoid false positives from global packages: python3 -m venv scanner-env source scanner-env/bin/activat
Python Itertools: 10 Tricks for Cleaner Code tags: python, programming, tips, tutorial tags: python, programming, tips, tutorial Python Itertools: 10 Tricks for Cleaner Code You’ve probably written a loop that felt like it was dragging your code into the mud. Maybe you concatenated lists with + , zipped mismatched iterables and lost data, or manually tracked indices to count items. Before you add another for loop to your script, consider this: Python’s itertools module is a hidden superpower that can turn messy iteration logic into elegant, memory-efficient, and readable one-liners. Mastering itertools doesn’t just make your code cleaner—it makes it faster, especially when working with large datasets or infinite sequences. Let’s dive into 10 practical tricks you can use today to write better Python code. 1. Chain Multiple Lists Without Copying Memory When you need to merge several lists, the + operator creates a new list in memory. That’s wasteful for large datasets. Instead, use itertools.chain() , which yields items lazily—only when you need them. from itertools import chain list1 = [ 1 , 2 , 3 ] list2 = [ 4 , 5 ] list3 = [ 6 ] merged = chain ( list1 , list2 , list3 ) for item in merged : print ( item ) # 1, 2, 3, 4, 5, 6 This approach is memory-efficient and ideal for streaming or processing huge collections [6]. 2. Zip Uneven Lists Without Losing Data The built-in zip() stops when the shortest iterable ends. But what if you want to keep going and fill in missing values? Use itertools.zip_longest() with a fillvalue . from itertools import zip_longest names = [ " Alice " , " Bob " ] ids = [ 101 , 102 , 103 ] for name , id in zip_longest ( names , ids , fillvalue = " Unknown " ): print ( f " { name } : { id } " ) Output: Alice: 101 Bob: 102 Unknown: 103 This is perfect for aligning mismatched data streams [3]. 3. Generate Infinite Counters Gracefully Need a counter that never stops? itertools.count() gives you an infinite iterator starting from a specified value. A
Block Kit, signature verification, and the design decisions that stop a button click from becoming an incident. That screenshot is a bot asking permission to delete an EBS volume. Clicking Approve Remediation snapshots the volume, waits for the snapshot to complete, deletes the volume, and edits the message to say what happened. Getting that to work is mostly plumbing. Getting it to work safely , so that a stale click, a replayed request, or a resource someone protected in the meantime cannot cause damage, is the interesting part. This walks through both, using the Slack adapter from FinOps Sentinel . The shape of the problem Slack interactivity is two separate channels that only look like a conversation: Your app ──── incoming webhook ────▶ Slack channel │ user clicks │ Your app ◀─── HTTP POST ──────────────────┘ (a completely new request, from Slack's servers) The click arrives as an unauthenticated POST from the public internet to whatever URL you registered. Nothing about the request proves it came from Slack, or that a human clicked anything. That is the security problem in one sentence, and everything below follows from it. Part 1: Setting up the Slack app Create the app and get a webhook api.slack.com/apps → Create New App → From scratch Name it, pick your workspace Incoming Webhooks → toggle On → Add New Webhook to Workspace Choose a channel, click Allow , copy the URL SLACK_WEBHOOK_URL = https://hooks.slack.com/services/TXXXXX/BXXXXX/XXXXXXXX Webhooks post to exactly one channel and cannot read anything. For a notification bot that is the right amount of privilege: no OAuth flow, no bot token, no scopes to review. Enable interactivity Interactivity & Shortcuts → toggle On → set the Request URL: https://your-domain.example/callbacks/slack Locally you need a tunnel: ngrok http 8000 # → https://a1b2c3d4.ngrok.app # Request URL: https://a1b2c3d4.ngrok.app/callbacks/slack The free ngrok URL changes on every restart, and you must update Slack each time. Save your