diff --git a/src/lib/ldap/client.ts b/src/lib/ldap/client.ts index cdeb6f1..bf08041 100644 --- a/src/lib/ldap/client.ts +++ b/src/lib/ldap/client.ts @@ -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 = {}; - 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 = {}; + 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)); + }, + ); }); }