From 05a9762d2d7d3ac5412c4d4321dba6e3515dfd23 Mon Sep 17 00:00:00 2001 From: Dmitry Mamontov Date: Mon, 16 May 2022 10:57:20 +0300 Subject: [PATCH 1/4] Add encryption algorithm --- custom_components/miwifi/__init__.py | 3 ++ custom_components/miwifi/config_flow.py | 35 ++++++++++++++ custom_components/miwifi/const.py | 1 + custom_components/miwifi/enum.py | 8 ++++ custom_components/miwifi/helper.py | 9 +++- custom_components/miwifi/luci.py | 47 ++++++++++++------- custom_components/miwifi/translations/de.json | 6 ++- custom_components/miwifi/translations/en.json | 6 ++- .../miwifi/translations/pt-BR.json | 6 ++- custom_components/miwifi/translations/ru.json | 6 ++- custom_components/miwifi/updater.py | 20 +++++++- tests/setup.py | 5 ++ tests/test_luci.py | 23 +++++++++ 13 files changed, 148 insertions(+), 27 deletions(-) diff --git a/custom_components/miwifi/__init__.py b/custom_components/miwifi/__init__.py index b7ee567..7ea18a4 100644 --- a/custom_components/miwifi/__init__.py +++ b/custom_components/miwifi/__init__.py @@ -17,6 +17,7 @@ from homeassistant.exceptions import PlatformNotReady from .const import ( + CONF_ENCRYPTION_ALGORITHM, CONF_ACTIVITY_DAYS, CONF_IS_FORCE_LOAD, DEFAULT_ACTIVITY_DAYS, @@ -31,6 +32,7 @@ UPDATER, ) from .discovery import async_start_discovery +from .enum import EncryptionAlgorithm from .helper import get_config_value, get_store from .services import SERVICES from .updater import LuciUpdater @@ -59,6 +61,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass, _ip, get_config_value(entry, CONF_PASSWORD), + get_config_value(entry, CONF_ENCRYPTION_ALGORITHM, EncryptionAlgorithm.SHA1), get_config_value(entry, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), get_config_value(entry, CONF_TIMEOUT, DEFAULT_TIMEOUT), get_config_value(entry, CONF_IS_FORCE_LOAD, False), diff --git a/custom_components/miwifi/config_flow.py b/custom_components/miwifi/config_flow.py index 32b5ad9..b9c42bc 100644 --- a/custom_components/miwifi/config_flow.py +++ b/custom_components/miwifi/config_flow.py @@ -23,6 +23,7 @@ CONF_ACTIVITY_DAYS, CONF_IS_FORCE_LOAD, CONF_STAY_ONLINE, + CONF_ENCRYPTION_ALGORITHM, DEFAULT_ACTIVITY_DAYS, DEFAULT_SCAN_INTERVAL, DEFAULT_STAY_ONLINE, @@ -32,6 +33,7 @@ UPDATER, ) from .discovery import async_start_discovery +from .enum import EncryptionAlgorithm from .helper import async_user_documentation_url, async_verify_access, get_config_value _LOGGER = logging.getLogger(__name__) @@ -125,6 +127,15 @@ async def async_step_user( { vol.Required(CONF_IP_ADDRESS): str, vol.Required(CONF_PASSWORD): str, + vol.Required( + CONF_ENCRYPTION_ALGORITHM, + default=EncryptionAlgorithm.SHA1, + ): vol.In( + [ + EncryptionAlgorithm.SHA1, + EncryptionAlgorithm.SHA256, + ] + ), vol.Required( CONF_STAY_ONLINE, default=DEFAULT_STAY_ONLINE ): cv.positive_int, @@ -160,6 +171,7 @@ async def async_step_discovery_confirm( self.hass, user_input[CONF_IP_ADDRESS], user_input[CONF_PASSWORD], + user_input[CONF_ENCRYPTION_ALGORITHM], user_input[CONF_TIMEOUT], ) @@ -202,6 +214,15 @@ async def async_step_discovery_confirm( { vol.Required(CONF_IP_ADDRESS, default=_ip): str, vol.Required(CONF_PASSWORD): str, + vol.Required( + CONF_ENCRYPTION_ALGORITHM, + default=EncryptionAlgorithm.SHA1, + ): vol.In( + [ + EncryptionAlgorithm.SHA1, + EncryptionAlgorithm.SHA256, + ] + ), vol.Required( CONF_STAY_ONLINE, default=DEFAULT_STAY_ONLINE ): cv.positive_int, @@ -243,6 +264,7 @@ async def async_step_init(self, user_input: ConfigType | None = None) -> FlowRes self.hass, user_input[CONF_IP_ADDRESS], user_input[CONF_PASSWORD], + user_input[CONF_ENCRYPTION_ALGORITHM], user_input[CONF_TIMEOUT], ) @@ -279,6 +301,19 @@ def _get_options_schema(self) -> vol.Schema: CONF_PASSWORD, default=get_config_value(self._config_entry, CONF_PASSWORD, ""), ): str, + vol.Required( + CONF_ENCRYPTION_ALGORITHM, + default=get_config_value( + self._config_entry, + CONF_ENCRYPTION_ALGORITHM, + EncryptionAlgorithm.SHA1, + ), + ): vol.In( + [ + EncryptionAlgorithm.SHA1, + EncryptionAlgorithm.SHA256, + ] + ), vol.Required( CONF_STAY_ONLINE, default=get_config_value( diff --git a/custom_components/miwifi/const.py b/custom_components/miwifi/const.py index 5000314..8a21c47 100644 --- a/custom_components/miwifi/const.py +++ b/custom_components/miwifi/const.py @@ -43,6 +43,7 @@ CONF_STAY_ONLINE: Final = "stay_online" CONF_IS_FORCE_LOAD: Final = "is_force_load" CONF_ACTIVITY_DAYS: Final = "activity_days" +CONF_ENCRYPTION_ALGORITHM: Final = "encryption_algorithm" CONF_REQUEST: Final = "request" CONF_RESPONSE: Final = "response" CONF_URI: Final = "uri" diff --git a/custom_components/miwifi/enum.py b/custom_components/miwifi/enum.py index 1a1dcc5..ecced7a 100644 --- a/custom_components/miwifi/enum.py +++ b/custom_components/miwifi/enum.py @@ -3,6 +3,7 @@ from __future__ import annotations from enum import Enum, IntEnum +from homeassistant.backports.enum import StrEnum from .const import ( ATTR_SWITCH_WIFI_2_4, @@ -171,6 +172,13 @@ def __str__(self) -> str: SKIP = 2, "Skip" +class EncryptionAlgorithm(StrEnum): + """EncryptionAlgorithm enum""" + + SHA1 = "sha" + SHA256 = "sha256" + + class Model(str, Enum): """Model enum""" diff --git a/custom_components/miwifi/helper.py b/custom_components/miwifi/helper.py index 69999d3..0637298 100644 --- a/custom_components/miwifi/helper.py +++ b/custom_components/miwifi/helper.py @@ -41,19 +41,26 @@ async def async_verify_access( hass: HomeAssistant, ip: str, # pylint: disable=invalid-name password: str, + encryption: str, timeout: int = DEFAULT_TIMEOUT, ) -> codes: """Verify ip and password. :param hass: HomeAssistant: Home Assistant object :param ip: str: device ip address + :param encryption: str: password encryption :param password: str: device password :param timeout: int: Timeout :return int: last update success """ updater = LuciUpdater( - hass=hass, ip=ip, password=password, timeout=timeout, is_only_login=True + hass=hass, + ip=ip, + password=password, + encryption=encryption, + timeout=timeout, + is_only_login=True, ) await updater.async_request_refresh() diff --git a/custom_components/miwifi/luci.py b/custom_components/miwifi/luci.py index 609656b..8a079a0 100644 --- a/custom_components/miwifi/luci.py +++ b/custom_components/miwifi/luci.py @@ -28,12 +28,13 @@ DIAGNOSTIC_DATE_TIME, DIAGNOSTIC_MESSAGE, ) +from .enum import EncryptionAlgorithm from .exceptions import LuciConnectionError, LuciError, LuciRequestError _LOGGER = logging.getLogger(__name__) -# pylint: disable=too-many-public-methods +# pylint: disable=too-many-public-methods,too-many-arguments class LuciClient: """Luci API Client.""" @@ -41,6 +42,7 @@ class LuciClient: _client: AsyncClient _password: str | None = None + _encryption: str = EncryptionAlgorithm.SHA1 _timeout: int = DEFAULT_TIMEOUT _token: str | None = None @@ -51,6 +53,7 @@ def __init__( client: AsyncClient, ip: str = CLIENT_ADDRESS, # pylint: disable=invalid-name password: str | None = None, + encryption: str = EncryptionAlgorithm.SHA1, timeout: int = DEFAULT_TIMEOUT, ) -> None: """Initialize API client. @@ -58,6 +61,7 @@ def __init__( :param client: AsyncClient: AsyncClient object :param ip: str: device ip address :param password: str: device password + :param encryption: str: password encryption algorithm :param timeout: int: Query execution timeout """ @@ -67,6 +71,7 @@ def __init__( self._client = client self.ip = ip # pylint: disable=invalid-name self._password = password + self._encryption = encryption self._timeout = timeout self._url = CLIENT_URL.format(ip=ip) @@ -83,18 +88,20 @@ async def login(self) -> dict: _nonce: str = self.generate_nonce() _url: str = f"{self._url}/api/{_method}" + _request_data: dict = { + "username": CLIENT_USERNAME, + "logtype": str(CLIENT_LOGIN_TYPE), + "password": self.generate_password_hash(_nonce, str(self._password)), + "nonce": _nonce, + } + try: + self._debug("Start request", _url, json.dumps(_request_data), _method, True) + async with self._client as client: response: Response = await client.post( _url, - data={ - "username": CLIENT_USERNAME, - "logtype": str(CLIENT_LOGIN_TYPE), - "password": self.generate_password_hash( - _nonce, str(self._password) - ), - "nonce": _nonce, - }, + data=_request_data, timeout=self._timeout, ) @@ -379,14 +386,16 @@ async def image(self, hardware: str) -> bytes | None: return None - @staticmethod - def sha1(key: str) -> str: - """Generate sha1 by key. + def sha(self, key: str) -> str: + """Generate sha by key. :param key: str: the key from which to get the hash - :return str: sha1 from key. + :return str: sha from key. """ + if self._encryption == EncryptionAlgorithm.SHA256: + return hashlib.sha256(key.encode()).hexdigest() + return hashlib.sha1(key.encode()).hexdigest() @staticmethod @@ -415,22 +424,28 @@ def generate_password_hash(self, nonce: str, password: str) -> str: :param nonce: str: nonce :param password: str: password - :return str: sha1 from password and nonce. + :return str: sha from password and nonce. """ - return self.sha1(nonce + self.sha1(password + CLIENT_PUBLIC_KEY)) + return self.sha(nonce + self.sha(password + CLIENT_PUBLIC_KEY)) - def _debug(self, message: str, url: str, content: Any, path: str) -> None: + def _debug( + self, message: str, url: str, content: Any, path: str, is_only_log: bool = False + ) -> None: """Debug log :param message: str: Message :param url: str: URL :param content: Any: Content :param path: str: Path + :param is_only_log: bool: Is only log """ _LOGGER.debug("%s (%s): %s", message, url, str(content)) + if is_only_log: + return + _content: dict | str = {} try: diff --git a/custom_components/miwifi/translations/de.json b/custom_components/miwifi/translations/de.json index a4d7ad6..da8d40f 100644 --- a/custom_components/miwifi/translations/de.json +++ b/custom_components/miwifi/translations/de.json @@ -3,7 +3,7 @@ "config": { "error": { "ip_address.not_matched": "Ungültige IP Adresse", - "password.not_matched": "Ungültiges Passwort", + "password.not_matched": "Falsches Passwort oder Verschlüsselungsalgorithmus", "connection": "Fehler bei der Herstellung einer Verbindung", "router.not.supported": "Der Router wird nicht unterstützt. Detaillierte Informationen wurden an das Benachrichtigungszentrum gesendet." }, @@ -14,6 +14,7 @@ "data": { "ip_address": "IP Adresse", "password": "Passwort", + "encryption_algorithm": "Passwort Verschlüsselungsalgorithmus", "stay_online": "Mindestaufenthalt in Sekunden online", "scan_interval": "Scanintervall in Sekunden [PRO]", "timeout": "Timeout von Anfragen in Sekunden [PRO]" @@ -24,7 +25,7 @@ "options": { "error": { "ip_address.not_matched": "Ungültige IP Adresse", - "password.not_matched": "Ungültiges Passwort", + "password.not_matched": "Falsches Passwort oder Verschlüsselungsalgorithmus", "connection": "Fehler bei der Herstellung einer Verbindung", "router.not.supported": "Der Router wird nicht unterstützt. Detaillierte Informationen wurden an das Benachrichtigungszentrum gesendet." }, @@ -35,6 +36,7 @@ "data": { "ip_address": "IP Adresse", "password": "Passwort", + "encryption_algorithm": "Passwort Verschlüsselungsalgorithmus", "stay_online": "Mindestaufenthalt in Sekunden online", "scan_interval": "Scanintervall in Sekunden [PRO]", "activity_days": "Anzahl an Tagen, die nach der letzten Aktivität gewartet werden soll [PRO]", diff --git a/custom_components/miwifi/translations/en.json b/custom_components/miwifi/translations/en.json index 36e91a1..1c66667 100644 --- a/custom_components/miwifi/translations/en.json +++ b/custom_components/miwifi/translations/en.json @@ -3,7 +3,7 @@ "config": { "error": { "ip_address.not_matched": "Invalid IP", - "password.not_matched": "Invalid password", + "password.not_matched": "Wrong password or encryption algorithm", "connection": "Failed to establish connection", "router.not.supported": "The router is not supported. Detailed information has been sent to the notification center." }, @@ -14,6 +14,7 @@ "data": { "ip_address": "IP address", "password": "Password", + "encryption_algorithm": "Password encryption algorithm", "stay_online": "Minimum stay online in seconds", "scan_interval": "Scan interval in seconds [PRO]", "timeout": "Timeout of requests in seconds [PRO]" @@ -24,7 +25,7 @@ "options": { "error": { "ip_address.not_matched": "Invalid IP", - "password.not_matched": "Invalid password", + "password.not_matched": "Wrong password or encryption algorithm", "connection": "Failed to establish connection", "router.not.supported": "The router is not supported. Detailed information has been sent to the notification center." }, @@ -35,6 +36,7 @@ "data": { "ip_address": "IP address", "password": "Password", + "encryption_algorithm": "Password encryption algorithm", "stay_online": "Minimum stay online in seconds", "scan_interval": "Scan interval in seconds [PRO]", "activity_days": "Allowed number of days to wait after the last activity [PRO]", diff --git a/custom_components/miwifi/translations/pt-BR.json b/custom_components/miwifi/translations/pt-BR.json index 2ad826e..ea87db2 100644 --- a/custom_components/miwifi/translations/pt-BR.json +++ b/custom_components/miwifi/translations/pt-BR.json @@ -3,7 +3,7 @@ "config": { "error": { "ip_address.not_matched": "IP inválido", - "password.not_matched": "Senha inválida", + "password.not_matched": "Senha ou algoritmo de criptografia incorreto", "connection": "Falha ao estabelecer conexão", "router.not.supported": "O roteador não é suportado. Informações detalhadas foram enviadas para o centro de notificação." }, @@ -14,6 +14,7 @@ "data": { "ip_address": "Endereço IP", "password": "Senha", + "encryption_algorithm": "Algoritmo de criptografia de senha", "stay_online": "Estadia mínima online em segundos", "scan_interval": "Intervalo de varredura em segundos [PRO]", "timeout": "Tempo limite de solicitações em segundos [PRO]" @@ -27,7 +28,7 @@ }, "error": { "ip_address.not_matched": "IP inválido", - "password.not_matched": "Senha inválida", + "password.not_matched": "Senha ou algoritmo de criptografia incorreto", "connection": "Falha ao estabelecer conexão", "router.not.supported": "O roteador não é suportado. Informações detalhadas foram enviadas para o centro de notificação." }, @@ -38,6 +39,7 @@ "data": { "ip_address": "Endereço IP", "password": "Senha", + "encryption_algorithm": "Algoritmo de criptografia de senha", "stay_online": "Estadia mínima online em segundos", "scan_interval": "Intervalo de varredura em segundos [PRO]", "activity_days": "Número permitido de dias de espera após a última atividade [PRO]", diff --git a/custom_components/miwifi/translations/ru.json b/custom_components/miwifi/translations/ru.json index 008481b..d3d5d76 100644 --- a/custom_components/miwifi/translations/ru.json +++ b/custom_components/miwifi/translations/ru.json @@ -3,7 +3,7 @@ "config": { "error": { "ip_address.not_matched": "Неверный IP", - "password.not_matched": "Неверный пароль", + "password.not_matched": "Неверный пароль или алгоритм шифрования", "connection": "Не удалось установить соединение", "router.not.supported": "Роутер не поддерживается. Подробная информация выслана в центр уведомлений." }, @@ -14,6 +14,7 @@ "data": { "ip_address": "IP адрес", "password": "Пароль", + "encryption_algorithm": "Алгоритм шифрования пароля", "stay_online": "Минимальное пребывание онлайн в секундах", "scan_interval": "Интервал сканирования в секундах [PRO]", "timeout": "Тайм-аут запросов в секундах [PRO]" @@ -24,7 +25,7 @@ "options": { "error": { "ip_address.not_matched": "Неверный IP", - "password.not_matched": "Неверный пароль", + "password.not_matched": "Неверный пароль или алгоритм шифрования", "connection": "Не удалось установить соединение", "router.not.supported": "Роутер не поддерживается. Подробная информация выслана в центр уведомлений." }, @@ -35,6 +36,7 @@ "data": { "ip_address": "IP адрес", "password": "Пароль", + "encryption_algorithm": "Алгоритм шифрования пароля", "stay_online": "Минимальное пребывание онлайн в секундах", "scan_interval": "Интервал сканирования в секундах [PRO]", "activity_days": "Допустимое количество дней ожидания после последней активности [PRO]", diff --git a/custom_components/miwifi/updater.py b/custom_components/miwifi/updater.py index e98dbdf..e8ba8f7 100644 --- a/custom_components/miwifi/updater.py +++ b/custom_components/miwifi/updater.py @@ -85,7 +85,15 @@ SIGNAL_NEW_DEVICE, UPDATER, ) -from .enum import Connection, DeviceAction, IfName, Mode, Model, Wifi +from .enum import ( + Connection, + DeviceAction, + EncryptionAlgorithm, + IfName, + Mode, + Model, + Wifi, +) from .exceptions import LuciConnectionError, LuciError, LuciRequestError from .luci import LuciClient from .self_check import async_self_check @@ -169,6 +177,7 @@ def __init__( hass: HomeAssistant, ip: str, password: str, + encryption: str = EncryptionAlgorithm.SHA1, scan_interval: int = DEFAULT_SCAN_INTERVAL, timeout: int = DEFAULT_TIMEOUT, is_force_load: bool = False, @@ -183,6 +192,7 @@ def __init__( :param hass: HomeAssistant: Home Assistant object :param ip: str: device ip address :param password: str: device password + :param encryption: str: password encryption algorithm :param scan_interval: int: Update interval :param timeout: int: Query execution timeout :param is_force_load: bool: Force boot devices when using repeater and mesh mode @@ -192,7 +202,13 @@ def __init__( :param entry_id: str | None: Entry ID """ - self.luci = LuciClient(get_async_client(hass, False), ip, password, timeout) + self.luci = LuciClient( + get_async_client(hass, False), + ip, + password, + EncryptionAlgorithm(encryption), + timeout, + ) self.ip = ip # pylint: disable=invalid-name self.is_force_load = is_force_load diff --git a/tests/setup.py b/tests/setup.py index d0b75f7..6662f9f 100644 --- a/tests/setup.py +++ b/tests/setup.py @@ -23,6 +23,7 @@ from custom_components.miwifi.const import ( CONF_ACTIVITY_DAYS, + CONF_ENCRYPTION_ALGORITHM, CONF_IS_FORCE_LOAD, DEFAULT_SCAN_INTERVAL, DEFAULT_TIMEOUT, @@ -31,6 +32,7 @@ SIGNAL_NEW_DEVICE, UPDATER, ) +from custom_components.miwifi.enum import EncryptionAlgorithm from custom_components.miwifi.helper import get_config_value, get_store from custom_components.miwifi.updater import LuciUpdater @@ -80,6 +82,9 @@ async def async_setup( hass, _ip, get_config_value(config_entry, CONF_PASSWORD), + get_config_value( + config_entry, CONF_ENCRYPTION_ALGORITHM, EncryptionAlgorithm.SHA1 + ), get_config_value(config_entry, CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), get_config_value(config_entry, CONF_TIMEOUT, DEFAULT_TIMEOUT), get_config_value(config_entry, CONF_IS_FORCE_LOAD, is_force), diff --git a/tests/test_luci.py b/tests/test_luci.py index 68929ce..e5f1434 100644 --- a/tests/test_luci.py +++ b/tests/test_luci.py @@ -15,6 +15,7 @@ from pytest_homeassistant_custom_component.common import get_fixture_path, load_fixture from pytest_httpx import HTTPXMock +from custom_components.miwifi.enum import EncryptionAlgorithm from custom_components.miwifi.exceptions import ( LuciConnectionError, LuciError, @@ -154,6 +155,28 @@ async def test_get(hass: HomeAssistant, httpx_mock: HTTPXMock) -> None: assert request.method == "GET" +async def test_get_sha256(hass: HomeAssistant, httpx_mock: HTTPXMock) -> None: + """get test""" + + httpx_mock.add_response(text=load_fixture("login_data.json"), method="POST") + httpx_mock.add_response(text='{"code": 0}', method="GET") + + client: LuciClient = LuciClient( + get_async_client(hass, False), + f"{MOCK_IP_ADDRESS}/", + "test", + EncryptionAlgorithm.SHA256, + ) + + await client.login() + assert await client.get("misystem/miwifi") == {"code": 0} + + request: Request | None = httpx_mock.get_request(method="GET") + assert request is not None + assert request.url == get_url("misystem/miwifi") + assert request.method == "GET" + + async def test_get_without_stok(hass: HomeAssistant, httpx_mock: HTTPXMock) -> None: """get test""" From 819c6fb017de53d799cbe4504e53d92885118448 Mon Sep 17 00:00:00 2001 From: Dmitry Mamontov Date: Mon, 16 May 2022 10:58:41 +0300 Subject: [PATCH 2/4] Add encryption algorithm --- custom_components/miwifi/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/miwifi/manifest.json b/custom_components/miwifi/manifest.json index fd815a8..e61725f 100644 --- a/custom_components/miwifi/manifest.json +++ b/custom_components/miwifi/manifest.json @@ -1,7 +1,7 @@ { "domain": "miwifi", "name": "MiWiFi", - "version": "2.7.0", + "version": "2.7.1", "documentation": "https://github.com/dmamontov/hass-miwifi/blob/main/README.md", "issue_tracker": "https://github.com/dmamontov/hass-miwifi/issues", "config_flow": true, From f9865b29cebb991e5a4eea274150a3f34a3ad2b6 Mon Sep 17 00:00:00 2001 From: Dmitry Mamontov Date: Mon, 16 May 2022 11:08:25 +0300 Subject: [PATCH 3/4] Add encryption algorithm --- custom_components/miwifi/enum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/miwifi/enum.py b/custom_components/miwifi/enum.py index ecced7a..23b9709 100644 --- a/custom_components/miwifi/enum.py +++ b/custom_components/miwifi/enum.py @@ -175,7 +175,7 @@ def __str__(self) -> str: class EncryptionAlgorithm(StrEnum): """EncryptionAlgorithm enum""" - SHA1 = "sha" + SHA1 = "sha1" SHA256 = "sha256" From 12274338da5679040070353305c8fcc20ecb8c27 Mon Sep 17 00:00:00 2001 From: Dmitry Mamontov Date: Mon, 16 May 2022 11:26:35 +0300 Subject: [PATCH 4/4] Update codeql --- .github/workflows/checks.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index a5b82ac..8c36b92 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -30,11 +30,11 @@ jobs: language: [ 'python' ] steps: - uses: actions/checkout@v2 - - uses: github/codeql-action/init@v1 + - uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - - uses: github/codeql-action/autobuild@v1 - - uses: github/codeql-action/analyze@v1 + - uses: github/codeql-action/autobuild@v2 + - uses: github/codeql-action/analyze@v2 hassfest: runs-on: "ubuntu-latest"