<?php

declare(strict_types=1);

namespace KiyoKart\Security\PHPStan;

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;

/**
 * @implements Rule<FuncCall>
 */
class ForbiddenFunctionRule implements Rule
{
    /** @var array<string, true> */
    private array $forbidden = [
        'exec' => true,
        'shell_exec' => true,
        'system' => true,
        'passthru' => true,
        'popen' => true,
        'proc_open' => true,
        'pcntl_exec' => true,
        'pcntl_alarm' => true,
        'pcntl_fork' => true,
        'pcntl_wait' => true,
        'pcntl_waitpid' => true,
        'pcntl_signal' => true,
        'pcntl_signal_dispatch' => true,
        'eval' => true,
        'assert' => true,
        'create_function' => true,
        'link' => true,
        'symlink' => true,
        'readlink' => true,
        'phpinfo' => true,
        'getenv' => true,
        'putenv' => true,
    ];

    public function getNodeType(): string
    {
        return FuncCall::class;
    }

    public function processNode(Node $node, Scope $scope): array
    {
        if (! $node instanceof FuncCall || ! $node->name instanceof Node\Name) {
            return [];
        }

        $name = strtolower((string) $node->name);
        if (! isset($this->forbidden[$name])) {
            return [];
        }

        return [
            sprintf(
                "Forbidden function call detected: %s(). Use Laravel-safe alternative.",
                $name
            ),
        ];
    }
}

