Skip to content

Commit 24b1328

Browse files
authored
Merge branch 'main' into fix-phpcs-2
2 parents b2786b2 + 9c01ce5 commit 24b1328

5 files changed

Lines changed: 200 additions & 698 deletions

File tree

.github/workflows/magento-compatibility.yml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ on:
88
workflow_dispatch:
99

1010
jobs:
11-
test-elasticsearch:
12-
name: Magento ${{ matrix.magento-version }} with PHP ${{ matrix.php-version }} Test
11+
test-opensearch-247:
12+
name: Magento ${{ matrix.magento-version }} with PHP ${{ matrix.php-version }} (OpenSearch) Test
1313
runs-on: ubuntu-latest
1414
strategy:
1515
fail-fast: false
1616
matrix:
1717
include:
1818
- magento-version: "2.4.7"
1919
php-version: "8.3"
20-
search-engine-name: "elasticsearch7"
20+
search-engine-name: "opensearch"
2121
- magento-version: "2.4.7-p8"
2222
php-version: "8.3"
23-
search-engine-name: "elasticsearch7"
23+
search-engine-name: "opensearch"
2424

2525
services:
2626
mysql:
@@ -32,13 +32,14 @@ jobs:
3232
- 3306:3306
3333
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
3434

35-
elasticsearch:
36-
image: elasticsearch:7.17.0
35+
opensearch:
36+
image: opensearchproject/opensearch:2.11.0
3737
ports:
3838
- 9200:9200
3939
env:
4040
discovery.type: single-node
41-
ES_JAVA_OPTS: -Xms512m -Xmx512m
41+
plugins.security.disabled: true
42+
OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m
4243
options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10
4344

4445
steps:
@@ -95,9 +96,9 @@ jobs:
9596
--use-rewrites=1 \
9697
--backend-frontname=admin \
9798
--search-engine=${{ matrix.search-engine-name }} \
98-
--elasticsearch-host=localhost \
99-
--elasticsearch-port=9200 \
100-
--elasticsearch-index-prefix=magento \
99+
--opensearch-host=localhost \
100+
--opensearch-port=9200 \
101+
--opensearch-index-prefix=magento \
101102
--cleanup-database
102103
103104
- name: Install MageForge Module from current commit

src/Console/Command/AbstractCommand.php

Lines changed: 165 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ abstract class AbstractCommand extends Command
3939
*/
4040
private array $secureEnvStorage = [];
4141

