security: Libvips Native Vulnerabilities Remediation - #1680
Merged
csxark merged 2 commits intoAug 28, 2026
Merged
Annotations
5 errors and 1 warning
|
Run accessibility tests
Process completed with exit code 1.
|
|
Run accessibility tests:
tests/unit/a11y/contrast.test.ts#L65
AssertionError: expected '\'use client\'\n\nimport { useMemo, u…' not to contain 'bg-amber-200'
- Expected
+ Received
- bg-amber-200
+ 'use client'
+
+ import { useMemo, useState, useId, useEffect } from 'react'
+ import { type AesMode } from '@/lib/cipher/symmetric/aes'
+ import { cryptoWorkerClient } from '@/lib/workers/cryptoWorkerClient'
+
+ const MODES: { id: AesMode; name: string; blurb: string }[] = [
+ { id: 'ECB', name: 'ECB', blurb: 'Only the changed block differs; equal blocks stay equal.' },
+ { id: 'CBC', name: 'CBC', blurb: 'The changed block and every block after it differ.' },
+ { id: 'CFB', name: 'CFB', blurb: 'One byte in-block, then every following block differs.' },
+ { id: 'OFB', name: 'OFB', blurb: 'Keystream is independent — only the one byte differs.' },
+ { id: 'CTR', name: 'CTR', blurb: 'Counter keystream — only the one byte differs.' },
+ ]
+
+ const KEY = '2b7e151628aed2a6abf7158809cf4f3c'
+ const IV = '000102030405060708090a0b0c0d0e0f'
+
+ function flipByte(text: string, index: number): string {
+ if (index < 0 || index >= text.length) return text
+ const code = text.charCodeAt(index)
+ const next = code === 65 ? 66 : 65
+ return text.slice(0, index) + String.fromCharCode(next) + text.slice(index + 1)
+ }
+
+ export default function ModesLab() {
+ const [text, setText] = useState('The magic words are squeamish ossifrage.')
+ const [flipIndex, setFlipIndex] = useState(4)
+ const textInputId = useId()
+ const rangeInputId = useId()
+ const resultsId = useId()
+
+ const safeIndex = Math.min(flipIndex, Math.max(0, text.length - 1))
+ const flipped = useMemo(() => flipByte(text, safeIndex), [text, safeIndex])
+ const [rows, setRows] = useState<any[]>([])
+ const [loading, setLoading] = useState(false)
+
+ useEffect(() => {
+ let active = true
+ const calculate = async () => {
+ setLoading(true)
+ try {
+ const result = await cryptoWorkerClient.runCryptoOperation<any[]>('batchModesLab', {
+ text,
+ flipped,
+ key: KEY,
+ iv: IV,
+ modes: MODES.map((m) => m.id),
+ })
+ if (active) setRows(MODES.map((m, i) => ({ ...m, ...result[i] })))
+ } catch (err) {
+ console.error('Worker failed:', err)
+ } finally {
+ if (active) setLoading(false)
+ }
+ }
+ calculate()
+ return () => {
+ active = false
+ }
+ }, [text, flipped])
+
+ const announcement = loading
+ ? 'Calculating cipher mode differences.'
+ : rows.length > 0
+ ? `Cipher mode comparison updated. ${rows.map((row) => `${row.name}: ${row.changedCount} of ${row.total} bytes changed`).join('. ')}`
+ : 'No cipher mode comparison results available.'
+
+ return (
+ <div className="flex flex-col gap-6">
+ <div className="rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-900/40">
+ <div className="flex flex-col gap-1.5">
+ <label htmlFor={textInputId} className="text-xs font-semibold text-zinc-500 dark:text-zinc-400">
+ Plaintext (ASCII)
+ </label>
+ <input
+ id={textInputId}
+ type="text"
+ value={text}
+ onChange={(e) => setText(e.target.value)}
+ className="w-full rounded-lg border border-zinc-200 bg-zinc-50/50 p-2.5 font-mono text-sm text-zinc-900 outline-none transition-all focus:border-teal-500 focus:bg-white dark:border-zinc-800 dark:bg-zinc-950/40 dark:text-zinc-100 dark:focus:border-teal-400"
+ />
+ </div>
+
+ <div className="mt-4 flex flex-col gap-1.5">
+ <div className="flex items-center justify-between">
+ <label htmlFor={rangeInputId} className="text-xs font-semibold text-zinc-500 dark:text-zinc-400">
+ Flip one plaintext byte (position {safeIndex})
+ </label>
+ <span className="font-mono text-xs text-zinc-500 dark:text-zinc-400">
+ key {KEY.slice(0, 8)}… · iv {IV.slice(0, 8)}��
|
|
Run accessibility tests:
tests/unit/a11y/cipher-sandbox-900.test.ts#L24
AssertionError: expected '\'use client\'\n\nimport React, { use…' to contain 'aria-label="Number of rounds"'
- Expected
+ Received
- aria-label="Number of rounds"
+ 'use client'
+
+ import React, { useState, useMemo } from 'react'
+ import {
+ CipherPipelineStage,
+ executeCipherPipeline,
+ calculateAvalancheEffect,
+ calculateFrequencyAnalysis,
+ validatePipelineInvertibility,
+ CaesarStageConfig,
+ AffineStageConfig,
+ XorStageConfig,
+ PBoxStageConfig,
+ ColumnarStageConfig,
+ BlockSwapStageConfig,
+ CyclicShiftStageConfig,
+ ReverseStageConfig,
+ SBoxStageConfig,
+ } from '@/lib/cipher/sandbox/cipherSandboxEngine'
+ import { CIPHER_PRESETS, CipherPreset } from '@/lib/cipher/sandbox/presets'
+ import {
+ Play,
+ ArrowUp,
+ ArrowDown,
+ Trash2,
+ Plus,
+ Lock,
+ Unlock,
+ Layers,
+ Sparkles,
+ AlertTriangle,
+ CheckCircle2,
+ Copy,
+ Download,
+ Upload,
+ BarChart2,
+ Activity,
+ Eye,
+ RefreshCw,
+ Sliders,
+ } from 'lucide-react'
+
+ export default function CipherSandbox() {
+ const [selectedPresetId, setSelectedPresetId] = useState<string>('spn_2round')
+ const [input, setInput] = useState<string>('CRYPTOGRAPHY')
+ const [direction, setDirection] = useState<'encrypt' | 'decrypt'>('encrypt')
+ const [rounds, setRounds] = useState<number>(2)
+ const [copied, setCopied] = useState<boolean>(false)
+ const [activeTab, setActiveTab] = useState<'trace' | 'metrics' | 'export'>('trace')
+
+ // Stages State
+ const [stages, setStages] = useState<CipherPipelineStage[]>(
+ CIPHER_PRESETS[0].stages
+ )
+
+ // Load Preset Handler
+ const handleSelectPreset = (presetId: string) => {
+ setSelectedPresetId(presetId)
+ const preset = CIPHER_PRESETS.find((p) => p.id === presetId)
+ if (preset) {
+ setStages(JSON.parse(JSON.stringify(preset.stages)))
+ setInput(preset.defaultInput)
+ setRounds(preset.rounds)
+ }
+ }
+
+ // Stage Manipulation
+ const handleAddStage = (category: 'substitution' | 'permutation', subType: string) => {
+ const newId = `stage-${Date.now()}`
+ let newStage: CipherPipelineStage
+
+ if (category === 'substitution') {
+ if (subType === 'caesar') {
+ newStage = {
+ id: newId,
+ name: 'Caesar Shift',
+ category: 'substitution',
+ subType: 'caesar',
+ shift: 3,
+ enabled: true,
+ }
+ } else if (subType === 'affine') {
+ newStage = {
+ id: newId,
+ name: 'Affine Transform',
+ category: 'substitution',
+ subType: 'affine',
+ a: 5,
+ b: 8,
+ enabled: true,
+ }
+ } else if (subType === 'xor') {
+ newStage = {
+ id: newId,
+ name: 'XOR Key',
+ category: 'substitution',
+ subType: 'xor',
+ key: 'KEY',
+ enabled: true,
+ }
+ } else {
+ newStage = {
+ id: newId,
+ name: 'S-Box Mapping',
+ category: 'substitution',
+ subType: 'sbox',
+ mapping: { A: 'Q', B: 'W', C: 'E', D: 'R', E: 'T' },
+ enabled: true,
+ }
+ }
+ } else {
+ if (subType === 'pbox') {
+ newStage = {
+ id: newId,
+ name: 'P-Box Permutation',
+ category: 'permutation',
+ subType: 'pbox',
+ blockSize: 4,
+ permutation: [2, 0, 3, 1],
+ enabled: true,
+ }
+ } else if (subType === 'columnar') {
+ newStage = {
+ id: newId,
+ name: 'Columnar Transposition',
+ category: 'permutation',
+ subType: 'columnar',
+ columns: 3,
+ keyOrder: [2, 0, 1],
+ enabled: true,
+ }
+ } else if (subType === 'block_swap') {
+ newStage = {
+ id: newId,
+ name: 'Block Swap',
+ category: 'permutation',
+ subType: 'block_swap',
+ blockSize: 2,
+ enable
|
|
Run accessibility tests:
tests/unit/a11y/cipher-sandbox-900.test.ts#L18
AssertionError: expected '\'use client\'\n\nimport React, { use…' to contain 'aria-label="Copy output"'
- Expected
+ Received
- aria-label="Copy output"
+ 'use client'
+
+ import React, { useState, useMemo } from 'react'
+ import {
+ CipherPipelineStage,
+ executeCipherPipeline,
+ calculateAvalancheEffect,
+ calculateFrequencyAnalysis,
+ validatePipelineInvertibility,
+ CaesarStageConfig,
+ AffineStageConfig,
+ XorStageConfig,
+ PBoxStageConfig,
+ ColumnarStageConfig,
+ BlockSwapStageConfig,
+ CyclicShiftStageConfig,
+ ReverseStageConfig,
+ SBoxStageConfig,
+ } from '@/lib/cipher/sandbox/cipherSandboxEngine'
+ import { CIPHER_PRESETS, CipherPreset } from '@/lib/cipher/sandbox/presets'
+ import {
+ Play,
+ ArrowUp,
+ ArrowDown,
+ Trash2,
+ Plus,
+ Lock,
+ Unlock,
+ Layers,
+ Sparkles,
+ AlertTriangle,
+ CheckCircle2,
+ Copy,
+ Download,
+ Upload,
+ BarChart2,
+ Activity,
+ Eye,
+ RefreshCw,
+ Sliders,
+ } from 'lucide-react'
+
+ export default function CipherSandbox() {
+ const [selectedPresetId, setSelectedPresetId] = useState<string>('spn_2round')
+ const [input, setInput] = useState<string>('CRYPTOGRAPHY')
+ const [direction, setDirection] = useState<'encrypt' | 'decrypt'>('encrypt')
+ const [rounds, setRounds] = useState<number>(2)
+ const [copied, setCopied] = useState<boolean>(false)
+ const [activeTab, setActiveTab] = useState<'trace' | 'metrics' | 'export'>('trace')
+
+ // Stages State
+ const [stages, setStages] = useState<CipherPipelineStage[]>(
+ CIPHER_PRESETS[0].stages
+ )
+
+ // Load Preset Handler
+ const handleSelectPreset = (presetId: string) => {
+ setSelectedPresetId(presetId)
+ const preset = CIPHER_PRESETS.find((p) => p.id === presetId)
+ if (preset) {
+ setStages(JSON.parse(JSON.stringify(preset.stages)))
+ setInput(preset.defaultInput)
+ setRounds(preset.rounds)
+ }
+ }
+
+ // Stage Manipulation
+ const handleAddStage = (category: 'substitution' | 'permutation', subType: string) => {
+ const newId = `stage-${Date.now()}`
+ let newStage: CipherPipelineStage
+
+ if (category === 'substitution') {
+ if (subType === 'caesar') {
+ newStage = {
+ id: newId,
+ name: 'Caesar Shift',
+ category: 'substitution',
+ subType: 'caesar',
+ shift: 3,
+ enabled: true,
+ }
+ } else if (subType === 'affine') {
+ newStage = {
+ id: newId,
+ name: 'Affine Transform',
+ category: 'substitution',
+ subType: 'affine',
+ a: 5,
+ b: 8,
+ enabled: true,
+ }
+ } else if (subType === 'xor') {
+ newStage = {
+ id: newId,
+ name: 'XOR Key',
+ category: 'substitution',
+ subType: 'xor',
+ key: 'KEY',
+ enabled: true,
+ }
+ } else {
+ newStage = {
+ id: newId,
+ name: 'S-Box Mapping',
+ category: 'substitution',
+ subType: 'sbox',
+ mapping: { A: 'Q', B: 'W', C: 'E', D: 'R', E: 'T' },
+ enabled: true,
+ }
+ }
+ } else {
+ if (subType === 'pbox') {
+ newStage = {
+ id: newId,
+ name: 'P-Box Permutation',
+ category: 'permutation',
+ subType: 'pbox',
+ blockSize: 4,
+ permutation: [2, 0, 3, 1],
+ enabled: true,
+ }
+ } else if (subType === 'columnar') {
+ newStage = {
+ id: newId,
+ name: 'Columnar Transposition',
+ category: 'permutation',
+ subType: 'columnar',
+ columns: 3,
+ keyOrder: [2, 0, 1],
+ enabled: true,
+ }
+ } else if (subType === 'block_swap') {
+ newStage = {
+ id: newId,
+ name: 'Block Swap',
+ category: 'permutation',
+ subType: 'block_swap',
+ blockSize: 2,
+ enabled: true,
+
|
|
Run accessibility tests:
tests/unit/a11y/cipher-sandbox-900.test.ts#L12
AssertionError: expected '\'use client\'\n\nimport React, { use…' to contain 'aria-label="Move stage up"'
- Expected
+ Received
- aria-label="Move stage up"
+ 'use client'
+
+ import React, { useState, useMemo } from 'react'
+ import {
+ CipherPipelineStage,
+ executeCipherPipeline,
+ calculateAvalancheEffect,
+ calculateFrequencyAnalysis,
+ validatePipelineInvertibility,
+ CaesarStageConfig,
+ AffineStageConfig,
+ XorStageConfig,
+ PBoxStageConfig,
+ ColumnarStageConfig,
+ BlockSwapStageConfig,
+ CyclicShiftStageConfig,
+ ReverseStageConfig,
+ SBoxStageConfig,
+ } from '@/lib/cipher/sandbox/cipherSandboxEngine'
+ import { CIPHER_PRESETS, CipherPreset } from '@/lib/cipher/sandbox/presets'
+ import {
+ Play,
+ ArrowUp,
+ ArrowDown,
+ Trash2,
+ Plus,
+ Lock,
+ Unlock,
+ Layers,
+ Sparkles,
+ AlertTriangle,
+ CheckCircle2,
+ Copy,
+ Download,
+ Upload,
+ BarChart2,
+ Activity,
+ Eye,
+ RefreshCw,
+ Sliders,
+ } from 'lucide-react'
+
+ export default function CipherSandbox() {
+ const [selectedPresetId, setSelectedPresetId] = useState<string>('spn_2round')
+ const [input, setInput] = useState<string>('CRYPTOGRAPHY')
+ const [direction, setDirection] = useState<'encrypt' | 'decrypt'>('encrypt')
+ const [rounds, setRounds] = useState<number>(2)
+ const [copied, setCopied] = useState<boolean>(false)
+ const [activeTab, setActiveTab] = useState<'trace' | 'metrics' | 'export'>('trace')
+
+ // Stages State
+ const [stages, setStages] = useState<CipherPipelineStage[]>(
+ CIPHER_PRESETS[0].stages
+ )
+
+ // Load Preset Handler
+ const handleSelectPreset = (presetId: string) => {
+ setSelectedPresetId(presetId)
+ const preset = CIPHER_PRESETS.find((p) => p.id === presetId)
+ if (preset) {
+ setStages(JSON.parse(JSON.stringify(preset.stages)))
+ setInput(preset.defaultInput)
+ setRounds(preset.rounds)
+ }
+ }
+
+ // Stage Manipulation
+ const handleAddStage = (category: 'substitution' | 'permutation', subType: string) => {
+ const newId = `stage-${Date.now()}`
+ let newStage: CipherPipelineStage
+
+ if (category === 'substitution') {
+ if (subType === 'caesar') {
+ newStage = {
+ id: newId,
+ name: 'Caesar Shift',
+ category: 'substitution',
+ subType: 'caesar',
+ shift: 3,
+ enabled: true,
+ }
+ } else if (subType === 'affine') {
+ newStage = {
+ id: newId,
+ name: 'Affine Transform',
+ category: 'substitution',
+ subType: 'affine',
+ a: 5,
+ b: 8,
+ enabled: true,
+ }
+ } else if (subType === 'xor') {
+ newStage = {
+ id: newId,
+ name: 'XOR Key',
+ category: 'substitution',
+ subType: 'xor',
+ key: 'KEY',
+ enabled: true,
+ }
+ } else {
+ newStage = {
+ id: newId,
+ name: 'S-Box Mapping',
+ category: 'substitution',
+ subType: 'sbox',
+ mapping: { A: 'Q', B: 'W', C: 'E', D: 'R', E: 'T' },
+ enabled: true,
+ }
+ }
+ } else {
+ if (subType === 'pbox') {
+ newStage = {
+ id: newId,
+ name: 'P-Box Permutation',
+ category: 'permutation',
+ subType: 'pbox',
+ blockSize: 4,
+ permutation: [2, 0, 3, 1],
+ enabled: true,
+ }
+ } else if (subType === 'columnar') {
+ newStage = {
+ id: newId,
+ name: 'Columnar Transposition',
+ category: 'permutation',
+ subType: 'columnar',
+ columns: 3,
+ keyOrder: [2, 0, 1],
+ enabled: true,
+ }
+ } else if (subType === 'block_swap') {
+ newStage = {
+ id: newId,
+ name: 'Block Swap',
+ category: 'permutation',
+ subType: 'block_swap',
+ blockSize: 2,
+ enabled: tru
|
|
Complete job
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v4, actions/setup-node@v4, actions/upload-artifact@v4. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|
background
wait
wait-all
cancel
parallel
Loading