gitlab-foss (5, Ruby), darktable (5, C), suitecrm (6, PHP), inkscape (4, C++), calibre (4, Python), scribus (4, C++), vscode (4, TypeScript), digikam (4, C++). Note: darktable-0004 and digikam-0004 are CWE-312 (cleartext credential logging), not CWE-407.
6.1 KiB
digiKam — CWE-407 + CWE-312 Disclosure Brief
2026-04-13 · Patches available — awaiting upstream merge
Finding
Four defects in digiKam across the Haar similarity search, GPS marker tiling, XMP keyword metadata, and OAuth2 authentication. Three are CWE-407 algorithmic complexity (O(n²) from QList::contains() / QStringList::contains() inside loops), and one is CWE-312 (OAuth2 client secret logged in plaintext). All patched.
The Defects
digikam-0001 (PATCHED — HIGH, CWE-407): core/libs/database/haar/haariface.cpp:680
// In fulfillsRestrictions — fires for every image in the database per similarity search:
if (targetAlbums.isEmpty() || targetAlbums.contains(albumId))
// QList<int>::contains — O(A) per image
fulfillsRestrictions() checks whether each image belongs to a target album using QList<int>::contains(). Called for every image in the database (N) during a similarity search. O(N * A) where N = images, A = target albums. For N=50,000 images and A=100 albums: 5,000,000 linear scans per search.
digikam-0002 (PATCHED — MEDIUM, CWE-407): core/utilities/geolocation/mapsearches/gpsmarkertiler.cpp:319
// In tile splitting — fires on every map zoom level change:
if (!newTile->imagesId.contains(currentImageId)) // QList<qlonglong>::contains — O(I)
{
newTile->imagesId.append(currentImageId);
}
GPS marker tiling deduplicates image IDs when splitting tiles using QList::contains(). O(I²) per tile split where I = images in the tile. For a city with 2000 geotagged photos in one tile: 4,000,000 comparisons.
digikam-0003 (PATCHED — MEDIUM, CWE-407): core/libs/metadataengine/engine/metaengine_xmp.cpp:881 and dmetadata/dmetadata_xmp.cpp:66
// In addToXmpTagStringBag — fires per image during batch metadata sync:
if (!newEntries.contains(*it)) // QStringList::contains — O(N) per keyword
newEntries.append(*it);
XMP keyword bag merge uses QStringList::contains() inside a loop. O(O*N) where O = old entries, N = new entries. Called per-image during batch metadata sync. With 100 keywords per image and 10,000 images in batch: O(100² * 10,000) total. Same pattern in removeFromXmpTagStringBag() and the DMetadata variants — 4 sites total.
digikam-0004 (PATCHED — HIGH, CWE-312): core/libs/dplugins/webservices/o2/src/o2.cpp:280
// In O2::onVerificationReceived — OAuth2 token exchange:
qDebug() << QString("O2::onVerificationReceived: Exchange access code data:\n%1")
.arg(QString(data));
// data contains: client_id=...&client_secret=ACTUAL_SECRET&...
After building the OAuth2 token exchange POST body (which includes clientSecret_), the full QByteArray is printed unconditionally via qDebug(). Any user with Qt debug logging enabled (debug build, QT_LOGGING_RULES=*) will have their OAuth2 client secret written to stderr/log files in plaintext. Affects all digiKam cloud service plugins using OAuth2: Google Photos, Flickr, OneDrive, etc.
Complexity Proof
digikam-0001: At N=50,000 images, A=100 target albums:
- Defective: 50,000 × 100 = 5,000,000 comparisons per search
- Fixed: 50,000 × 1 = 50,000 QSet lookups
- ~250× speedup.
digikam-0002: At I=2000 images per tile:
- Defective: 2000 × 1999 / 2 = ~2,000,000 comparisons per tile split
- Fixed: 2000 hash lookups + inserts
- ~250× speedup.
digikam-0003: At K=200 keywords per image:
- Defective: 200 × 200 = 40,000 comparisons per image
- Fixed: 200 hash lookups per image
- ~100× speedup.
Impact
digiKam serves photographers managing large photo libraries — tens of thousands to hundreds of thousands of images. The Haar similarity search defect (0001) fires on every duplicate detection and similarity search, the primary tool for organizing large photo imports. At 50,000 images with 100 target albums, every search performs 5 million unnecessary comparisons. The GPS tiling defect (0002) fires on every map zoom, making geographic browsing of geotagged collections sluggish. The XMP keyword defect (0003) fires during batch metadata sync — a common workflow when organizing photos after import. The credential defect (0004) exposes OAuth2 secrets for cloud photo services.
The Fix
digikam-0001: Convert targetAlbums from QList<int> to QSet<int> at the call site:
// Before
bool fulfillsRestrictions(..., const QList<int>& targetAlbums, ...)
// After — O(1) QSet lookup
const QSet<int> targetAlbumsSet(targetAlbums.begin(), targetAlbums.end());
bool fulfillsRestrictions(..., const QSet<int>& targetAlbums, ...)
digikam-0002: Add QSet<qlonglong> imagesIdSet alongside the existing QList:
if (!newTile->imagesIdSet.contains(currentImageId))
{
newTile->imagesIdSet.insert(currentImageId);
newTile->imagesId.append(currentImageId);
}
digikam-0003: Convert the lookup list to QSet<QString> before the loop:
QSet<QString> newEntriesSet(newEntries.constBegin(), newEntries.constEnd());
if (!newEntriesSet.contains(*it))
newEntries.append(*it);
digikam-0004: Redact the request body from the debug log:
// Before
qDebug() << QString("...Exchange access code data:\n%1").arg(QString(data));
// After
qDebug() << "O2::onVerificationReceived: Sending token exchange request (body redacted)";
Patch
Fixes available: defects/digikam/patch/digikam-0001-0004-*.patch
Four patches across haariface.cpp, gpsmarkertiler.cpp, metaengine_xmp.cpp, dmetadata_xmp.cpp, and o2.cpp.
digikam-0001: 250× speedup at 50,000 images. digikam-0002: 250× at 2000 geotagged images per tile. digikam-0003: 100× at 200 keywords per image. digikam-0004: credential leak eliminated.
What We Ask
Patches are ready for review.
- Confirm receipt and assign an issue reference (KDE/digikam on invent.kde.org).
- Assess severity — digikam-0001 fires on every similarity search; digikam-0004 leaks OAuth2 secrets in debug logs.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the digiKam team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.