![]() |
VOOZH | about |
I just released polycvss v0.4.0.
polycvss is a Rust library to parse and score CVSS vector strings.
Changes since polycvss v0.3.5:
Err::InvalidChar, check for invalid characters when parsing vector stringsTryFrom<String>This time it'll be different...
Recent GitHub problems:
Gentoo and Ghostly are migrating away from GitHub; the former because of the aggressive Copilot marketing, and the latter because of the repeated outages.
Microsoft has apologized and vowed to do better, but I think GitHub will continue to deteriorate.
Why?
Microsoft has made pledges like this before. In 2024 after a series of high-profile security blunders, a Microsoft representative said “We are making security our top priority” and announced an initiative which made executive pay partially dependent on meeting security plans and milestones. Despite that assurance, in 2025 Microsoft was criticized by Senator Ron Wyden for “dangerous software engineering decisions” and in 2026 poor Microsoft cloud security was at the center of a scathing ProPublica investigation.
The decline of GitHub was anticipated when Microsoft acquired GitHub in 2018 and became almost inevitable once Microsoft folded GitHub into their CoreAI division last year. As long as they are incentivized to do so, Microsoft employees will continue to prioritize aggressive Copilot marketing over any other concern, including platform reliability.
Many of the problems listed above can be traced directly to deliberate changes made by Microsoft in pursuit of their disasterous, money-losing “AI” initiative. Degrading the quality of a platform to maximize short-term shareholder profit is textbook enshittification 2.
So what should we do?
I don’t know exactly, but here are some suggestions:
I suspect GitHub’s problems will compound as the “AI” bubble begins to collapse. Unfortunately I don’t know if that means “next month” or “next year” 3.
Regardless, now seems like a good time to proceed towards the exit. I’m going to start migrating my personal projects to Codeberg.
Updates
Technically you can opt-out of the LLM training and CLI telemetry if you a) know that it is happening and b) can find the correct setting, but collecting users’ data without their knowledge or explicit consent is a deceptive design pattern known as Privacy Zuckering. ↩︎ ↩︎
We have seen this pattern of decay on other development platforms too. In 2013 SourceForge added an optional feature to place ad-supported content into binary installers in a dubious attempt at generating revenue, and by 2015 they were hijacking project pages and adding adware and malware to project downloads without developers' consent. ↩︎
For more information I recommend following Ed Zitron; his reporting is detailed and his predictions about the “AI” bubble have been solid. ↩︎
Forgejo runners.
Several months ago I set up a personal Forgejo server and several runners. I have been using it to manage releases for a couple of projects.
I documented the setup process here: Forgejo Setup.
Update (2026-04-26): Setup guide updated for Forgejo v15.0.
This morning I woke up to an oddly unresponsive web server. The TLS handshake would complete and then requests would block until the connection timed out.
At first I thought it was a transient error, so I restarted Apache. That didn’t fix the problem, so I restarted the VM, and then the physical machine.
The cause was a slop bot flood; apparently they discovered git.pablotron.org and decided to blunderbuss the server by simultaneously scraping the site.
git.pablotron.org is (was) a cgit instance with a relatively lax robots.txt. cgit is extremely fast, but it still generates content dynamically. This means that git.pablotron.org is (was) susceptible to what is effectively a DDoS attack.
I tried tweaking Apache parameters but that didn’t help, so I’ve shut git.pablotron.org down for now. I’ll bring it back once I have time to add some slop mitigation.
Fortunately pablotron.org itself is less vulnerable to this kind of abuse because it is statically generated and has a restrictive robots.txt.
Update (2026-03-30): Improve wording.
I restarted Firefox after an update and my laptop fan started going berzerk.
The culprit was a background thread pegged at 100% CPU for one of the new AI “features” in Firefox; presumably doing inference for a local model.
I disabled this junk, and you should too.
Steps:
This absurd gaggle of misfeatures should have always been opt-in, not opt-out.
I also resent the Orwellian doublespeak that Mozilla is using to describe this nonsense.
Definitions:
The intentional obfuscation suggests that Mozilla was aware it was inappropriate to enable this claptrap by default.
George Lakoff wrote about the use of Orwellian language in politics in “Don’t Think of an Elephant!”:
But we should recognize that they use Orwellian language precisely when they have to: when they are weak, when they cannot just come out and say what they mean. Imagine if they came out supporting a “Dirty Skies Bill” or a “Forest Destruction Bill” or a “Kill Public Education” bill. They would lose. They are aware people do not support what they are really trying to do.
Orwellian language points to weakness – Orwellian weakness. When you hear Orwellian language, note where it is, because it is a guide to where they are vulnerable.
I just released the first version of pbech32, a Rust library for encoding and decoding Bech32 data.
Bech32 is a fast and user-friendly base 32 encoding format that includes a namespace and checksum.
Display and
FromStr traits.Decode from string:
usepbech32::Bech32;lets="a1qypqxpq9mqr2hj";// bech32m-encoded string
letgot: Bech32=s.parse()?;// decode string
assert_eq!(got.hrp.to_string(),"a");// check human-readable part
assert_eq!(got.data,vec![1,2,3,4,5]);// check data
Encode to string:
usepbech32::{Bech32,Hrp,Scheme};letscheme=Scheme::Bech32m;// checksum scheme
lethrp: Hrp="a".parse()?;// human-readable part
letdata=vec![1,2,3,4,5];// data
letgot=Bech32{scheme,hrp,data}.to_string();// encode as string
assert_eq!(got,"a1qypqxpq9mqr2hj");// check result
Encode to a writer:
usestd::io::Write;usepbech32::{Encoder,Hrp,Scheme};letmutvec: Vec<u8>=Vec::new();// output vector
lethrp: Hrp="hello".parse()?;// human readable part
{letmutenc=Encoder::new(&mutvec,Scheme::Bech32m,hrp)?;// create encoder
enc.write_all(b"folks")?;// write data
enc.flush()?;// flush encoder (RECOMMENDED)
}letgot=str::from_utf8(vec.as_ref())?;// convert output vector to string
assert_eq!(got,"hello1vehkc6mn27xpct");// check result
More examples are available in the examples/ directory of
the pbech32 Git repository.
impl Drop for Encoder. Fix
clippy warnings. Minor documentation improvements.Happy New Year!
The number 2026 has two factors: 2 and 1013. I wondered “is 1013 prime?” and, for fun, “can I solve this in my head?” (e.g. no calculator, computer, or pen and paper).
If 1013 is not prime, then it is composite and must have at least one odd prime factor ≤ ⌊√1013⌋.
(We know the factor – if it exists – is odd because factors of odd composites are always odd, and we know it is prime because of the fundamental theorem of arithmetic).
So our approach will be to check odd primes from 3 to √1013 to see if any divide 1013.
Unfortunately we don’t know √1013. Fortunately 1013 is close to 1024, and 1024 is even power of 2. So let’s use 1024 to approximate √1013:
So we need to test odd primes in the range [3,31] to see if any divide 1013.
Before that, though, we prune the list of potential factors with the divisibility rules. We remove:
1+1+3=5 and 3∤55*3+101=116, 5*6+11=41, and 7∤41.(I didn’t remember the divisibility rules for the range [11,29], so I checked if any of the primes divide 1013 instead).
31 is the only remaining potential factor after pruning. To check it,
we can either use the Euclidean algorithm to see if gcd(31, 1013) != 1 or do some trial arithmetic. I chose the latter:
1013 does not have any odd prime factors in the range [3,√1013], so it must be prime.
Let’s check our work with SymPy:
>>> import sympy
>>> sympy.ntheory.primetest.isprime(1013)
True
Success!
I just released polycvss version 0.2.0.
polycvss is a Rust library to parse and score CVSS vector strings.
Features:
serde build feature.Here is an example tool which parses the first command-line argument as a CVSS vector string, then prints the score and severity:
usepolycvss::{Err,Score,Severity,Vector};fn main()-> Result<(),Err>{letargs: Vec<String>=std::env::args().collect();// get cli args
ifargs.len()==2{letvec: Vector=args[1].parse()?;// parse string
letscore=Score::from(vec);// get score
letseverity=Severity::from(score);// get severity
println!("{score}{severity}");// print score and severity
}else{letname=args.first().map_or("app",|s|s);// get app name
eprintln!("Usage: {name} [VECTOR]");// print usage
}Ok(())}Here is the example tool output for a CVSS v2 vector string, a CVSS v3 vector string, and a CVSS v4 vector string:
# test with cvss v2 vector string
$ cvss-score "AV:A/AC:H/Au:N/C:C/I:C/A:C"
6.8 MEDIUM
# test with cvss v3 vector string
$ cvss-score "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
9.8 CRITICAL
# test with cvss v4 vector string
$ cvss-score "CVSS:4.0/AV:L/AC:H/AT:N/PR:N/UI:P/VC:L/VI:L/VA:L/SC:H/SI:H/SA:H"
5.2 MEDIUM
This example tool is included in the Git repository as
src/bin/cvss-score.rs.
polycvss::v4::Nomenclature and improve documentation.Error messages, remove unreleased CVSS v2.x Version variants, and improve documentation.impl From<Vector> for Severity
and examples/ directory.v4-scores example, update
dependencies, documentation and formatting fixes.impl std::error::Error for polycvss::Err. Remove unused code. Minor
documentation fixes and improvements.Err::InvalidChar and check for
invalid characters when parsing vector strings. Add fuzz targets.
Documentation fixes and improvements.Last week I installed Armbian on an Odroid N2L. The installation steps, installation results, and fixes for some problems are documented below.
DF00FAF1C577104B50BF1D0093D6889F9F0E78D5):wget -O- https://apt.armbian.com/armbian.key | gpg -- import -gpg --verify Armbian_community_25.8.0-trunk.8_Odroidn2l_bookworm_current_6.12.28_minimal.img.xz{.asc,}unxz Armbian_community_25.8.0-trunk.8_Odroidn2l_bookworm_current_6.12.28_minimal.img.xzsudo dd if=Armbian_community_25.8.0-trunk.8_Odroidn2l_bookworm_current_6.12.28_minimal.img of=/dev/sda bs=1M status=progress/mnt/tmp:sudo mount /dev/sda2 /mnt/tmp/mnt/tmp/root/.not_logged_in_yet. My populated autoconfig is
here, but it did not work as expected;
see below.Worked as expected:
Did not work as expected:
1234.To correct these problems I connected a keyboard and monitor and did the following:
root with the password 1234./etc/netplan/20-eth-fixed-mac.yaml and fixed the
errors. The corrected version is below.netplan apply to apply the corrected network configuration.Here is the corrected /etc/netplan/20-eth-fixed-mac.yaml:
network:version:2After fixing networking, I did the following:
/etc/ssh/sshd_config to disable root logins and password
logins.apt-get update && apt-get upgrade.unattended-upgrades.Odroid N2L running Armbian.
Yesterday I ported the caching and security headers
from the Apache configuration for the public site to the Nginx
configuration for the Tor mirror.
The caching headers are particularly helpful for the Tor mirror.
The updated Nginx configuration and additional documentation are here: Site Backend - Onion Service.