All checks were successful
ci/woodpecker/push/push Pipeline was successful
## Added - CoT (Cursor on Target) server on port 8089 enabling ATAK/iTAK device connectivity - Support for TAK stream protocol and traditional XML CoT messages - TLS/SSL support with automatic fallback to plain TCP - Username/password authentication for CoT connections - Real-time device position tracking with TTL-based expiration (90s default) - API endpoints: `/api/cot/config`, `/api/cot/server-package`, `/api/cot/truststore`, `/api/me/cot-password` - TAK Server section in Settings with QR code for iTAK setup - ATAK password management in Account page for OIDC users - CoT device markers on map showing real-time positions - Comprehensive documentation in `docs/` directory - Environment variables: `COT_PORT`, `COT_TTL_MS`, `COT_REQUIRE_AUTH`, `COT_SSL_CERT`, `COT_SSL_KEY`, `COT_DEBUG` - Dependencies: `fast-xml-parser`, `jszip`, `qrcode` ## Changed - Authentication system supports CoT password management for OIDC users - Database schema includes `cot_password_hash` field - Test suite refactored to follow functional design principles ## Removed - Consolidated utility modules: `authConfig.js`, `authSkipPaths.js`, `bootstrap.js`, `poiConstants.js`, `session.js` ## Security - XML entity expansion protection in CoT parser - Enhanced input validation and SQL injection prevention - Authentication timeout to prevent hanging connections ## Breaking Changes - Port 8089 must be exposed for CoT server. Update firewall rules and Docker/Kubernetes configurations. ## Migration Notes - OIDC users must set ATAK password via Account settings before connecting - Docker: expose port 8089 (`-p 8089:8089`) - Kubernetes: update Helm values to expose port 8089 Co-authored-by: Madison Grubb <madison@elastiflow.com> Reviewed-on: #6
60 lines
2.1 KiB
JavaScript
60 lines
2.1 KiB
JavaScript
/**
|
|
* Encode a number as varint bytes (little-endian, continuation bit).
|
|
* @param {number} value - Value to encode
|
|
* @param {number[]} bytes - Accumulated bytes (default empty)
|
|
* @returns {number[]} Varint bytes
|
|
*/
|
|
const encodeVarint = (value, bytes = []) => {
|
|
const byte = value & 0x7F
|
|
const remaining = value >>> 7
|
|
if (remaining === 0) {
|
|
return [...bytes, byte]
|
|
}
|
|
return encodeVarint(remaining, [...bytes, byte | 0x80])
|
|
}
|
|
|
|
/**
|
|
* Build a TAK Protocol stream frame: 0xBF, varint payload length, payload.
|
|
* @param {string|Buffer} payload - UTF-8 payload (e.g. CoT XML)
|
|
* @returns {Buffer} TAK stream frame buffer.
|
|
*/
|
|
export function buildTakFrame(payload) {
|
|
const buf = Buffer.isBuffer(payload) ? payload : Buffer.from(payload, 'utf8')
|
|
const varint = encodeVarint(buf.length)
|
|
return Buffer.concat([Buffer.from([0xBF]), Buffer.from(varint), buf])
|
|
}
|
|
|
|
/**
|
|
* Build CoT XML for a position update (event + point + optional contact).
|
|
* @param {object} opts - Position options
|
|
* @param {string} opts.uid - Entity UID
|
|
* @param {number} opts.lat - Latitude
|
|
* @param {number} opts.lon - Longitude
|
|
* @param {string} [opts.callsign] - Optional callsign
|
|
* @param {string} [opts.type] - Optional event type (default a-f-G)
|
|
* @returns {string} CoT XML string.
|
|
*/
|
|
export function buildPositionCotXml({ uid, lat, lon, callsign, type = 'a-f-G' }) {
|
|
const contact = callsign ? `<detail><contact callsign="${escapeXml(callsign)}"/></detail>` : ''
|
|
return `<event uid="${escapeXml(uid)}" type="${escapeXml(type)}"><point lat="${lat}" lon="${lon}"/>${contact}</event>`
|
|
}
|
|
|
|
/**
|
|
* Build CoT XML for auth (username/password).
|
|
* @param {object} opts - Auth options
|
|
* @param {string} opts.username - Username
|
|
* @param {string} opts.password - Password
|
|
* @returns {string} CoT XML string.
|
|
*/
|
|
export function buildAuthCotXml({ username, password }) {
|
|
return `<event><detail><auth username="${escapeXml(username)}" password="${escapeXml(password)}"/></detail></event>`
|
|
}
|
|
|
|
function escapeXml(s) {
|
|
return String(s)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
}
|