diff --git a/openvas/glpi-to-openvas.sh b/openvas/glpi-to-openvas.sh
index d19f553..081f6dd 100755
--- a/openvas/glpi-to-openvas.sh
+++ b/openvas/glpi-to-openvas.sh
@@ -56,34 +56,25 @@ else
fetch_inventory > "$INVENTORY_JSON" || exit 1
fi
-# Normalize: one record per line with parsed IPs
-NORMALIZED=$(jq -r '
- .[]
- | select( ($ONLY_DYNAMIC == 0 or .is_dynamic == 1) )
- | ( .comment // "" ) as $c
- | ( [$c | scan("ts=([0-9.]+)")] | first | first // "" ) as $ts
- | ( [$c | scan("mgmt[ =]([0-9.]+)")] | first | first // "" ) as $mgmt
- | select( $INCLUDE_NO_IP == 1 or $ts != "" or $mgmt != "" )
- | [ (.id|tostring), .name, $ts, $mgmt, (.serial // "-"),
- (.last_inventory_update // "-"), ((.is_dynamic // 0)|tostring) ]
- | @tsv' --argjson ONLY_DYNAMIC "$ONLY_DYNAMIC" --argjson INCLUDE_NO_IP "$INCLUDE_NO_IP" "$INVENTORY_JSON")
-
-[ -n "$NORMALIZED" ] || { echo "no scan targets parsed from GLPI" >&2; exit 2; }
-
case "$FORMAT" in
csv)
printf 'glpi_id,name,ts_ip,mgmt_ip,serial,last_inventory,agent_fed\n'
- printf '%s\n' "$NORMALIZED"
+ jq -r '
+ .[]
+ | select( ($ONLY_DYNAMIC == 0 or .is_dynamic == 1) )
+ | ( .comment // "" ) as $c
+ | ( [$c | scan("ts=([0-9.]+)")] | first | first // "" ) as $ts
+ | ( [$c | scan("mgmt[ =]([0-9.]+)")] | first | first // "" ) as $mgmt
+ | select( $INCLUDE_NO_IP == 1 or $ts != "" or $mgmt != "" )
+ | [ (.id|tostring), .name, $ts, $mgmt, (.serial // ""),
+ (.last_inventory_update // ""), ((.is_dynamic // 0)|tostring) ]
+ | @csv' --argjson ONLY_DYNAMIC "$ONLY_DYNAMIC" --argjson INCLUDE_NO_IP "$INCLUDE_NO_IP" "$INVENTORY_JSON"
;;
gmp)
printf '\n' "$(date -Is)"
- printf '%s\n' "$NORMALIZED" | while IFS="$(printf '\t')" read -r id name ts mgmt _serial inv dyn; do
- hosts="$ts"
- if [ -n "$mgmt" ]; then hosts="${hosts:+$hosts,}$mgmt"; fi
- [ -n "$hosts" ] || continue
- printf 'glpi-%s-%s%sGLPI #%s inv=%s agent=%sScan Config Default\n' \
- "$id" "$(printf '%s' "$name" | tr -c '[:alnum:].-' '-')" "$hosts" "$id" "$inv" "$dyn"
- done
+ # jq emits the complete element per record (host list built in jq — avoids
+ # bash read() IFS-collapse on empty TSV fields; record bound via . as $rec)
+ jq -r ' .[] | . as $rec | ( $rec.comment // "" ) as $c | ( [$c | scan("ts=([0-9.]+)")] | first | first // "" ) as $ts | ( [$c | scan("mgmt[ =]([0-9.]+)")] | first | first // "" ) as $mgmt | [ $ts, $mgmt ] | map(select(length > 0)) | join(",") as $hosts | select( $hosts != "" ) | $rec.name as $n | "glpi-\($rec.id)-\($n | gsub("[^a-zA-Z0-9.-]"; "-"))\($hosts)GLPI #\($rec.id) agent=\($rec.is_dynamic // 0)"' --argjson ONLY_DYNAMIC "$ONLY_DYNAMIC" --argjson INCLUDE_NO_IP "$INCLUDE_NO_IP" "$INVENTORY_JSON"
;;
*) echo "bad format: $FORMAT" >&2; exit 1 ;;
esac
diff --git a/openvas/import_targets.gmp b/openvas/import_targets.gmp
new file mode 100644
index 0000000..77192bb
--- /dev/null
+++ b/openvas/import_targets.gmp
@@ -0,0 +1,35 @@
+import re
+from lxml import etree
+data = open('/tmp/targets.gmp').read()
+cmds = re.findall(r'.*?', data, re.S)
+
+pl = etree.fromstring(gmp.send_command('').encode())
+plist_id = None
+for p in pl.findall('.//port_list'):
+ if 'All IANA assigned TCP' in (p.findtext('name') or ''):
+ plist_id = p.get('id')
+ break
+if not plist_id:
+ raise SystemExit('no IANA TCP port list found')
+
+# clean previous glpi-* targets (idempotent re-import)
+existing = etree.fromstring(gmp.send_command('').encode())
+for t in existing.findall('.//target'):
+ if (t.findtext('name') or '').startswith('glpi-'):
+ gmp.send_command(f'')
+
+ok = fail = 0
+for c in cmds:
+ frag = etree.fromstring(c.encode())
+ el = etree.SubElement(frag, 'port_list')
+ el.set('id', plist_id)
+ root = etree.fromstring(gmp.send_command(etree.tostring(frag).decode()).encode())
+ st = root.get('status')
+ if st == '201':
+ ok += 1
+ else:
+ fail += 1
+ if fail <= 3:
+ resp = root.findtext('.//response')
+ print('FAIL', st, (resp or etree.tostring(root).decode())[:180])
+print(f'imported={ok} failed={fail}')