42+
/**
43+
* @var array<string, string|null>|null
44+
*/
45+
private ?array $cachedEnv = null;
46+
4247
/**
4348
* Get the command name with proper group structure
4449
*
@@ -191,7 +196,7 @@ protected function handleInvalidThemeWithSuggestions(
191196
* @param OutputInterface $output
192197
* @return bool
193198
*/
194-
private function isInteractiveTerminal(OutputInterface $output): bool
199+
protected function isInteractiveTerminal(OutputInterface $output): bool
195200
{
196201
// Check if output supports ANSI
197202
if (!$output->isDecorated()) {
@@ -229,7 +234,7 @@ private function isInteractiveTerminal(OutputInterface $output): bool
229234
*
230235
* @return void
231236
*/
232-
private function setPromptEnvironment(): void
237+
protected function setPromptEnvironment(): void
233238
{
234239
// Store original values for restoration
235240
$this->originalEnv = [
@@ -249,7 +254,7 @@ private function setPromptEnvironment(): void
249254
*
250255
* @return void
251256
*/
252-
private function resetPromptEnvironment(): void
257+
protected function resetPromptEnvironment(): void
253258
{
254259
foreach ($this->originalEnv as $key => $value) {
255260
if ($value === null) {
@@ -261,49 +266,180 @@ private function resetPromptEnvironment(): void
261266
}
262267

263268
/**
264-
* Get environment variable value
265-
*
266-
* @param string $key
267-
* @return string|null
269+
* Safely get environment variable with sanitization
268270
*/
269-
private function getEnvVar(string $key): ?string
271+
private function getEnvVar(string $name): ?string
270272
{
271-
return getenv($key) ?: null;
273+
$value = $this->getSecureEnvironmentValue($name);
274+
275+
if ($value === null || $value === '') {
276+
return null;
277+
}
278+
279+
return $this->sanitizeEnvironmentValue($name, $value);
272280
}
273281

274282
/**
275-
* Get server variable value
276-
*
277-
* @param string $key
278-
* @return string|null
283+
* Securely retrieve environment variable without direct superglobal access
279284
*/
280-
private function getServerVar(string $key): ?string
285+
private function getSecureEnvironmentValue(string $name): ?string
281286
{
282-
return $_SERVER[$key] ?? null;
287+
if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) {
288+
return null;
289+
}
290+
291+
$envVars = $this->getCachedEnvironmentVariables();
292+
return $envVars[$name] ?? null;
283293
}
284294

285295
/**
286-
* Set environment variable securely
296+
* Cache and filter environment variables safely
287297
*
288-
* @param string $key
289-
* @param string $value
290-
* @return void
298+
* @return array<string, string|null>
291299
*/
292-
private function setEnvVar(string $key, string $value): void
300+
private function getCachedEnvironmentVariables(): array
293301
{
294-
$this->secureEnvStorage[$key] = $value;
295-
putenv("$key=$value");
302+
if ($this->cachedEnv === null) {
303+
$this->cachedEnv = [];
304+
$allowedVars = [
305+
'COLUMNS',
306+
'LINES',
307+
'TERM',
308+
'CI',
309+
'GITHUB_ACTIONS',
310+
'GITLAB_CI',
311+
'JENKINS_URL',
312+
'TEAMCITY_VERSION',
313+
];
314+
315+
foreach ($allowedVars as $var) {
316+
if (isset($this->secureEnvStorage[$var])) {
317+
$this->cachedEnv[$var] = $this->secureEnvStorage[$var];
318+
} else {
319+
$globalEnv = filter_input_array(INPUT_ENV) ?: [];
320+
if (array_key_exists($var, $globalEnv)) {
321+
$this->cachedEnv[$var] = (string) $globalEnv[$var];
322+
}
323+
}
324+
}
325+
}
326+
327+
return $this->cachedEnv;
296328
}
297329

298330
/**
299-
* Remove environment variable securely
300-
*
301-
* @param string $key
302-
* @return void
331+
* Sanitize environment value based on variable type
332+
*/
333+
private function sanitizeEnvironmentValue(string $name, string $value): ?string
334+
{
335+
return match ($name) {
336+
'COLUMNS', 'LINES' => $this->sanitizeNumericValue($value),
337+
'TERM' => $this->sanitizeTermValue($value),
338+
'CI', 'GITHUB_ACTIONS', 'GITLAB_CI' => $this->sanitizeBooleanValue($value),
339+
'JENKINS_URL', 'TEAMCITY_VERSION' => $this->sanitizeAlphanumericValue($value),
340+
default => $this->sanitizeAlphanumericValue($value),
341+
};
342+
}
343+
344+
/**
345+
* Sanitize numeric values (COLUMNS, LINES)
346+
*/
347+
private function sanitizeNumericValue(string $value): ?string
348+
{
349+
$filtered = filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 9999]]);
350+
return $filtered !== false ? (string) $filtered : null;
351+
}
352+
353+
/**
354+
* Sanitize terminal type values
355+
*/
356+
private function sanitizeTermValue(string $value): ?string
357+
{
358+
$sanitized = preg_replace('/[^a-zA-Z0-9\-]/', '', $value);
359+
if ($sanitized === null) {
360+
return null;
361+
}
362+
return (strlen($sanitized) > 0 && strlen($sanitized) <= 50) ? $sanitized : null;
363+
}
364+
365+
/**
366+
* Sanitize boolean-like values
367+
*/
368+
private function sanitizeBooleanValue(string $value): ?string
369+
{
370+
$cleaned = strtolower(trim($value));
371+
return in_array($cleaned, ['1', 'true', 'yes', 'on'], true) ? $cleaned : null;
372+
}
373+
374+
/**
375+
* Sanitize alphanumeric values
376+
*/
377+
private function sanitizeAlphanumericValue(string $value): ?string
378+
{
379+
$sanitized = preg_replace('/[^\w\-.]/', '', $value);
380+
if ($sanitized === null) {
381+
return null;
382+
}
383+
return (strlen($sanitized) > 0 && strlen($sanitized) <= 255) ? $sanitized : null;
384+
}
385+
386+
/**
387+
* Safely get server variable with sanitization
388+
*/
389+
private function getServerVar(string $name): ?string
390+
{
391+
if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) {
392+
return null;
393+
}
394+
395+
$value = filter_input(INPUT_SERVER, $name);
396+
397+
if ($value === null || $value === false || $value === '') {
398+
return null;
399+
}
400+
401+
return $this->sanitizeAlphanumericValue((string) $value);
402+
}
403+
404+
/**
405+
* Safely set environment variable with validation
406+
*/
407+
private function setEnvVar(string $name, string $value): void
408+
{
409+
if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) {
410+
return;
411+
}
412+
413+
$sanitizedValue = $this->sanitizeEnvironmentValue($name, $value);
414+
415+
if ($sanitizedValue !== null) {
416+
$this->setSecureEnvironmentValue($name, $sanitizedValue);
417+
}
418+
}
419+
420+
/**
421+
* Securely store environment variable without direct superglobal access
422+
*/
423+
private function setSecureEnvironmentValue(string $name, string $value): void
424+
{
425+
$this->secureEnvStorage[$name] = $value;
426+
}
427+
428+
/**
429+
* Clear the environment variable cache
430+
*/
431+
private function clearEnvironmentCache(): void
432+
{
433+
$this->secureEnvStorage = [];
434+
$this->cachedEnv = null;
435+
}
436+
437+
/**
438+
* Securely remove environment variable from cache
303439
*/
304-
private function removeSecureEnvironmentValue(string $key): void
440+
private function removeSecureEnvironmentValue(string $name): void
305441
{
306-
unset($this->secureEnvStorage[$key]);
307-
putenv($key);
442+
unset($this->secureEnvStorage[$name]);
443+
$this->clearEnvironmentCache();
308444
}
309445
}

0 commit comments

Comments
 (0)