Request only mail/cn/displayName from LDAP instead of every attribute

The real error surfaced by the previous commit's diagnostics: "Parser
error ... Encoding too long" from @ldapjs/asn1's BER decoder — a parser
desync (it misreads some later byte as a new field's length prefix),
most likely triggered while decoding some AD attribute value on one of
the ~1000 objects that we never actually use (e.g. nTSecurityDescriptor,
msDS-ReplAttributeMetaData, or similar oversized/binary attributes AD
attaches to many objects by default). Every call site only ever reads
mail/cn/displayName, so there was never a reason to request the full
attribute set — doing so avoids decoding whatever was tripping the bug,
regardless of exactly which attribute it was.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012o9j9RezxbZVKQMrB7oRLY
This commit is contained in:
ogrechkoandClaude Sonnet 5 committed 2026-08-05 11:18:57 +00:00
1 parent 2a505da975
commit 1e8133ee39
1 file changed
+26 -14
+26 -14
View File
@@ -69,21 +69,33 @@ function search(
// response handling, not the query itself. ldapjs runs the multi-page
// RFC 2696 round-trips internally; callers still just see one continuous
// stream of `searchEntry` events followed by a single `end`.
client.search(baseDn, { filter, scope: "sub", paged: { pageSize: 200 } }, (err, res) => {
if (err) {
reject(describeError(client, err));
return;
}
res.on("searchEntry", (entry) => {
const attributes: Record<string, string> = {};
for (const attr of entry.pojo.attributes) {
attributes[attr.type] = attr.values[0] ?? "";
//
// `attributes` is also restricted to just what callers actually read
// (mail/cn/displayName) instead of requesting every attribute AD has on
// an object. The real failure turned out to be @ldapjs/asn1's BER
// decoder throwing "Encoding too long" — a parser desync, most likely
// triggered while decoding some oversized/exotic AD attribute we never
// use anyway (e.g. nTSecurityDescriptor, msDS-ReplAttributeMetaData).
// Not requesting them sidesteps the bug entirely.
client.search(
baseDn,
{ filter, scope: "sub", paged: { pageSize: 200 }, attributes: ["mail", "cn", "displayName"] },
(err, res) => {
if (err) {
reject(describeError(client, err));
return;
}
results.push({ dn: entry.objectName ?? entry.pojo.objectName, attributes });
});
res.on("error", (searchErr) => reject(describeError(client, searchErr)));
res.on("end", () => resolve(results));
});
res.on("searchEntry", (entry) => {
const attributes: Record<string, string> = {};
for (const attr of entry.pojo.attributes) {
attributes[attr.type] = attr.values[0] ?? "";
}
results.push({ dn: entry.objectName ?? entry.pojo.objectName, attributes });
});
res.on("error", (searchErr) => reject(describeError(client, searchErr)));
res.on("end", () => resolve(results));
},
);
});
}