diff --git a/CHANGELOG.md b/CHANGELOG.md
index 882b794..52a7271 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
+- Encrypt stored auth credentials, parameterize device queries, escape XML/error output
- Sanitize invalid characters from SCCM
## [2.6.0] - 2026-05-26
diff --git a/inc/config.class.php b/inc/config.class.php
index 4439ba7..f98b7e2 100644
--- a/inc/config.class.php
+++ b/inc/config.class.php
@@ -148,6 +148,12 @@ public function prepareInputForUpdate($input): array
unset($input["sccmdb_password"]);
}
+ if (isset($input["auth_info"]) && !empty($input["auth_info"])) {
+ $input["auth_info"] = (new GLPIKey())->encrypt($input["auth_info"]);
+ } else {
+ unset($input["auth_info"]);
+ }
+
if (array_key_exists('inventory_server_url', $input) && !empty($input['inventory_server_url'])) {
$input['inventory_server_url'] = trim((string) $input['inventory_server_url'], '/ ');
}
@@ -163,15 +169,17 @@ public function showForm($ID, array $options = []): bool
$this->initForm($ID, $options);
$password = (new GLPIKey())->decrypt($this->fields['sccmdb_password'] ?? '');
+ $auth_info = (new GLPIKey())->decrypt($this->fields['auth_info'] ?? '');
$url = ($this->fields['inventory_server_url'] ?: __('Example:') . ' ' . $CFG_GLPI['url_base']) . '/front/inventory.php';
TemplateRenderer::getInstance()->display(
'@sccm/config.html.twig',
[
- 'item' => $this,
- 'params' => $options,
- 'password_display' => $password,
- 'url' => $url,
+ 'item' => $this,
+ 'params' => $options,
+ 'password_display' => $password,
+ 'auth_info_display' => $auth_info,
+ 'url' => $url,
],
);
@@ -216,7 +224,7 @@ public static function install(Migration $migration): bool
`sccm_collection_name` VARCHAR(255) NULL,
`inventory_server_url` VARCHAR(255) NULL,
`active_sync` tinyint NOT NULL DEFAULT '0',
- `verify_ssl_cert` tinyint NOT NULL DEFAULT '0',
+ `verify_ssl_cert` tinyint NOT NULL DEFAULT '1',
`use_auth_ntlm` tinyint NOT NULL DEFAULT '0',
`unrestricted_auth` tinyint NOT NULL DEFAULT '0',
`use_auth_info` tinyint NOT NULL DEFAULT '0',
@@ -255,7 +263,7 @@ public static function install(Migration $migration): bool
}
if (!$DB->fieldExists($table, 'verify_ssl_cert')) {
- $migration->addField($table, "verify_ssl_cert", "tinyint NOT NULL default '0'");
+ $migration->addField($table, "verify_ssl_cert", "tinyint NOT NULL default '1'");
$migration->migrationOneTable($table);
}
@@ -298,6 +306,25 @@ public static function install(Migration $migration): bool
$migration->migrationOneTable($table);
}
+ // Encrypt auth_info — iterate ALL configs
+ if (!$DB->fieldExists($table, 'is_auth_info_encrypted')) {
+ $key = new GLPIKey();
+ foreach ($DB->request(['FROM' => $table]) as $config_data) {
+ if (!empty($config_data['auth_info'])) {
+ $migration->addPostQuery(
+ $DB->buildUpdate(
+ $table,
+ ['auth_info' => $key->encrypt($config_data['auth_info'])],
+ ['id' => $config_data['id']],
+ ),
+ );
+ }
+ }
+
+ $migration->addField($table, "is_auth_info_encrypted", "tinyint NOT NULL default '1'");
+ $migration->migrationOneTable($table);
+ }
+
if (!$DB->fieldExists($table, 'sccm_collection_name')) {
$migration->addField($table, 'sccm_collection_name', 'string', ['after' => 'sccmdb_password', 'value' => '']);
$migration->migrationOneTable($table);
diff --git a/inc/sccm.class.php b/inc/sccm.class.php
index 83611c6..3a157d8 100644
--- a/inc/sccm.class.php
+++ b/inc/sccm.class.php
@@ -84,11 +84,13 @@ public function getDevices(int $config_id, $where = 0, $limit = 99999999): void
$query = self::getcomputerQuery($collection_name);
+ $params = [];
if ($where != 0) {
- $query .= " WHERE csd.MachineID = '" . $where . "'";
+ $query .= " WHERE csd.MachineID = ?";
+ $params[] = $where;
}
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, $params);
$i = 0;
$this->devices = [];
@@ -114,9 +116,9 @@ public function getDatas(PluginSccmSccmdb $sccm_db, $type, $deviceid, $limit = 9
$query = "SELECT " . implode(',', $fields) . "\n";
$query .= " FROM " . $table . "\n";
- $query .= " WHERE MachineID = '" . $deviceid . "'" . "\n";
+ $query .= " WHERE MachineID = ?" . "\n";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid]);
$data = [];
$i = 0;
@@ -141,9 +143,9 @@ public function getNetwork(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 999999
INNER JOIN v_R_System VrS ON VrS.ResourceID=NeDa.MachineID
INNER JOIN v_GS_NETWORK_ADAPTER net ON net.ResourceID=NeDa.MachineID AND NeDa.ServiceName00=net.ServiceName0
WHERE MACAddress00 is not null
- AND NeDa.MachineID = '" . $deviceid . "'";
+ AND NeDa.MachineID = ?";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid]);
$data = [];
$i = 0;
@@ -163,7 +165,7 @@ public function getSoftware(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
ArPd_64.Publisher0 as \"ArPd-Publisher\"
FROM v_GS_ADD_REMOVE_PROGRAMS_64 ArPd_64
INNER JOIN v_R_System VrS on VrS.ResourceID=ArPd_64.ResourceID
- WHERE ArPd_64.ResourceID = {$deviceid}
+ WHERE ArPd_64.ResourceID = ?
AND (ArPd_64.DisplayName0 is not null and ArPd_64.DisplayName0 <> '')
UNION
SELECT ArPd.DisplayName0 as \"ArPd-DisplayName\",
@@ -172,10 +174,10 @@ public function getSoftware(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
ArPd.Publisher0 as \"ArPd-Publisher\"
FROM v_GS_ADD_REMOVE_PROGRAMS ArPd
INNER JOIN v_R_System VrS on VrS.ResourceID=ArPd.ResourceID
- WHERE ArPd.ResourceID = {$deviceid}
+ WHERE ArPd.ResourceID = ?
AND (ArPd.DisplayName0 is not null and ArPd.DisplayName0 <> '')";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid, $deviceid]);
$data = [];
$i = 0;
@@ -202,10 +204,10 @@ public function getMemories(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
GroupID as \"Mem-NumSlots\",
'' as \"Mem-SerialNumber\"
FROM v_GS_PHYSICAL_MEMORY
- WHERE ResourceID = '" . $deviceid . "'
+ WHERE ResourceID = ?
ORDER BY \"Mem-NumSlots\"";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid]);
$data = [];
$i = 0;
@@ -228,10 +230,10 @@ public function getVideos(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 9999999
GroupID as \"Vid-PciSlot\"
FROM v_GS_VIDEO_CONTROLLER
WHERE VideoProcessor0 is not null
- AND ResourceID = '" . $deviceid . "'
+ AND ResourceID = ?
ORDER BY GroupID";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid]);
$data = [];
$i = 0;
@@ -251,9 +253,9 @@ public function getSounds(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 9999999
Manufacturer0 as \"Snd-Manufacturer\",
Name0 as \"Snd-Name\"
FROM v_GS_SOUND_DEVICE
- WHERE ResourceID = '" . $deviceid . "'";
+ WHERE ResourceID = ?";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid]);
$data = [];
$i = 0;
@@ -282,9 +284,9 @@ public function getStorages(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 99999
INNER JOIN v_gs_Disk as gdi on gdi.ResourceID = gld.ResourceID
LEFT JOIN Motherboard_DATA as md on gld.ResourceID = md.MachineID
WHERE gld.GroupID = gdi.GroupID
- AND gld.ResourceID = '" . $deviceid . "'";
+ AND gld.ResourceID = ?";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid]);
$data = [];
$i = 0;
@@ -307,9 +309,9 @@ public function getMedias(PluginSccmSccmdb $sccm_db, $deviceid, $limit = 9999999
SCSITargetID0 as \"Med-SCSITargetId\",
MediaType0 as \"Med-Type\"
FROM v_GS_CDROM
- WHERE ResourceID = '" . $deviceid . "'";
+ WHERE ResourceID = ?";
- $result = $sccm_db->exec_query($query);
+ $result = $sccm_db->exec_query($query, [$deviceid]);
$data = [];
$i = 0;
@@ -433,6 +435,11 @@ public static function executeCollect($task): int
}
foreach ($PluginSccmSccm->devices as $device_values) {
+ if (!ctype_digit((string) ($device_values['CSD-MachineID'] ?? ''))) {
+ Toolbox::logInFile('sccm', sprintf('[Config %s] Invalid device id, skipping: ', $config_id) . ($device_values['CSD-MachineID'] ?? '') . "\n", true);
+ continue;
+ }
+
$PluginSccmSccmxml = new PluginSccmSccmxml($device_values);
$PluginSccmSccmxml->setAccessLog();
@@ -569,6 +576,11 @@ public static function executePush(CronTask $task): int
$result = $PluginSccmSccmdb->exec_query($query);
while ($tab = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC)) {
+ if (!ctype_digit((string) ($tab['CSD-MachineID'] ?? ''))) {
+ Toolbox::logInFile('sccm', sprintf('[Config %s] Invalid device id, skipping: ', $config_id) . ($tab['CSD-MachineID'] ?? '') . "\n", true);
+ continue;
+ }
+
$REP_XML = realpath(GLPI_PLUGIN_DOC_DIR . '/sccm/xml/' . $config_id . '/' . $tab['CSD-MachineID'] . '.ocs');
if ($REP_XML === '0') {
@@ -599,7 +611,7 @@ public static function executePush(CronTask $task): int
}
if ($config->getField('use_auth_info') == "1") {
- curl_setopt($ch, CURLOPT_USERPWD, $config->getField('auth_info'));
+ curl_setopt($ch, CURLOPT_USERPWD, (new GLPIKey())->decrypt($config->getField('auth_info') ?? ''));
}
$url = ($config->getField('inventory_server_url') ?: $CFG_GLPI['url_base']) . '/front/inventory.php';
diff --git a/inc/sccmdb.class.php b/inc/sccmdb.class.php
index f6b4dec..f57cded 100644
--- a/inc/sccmdb.class.php
+++ b/inc/sccmdb.class.php
@@ -73,9 +73,9 @@ public function disconnect(): void
sqlsrv_close($this->dbconn);
}
- public function exec_query($query)
+ public function exec_query($query, array $params = [])
{
- $result = sqlsrv_query($this->dbconn, $query);
+ $result = sqlsrv_query($this->dbconn, $query, $params);
if ($result == false) {
throw new BadRequestHttpException('Query error: ' . print_r(sqlsrv_errors(), true));
}
@@ -89,7 +89,7 @@ public function FormatErrors(array $errors): void
$state = "SQLSTATE: " . $error['SQLSTATE'];
$code = "Code: " . $error['code'];
$message = "Message: " . $error['message'];
- echo $state . "" . $code . "
" . $message . "
";
+ echo htmlspecialchars($state) . "" . htmlspecialchars($code) . "
" . htmlspecialchars($message) . "
";
Toolbox::logInFile("sccm", $state . PHP_EOL . $code . PHP_EOL . $message . PHP_EOL);
}
}
diff --git a/inc/sccmxml.class.php b/inc/sccmxml.class.php
index 9e82abc..7a03547 100644
--- a/inc/sccmxml.class.php
+++ b/inc/sccmxml.class.php
@@ -56,12 +56,12 @@ public function __construct(public array $data)
{$this->agentbuildnumber}
- {$this->device_id}
INVENTORY
XML;
$this->sxml = new SimpleXMLElement($SXML);
+ $this->sxml->addChild('DEVICEID', $this->device_id);
}
public function setAccessLog(): void
diff --git a/rector.php b/rector.php
index a58505d..9ddbe93 100644
--- a/rector.php
+++ b/rector.php
@@ -29,29 +29,27 @@
* -------------------------------------------------------------------------
*/
+use Rector\Configuration\RectorConfigBuilder;
+
require_once __DIR__ . '/../../src/Plugin.php';
-use Rector\Caching\ValueObject\Storage\FileCacheStorage;
-use Rector\Config\RectorConfig;
-use Rector\ValueObject\PhpVersion;
+$baseline_file = __DIR__ . '/../../PluginsRector.php';
+if (!file_exists($baseline_file)) {
+ throw new RuntimeException(
+ sprintf(
+ 'Unable to find "%s". Running rector on a plugin requires a GLPI development checkout that ships PluginsRector.php.',
+ $baseline_file,
+ ),
+ );
+}
+
+$baseline = require $baseline_file;
+
+/** @var RectorConfigBuilder $config */
+$config = $baseline([
+ __DIR__ . '/front',
+ __DIR__ . '/inc',
+ __DIR__ . '/tests',
+]);
-return RectorConfig::configure()
- ->withPaths([
- __DIR__ . '/front',
- __DIR__ . '/inc',
- ])
- ->withPhpVersion(PhpVersion::PHP_82)
- ->withCache(
- cacheDirectory: __DIR__ . '/var/rector',
- cacheClass: FileCacheStorage::class,
- )
- ->withRootFiles()
- ->withParallel(timeoutSeconds: 300)
- ->withImportNames(removeUnusedImports: true)
- ->withPreparedSets(
- deadCode: true,
- codeQuality: true,
- codingStyle: true,
- )
- ->withPhpSets(php82: true) // apply PHP sets up to PHP 8.2
-;
+return $config;
diff --git a/setup.php b/setup.php
index b517345..a0b2b05 100644
--- a/setup.php
+++ b/setup.php
@@ -1,5 +1,36 @@
.
+ * -------------------------------------------------------------------------
+ * @author François Legastelois
+ * @copyright Copyright (C) 2014-2023 by SCCM plugin team.
+ * @license GPLv3 https://www.gnu.org/licenses/gpl-3.0.html
+ * @link https://github.com/pluginsGLPI/sccm
+ * -------------------------------------------------------------------------
+ */
+
+declare(strict_types=1);
+
/**
* -------------------------------------------------------------------------
* SCCM plugin for GLPI
@@ -51,7 +82,10 @@ function plugin_init_sccm()
}
// Encryption
- $PLUGIN_HOOKS[Hooks::SECURED_FIELDS]['sccm'] = ['glpi_plugin_sccm_configs.sccmdb_password'];
+ $PLUGIN_HOOKS[Hooks::SECURED_FIELDS]['sccm'] = [
+ 'glpi_plugin_sccm_configs.sccmdb_password',
+ 'glpi_plugin_sccm_configs.auth_info',
+ ];
}
/**
diff --git a/templates/config.html.twig b/templates/config.html.twig
index b415b0b..fbdb5a6 100644
--- a/templates/config.html.twig
+++ b/templates/config.html.twig
@@ -107,10 +107,11 @@
__('Use specific authentication information', 'sccm')
) }}
- {{ fields.textField(
+ {{ fields.passwordField(
'auth_info',
- item.fields['auth_info'],
- __('Value for specific authentication', 'sccm')
+ auth_info_display,
+ __('Value for specific authentication', 'sccm'),
+ {'autocomplete': 'off'}
) }}
{{ fields.checkboxField(
diff --git a/tests/PluginSccmSccmTest.php b/tests/PluginSccmSccmTest.php
index 2c10746..c985ce5 100644
--- a/tests/PluginSccmSccmTest.php
+++ b/tests/PluginSccmSccmTest.php
@@ -36,9 +36,9 @@ class PluginSccmSccmTest extends GLPITestCase
{
private function callSanitizeRow(array $row): array
{
- $sccm = (new \ReflectionClass(PluginSccmSccm::class))->newInstanceWithoutConstructor();
+ $sccm = (new ReflectionClass(PluginSccmSccm::class))->newInstanceWithoutConstructor();
- return (new \ReflectionMethod(PluginSccmSccm::class, 'sanitizeRow'))->invoke($sccm, $row);
+ return (new ReflectionMethod(PluginSccmSccm::class, 'sanitizeRow'))->invoke($sccm, $row);
}
public function testSanitizeRowPreservesCleanString(): void
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 3744113..355023a 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -35,5 +35,5 @@
require dirname(__DIR__) . '/vendor/autoload.php';
if (!Plugin::isPluginActive($current_plugin_folder)) {
- throw new RuntimeException("Plugin $current_plugin_folder is not active in the test database");
+ throw new RuntimeException(sprintf('Plugin %s is not active in the test database', $current_plugin_folder));
}