Skip to content

Commit 5253940

Browse files
committed
fix: whitelist OpenEMR symbols in composer-require-checker
These symbols come from the host OpenEMR application at runtime. openemr/openemr is a dev-only dependency (not declared in require) so the checker must allow them explicitly. Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8170c99 commit 5253940

7 files changed

Lines changed: 29 additions & 12 deletions

.composer-require-checker.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414
"callable",
1515
"iterable",
1616
"void",
17-
"object"
17+
"object",
18+
"attr",
19+
"xlt",
20+
"OpenEMR\\Common\\Crypto\\CryptoGen",
21+
"OpenEMR\\Common\\Database\\QueryUtils",
22+
"OpenEMR\\Events\\Globals\\GlobalsInitializedEvent",
23+
"OpenEMR\\Services\\Globals\\GlobalSetting"
1824
],
1925
"php-core-extensions": [
2026
"Core",

src/ConfigService.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ class ConfigService
1818
private const UPSERT_SQL = <<<'SQL'
1919
INSERT INTO `globals` (`gl_name`, `gl_index`, `gl_value`)
2020
VALUES (?, 0, ?)
21-
ON DUPLICATE KEY UPDATE `gl_value` = VALUES(`gl_value`)
21+
ON DUPLICATE KEY UPDATE `gl_value` = ?
2222
SQL;
2323

2424
/**
2525
* Save a setting to the globals table (plaintext).
2626
*/
2727
public function saveSetting(string $key, string $value): void
2828
{
29-
QueryUtils::sqlStatementThrowException(self::UPSERT_SQL, [$key, $value]);
29+
QueryUtils::sqlStatementThrowException(self::UPSERT_SQL, [$key, $value, $value]);
3030
}
3131

3232
/**

src/GlobalsRegistrar.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ static function (GlobalsInitializedEvent $event) use ($descriptor, $globalsBag):
4242

4343
// Enable/disable toggle
4444
$enableSetting = new GlobalSetting(
45-
xlt('Enable ' . $descriptor->sectionName),
45+
sprintf(xlt('Enable %s'), $descriptor->sectionName),
4646
GlobalSetting::DATA_TYPE_BOOL,
4747
'0',
48-
xlt('Enable or disable the ' . $descriptor->sectionName . ' module'),
48+
sprintf(xlt('Enable or disable the %s module'), $descriptor->sectionName),
4949
);
5050
$service->appendToSection(
5151
$descriptor->sectionName,

tests/Mocks/openemr_functions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ function xlt(string $text): string
1515
if (!function_exists('attr')) {
1616
function attr(string $text): string
1717
{
18-
return htmlspecialchars($text, ENT_QUOTES);
18+
return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1919
}
2020
}

tests/Unit/ConfigServiceTest.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function testSaveSettingExecutesUpsert(): void
3131
$this->assertCount(1, $queries);
3232
$this->assertStringContainsString('INSERT INTO `globals`', $queries[0]['sql']);
3333
$this->assertStringContainsString('ON DUPLICATE KEY UPDATE', $queries[0]['sql']);
34-
$this->assertSame(['oce_test_key', 'test_value'], $queries[0]['binds']);
34+
$this->assertSame(['oce_test_key', 'test_value', 'test_value'], $queries[0]['binds']);
3535
}
3636

3737
public function testSaveEncryptedSettingEncryptsBeforeSaving(): void
@@ -41,8 +41,10 @@ public function testSaveEncryptedSettingEncryptsBeforeSaving(): void
4141
$queries = QueryUtils::getQueries();
4242
$this->assertCount(1, $queries);
4343
$this->assertSame('oce_secret_key', $queries[0]['binds'][0]);
44-
// MockCryptoGen uses base64
45-
$this->assertSame(base64_encode('secret_value'), $queries[0]['binds'][1]);
44+
// MockCryptoGen uses base64; value is bound twice for INSERT and UPDATE
45+
$encrypted = base64_encode('secret_value');
46+
$this->assertSame($encrypted, $queries[0]['binds'][1]);
47+
$this->assertSame($encrypted, $queries[0]['binds'][2]);
4648
}
4749

4850
public function testSaveSettingPropagatesExceptions(): void
@@ -62,7 +64,7 @@ public function testMultipleSaveSettingCallsAreRecorded(): void
6264

6365
$queries = QueryUtils::getQueries();
6466
$this->assertCount(2, $queries);
65-
$this->assertSame(['key_a', 'val_a'], $queries[0]['binds']);
66-
$this->assertSame(['key_b', 'val_b'], $queries[1]['binds']);
67+
$this->assertSame(['key_a', 'val_a', 'val_a'], $queries[0]['binds']);
68+
$this->assertSame(['key_b', 'val_b', 'val_b'], $queries[1]['binds']);
6769
}
6870
}

tests/Unit/GlobalsRegistrarTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ public function testRegisterAddsListener(): void
3737
$this->assertTrue($dispatcher->hasListeners(GlobalsInitializedEvent::EVENT_HANDLE));
3838
}
3939

40+
protected function tearDown(): void
41+
{
42+
// Clean up global state written by GlobalsService::save()
43+
// phpcs:ignore Squiz.PHP.GlobalKeyword.NotAllowed
44+
global $GLOBALS_METADATA;
45+
$GLOBALS_METADATA = null;
46+
}
47+
4048
/**
4149
* Dispatch the event and return the captured globals metadata.
4250
*
@@ -80,6 +88,7 @@ public function testCreatesSectionWithSettingsLink(): void
8088
{
8189
$metadata = $this->dispatchAndCapture();
8290

91+
$this->assertArrayHasKey('OpenCoreEMR Test Module', $metadata);
8392
$section = $metadata['OpenCoreEMR Test Module'];
8493
$this->assertArrayHasKey('oce_test_module_enabled_settings_link', $section);
8594

tests/bootstrap.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// Suppress error_log output during tests
99
ini_set('error_log', '/dev/null');
1010

11-
// Load mock classes before anything else to prevent "class not found" errors
11+
// Load mock classes to shadow real OpenEMR classes (must come after autoloader)
1212
require_once __DIR__ . '/Mocks/MockQueryUtils.php';
1313
require_once __DIR__ . '/Mocks/MockCryptoGen.php';
1414
require_once __DIR__ . '/Mocks/openemr_functions.php';

0 commit comments

Comments
 (0)