Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit be5d99c

Browse files
fix(python): Fixes for Python code scanning alerts
1 parent cb04e89 commit be5d99c

File tree

9 files changed

+242
-56
lines changed

9 files changed

+242
-56
lines changed

‎.github/pytools/Sign-File.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function FindSignTool {
1919
if (Test-Path -Path $SignTool -PathType Leaf) {
2020
return $SignTool
2121
}
22-
$sdkVers = "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0"
22+
$sdkVers = "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0","10.0.14393.0","10.0.15063.0","10.0.16299.0","10.0.17134.0","10.0.26100.0"
2323
Foreach ($ver in $sdkVers)
2424
{
2525
$SignTool = "${env:ProgramFiles(x86)}\Windows Kits10円\bin\${ver}\x64\signtool.exe"

‎.github/scripts/get_affected.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -626,11 +626,9 @@ def find_affected_sketches(changed_files: list[str]) -> None:
626626
q = queue.Queue()
627627

628628
if component_mode:
629-
print(f"Affected IDF component examples:", file=sys.stderr)
630629
# Get all available component examples once for efficiency
631630
all_examples = list_idf_component_examples()
632631
else:
633-
print(f"Affected sketches:", file=sys.stderr)
634632
all_examples = []
635633

636634
for file in changed_files:
@@ -648,11 +646,9 @@ def find_affected_sketches(changed_files: list[str]) -> None:
648646
# Check if this file belongs to an IDF component example
649647
for example in all_examples:
650648
if file.startswith(example + "/") and example not in affected_sketches:
651-
print(example, file=sys.stderr)
652649
affected_sketches.append(example)
653650
else:
654651
if file.endswith('.ino') and file not in affected_sketches:
655-
print(file, file=sys.stderr)
656652
affected_sketches.append(file)
657653

658654
# Continue with reverse dependency traversal
@@ -687,18 +683,24 @@ def find_affected_sketches(changed_files: list[str]) -> None:
687683
if should_traverse:
688684
q.put(dependency)
689685
if dependency_example and dependency_example not in affected_sketches:
690-
print(dependency_example, file=sys.stderr)
691686
affected_sketches.append(dependency_example)
692687
else:
693688
q.put(dependency)
694689
if dependency.endswith('.ino') and dependency not in affected_sketches:
695-
print(dependency, file=sys.stderr)
696690
affected_sketches.append(dependency)
697691

698692
if component_mode:
699693
print(f"Total affected IDF component examples: {len(affected_sketches)}", file=sys.stderr)
694+
if affected_sketches:
695+
print("Affected IDF component examples:", file=sys.stderr)
696+
for example in affected_sketches:
697+
print(f" {example}", file=sys.stderr)
700698
else:
701699
print(f"Total affected sketches: {len(affected_sketches)}", file=sys.stderr)
700+
if affected_sketches:
701+
print("Affected sketches:", file=sys.stderr)
702+
for sketch in affected_sketches:
703+
print(f" {sketch}", file=sys.stderr)
702704

703705
def save_dependencies_as_json(output_file: str = "dependencies.json") -> None:
704706
"""

‎.github/scripts/merge_packages.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818

1919
def load_package(filename):
20-
pkg = json.load(open(filename))["packages"][0]
20+
with open(filename) as f:
21+
pkg = json.load(f)["packages"][0]
2122
print("Loaded package {0} from {1}".format(pkg["name"], filename), file=sys.stderr)
2223
print("{0} platform(s), {1} tools".format(len(pkg["platforms"]), len(pkg["tools"])), file=sys.stderr)
2324
return pkg

‎libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
const char *ssid = "..........";
2121
const char *password = "..........";
22+
uint32_t last_ota_time = 0;
2223

2324
void setup() {
2425
Serial.begin(115200);
@@ -40,9 +41,13 @@ void setup() {
4041
// No authentication by default
4142
// ArduinoOTA.setPassword("admin");
4243

43-
// Password can be set with it's md5 value as well
44-
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
45-
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
44+
// Password can be set with plain text (will be hashed internally)
45+
// The authentication uses PBKDF2-HMAC-SHA256 with 10,000 iterations
46+
// ArduinoOTA.setPassword("admin");
47+
48+
// Or set password with pre-hashed value (SHA256 hash of "admin")
49+
// SHA256(admin) = 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
50+
// ArduinoOTA.setPasswordHash("8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918");
4651

4752
ArduinoOTA
4853
.onStart([]() {
@@ -60,7 +65,10 @@ void setup() {
6065
Serial.println("\nEnd");
6166
})
6267
.onProgress([](unsigned int progress, unsigned int total) {
63-
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
68+
if (millis() - last_ota_time > 500) {
69+
Serial.printf("Progress: %u%%\n", (progress / (total / 100)));
70+
last_ota_time = millis();
71+
}
6472
})
6573
.onError([](ota_error_t error) {
6674
Serial.printf("Error[%u]: ", error);

‎libraries/ArduinoOTA/src/ArduinoOTA.cpp

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
#include "ArduinoOTA.h"
2020
#include "NetworkClient.h"
2121
#include "ESPmDNS.h"
22-
#include "MD5Builder.h"
22+
#include "SHA2Builder.h"
23+
#include "PBKDF2_HMACBuilder.h"
2324
#include "Update.h"
2425

2526
// #define OTA_DEBUG Serial
@@ -72,18 +73,20 @@ String ArduinoOTAClass::getHostname() {
7273

7374
ArduinoOTAClass &ArduinoOTAClass::setPassword(const char *password) {
7475
if (_state == OTA_IDLE && password) {
75-
MD5Builder passmd5;
76-
passmd5.begin();
77-
passmd5.add(password);
78-
passmd5.calculate();
76+
// Hash the password with SHA256 for storage (not plain text)
77+
SHA256Builder pass_hash;
78+
pass_hash.begin();
79+
pass_hash.add(password);
80+
pass_hash.calculate();
7981
_password.clear();
80-
_password = passmd5.toString();
82+
_password = pass_hash.toString();
8183
}
8284
return *this;
8385
}
8486

8587
ArduinoOTAClass &ArduinoOTAClass::setPasswordHash(const char *password) {
8688
if (_state == OTA_IDLE && password) {
89+
// Store the pre-hashed password directly
8790
_password.clear();
8891
_password = password;
8992
}
@@ -188,17 +191,18 @@ void ArduinoOTAClass::_onRx() {
188191
_udp_ota.read();
189192
_md5 = readStringUntil('\n');
190193
_md5.trim();
191-
if (_md5.length() != 32) {
194+
if (_md5.length() != 32) {// MD5 produces 32 character hex string for firmware integrity
192195
log_e("bad md5 length");
193196
return;
194197
}
195198

196199
if (_password.length()) {
197-
MD5Builder nonce_md5;
198-
nonce_md5.begin();
199-
nonce_md5.add(String(micros()));
200-
nonce_md5.calculate();
201-
_nonce = nonce_md5.toString();
200+
// Generate a random challenge (nonce)
201+
SHA256Builder nonce_sha256;
202+
nonce_sha256.begin();
203+
nonce_sha256.add(String(micros()) + String(random(1000000)));
204+
nonce_sha256.calculate();
205+
_nonce = nonce_sha256.toString();
202206

203207
_udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort());
204208
_udp_ota.printf("AUTH %s", _nonce.c_str());
@@ -222,20 +226,37 @@ void ArduinoOTAClass::_onRx() {
222226
_udp_ota.read();
223227
String cnonce = readStringUntil(' ');
224228
String response = readStringUntil('\n');
225-
if (cnonce.length() != 32 || response.length() != 32) {
229+
if (cnonce.length() != 64 || response.length() != 64) {// SHA256 produces 64 character hex string
226230
log_e("auth param fail");
227231
_state = OTA_IDLE;
228232
return;
229233
}
230234

231-
String challenge = _password + ":" + String(_nonce) + ":" + cnonce;
232-
MD5Builder _challengemd5;
233-
_challengemd5.begin();
234-
_challengemd5.add(challenge);
235-
_challengemd5.calculate();
236-
String result = _challengemd5.toString();
237-
238-
if (result.equals(response)) {
235+
// Verify the challenge/response using PBKDF2-HMAC-SHA256
236+
// The client should derive a key using PBKDF2-HMAC-SHA256 with:
237+
// - password: the OTA password (or its hash if using setPasswordHash)
238+
// - salt: nonce + cnonce
239+
// - iterations: 10000 (or configurable)
240+
// Then hash the challenge with the derived key
241+
242+
String salt = _nonce + ":" + cnonce;
243+
SHA256Builder sha256;
244+
// Use the stored password hash for PBKDF2 derivation
245+
PBKDF2_HMACBuilder pbkdf2(&sha256, _password, salt, 10000);
246+
247+
pbkdf2.begin();
248+
pbkdf2.calculate();
249+
String derived_key = pbkdf2.toString();
250+
251+
// Create challenge: derived_key + nonce + cnonce
252+
String challenge = derived_key + ":" + _nonce + ":" + cnonce;
253+
SHA256Builder challenge_sha256;
254+
challenge_sha256.begin();
255+
challenge_sha256.add(challenge);
256+
challenge_sha256.calculate();
257+
String expected_response = challenge_sha256.toString();
258+
259+
if (expected_response.equals(response)) {
239260
_udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort());
240261
_udp_ota.print("OK");
241262
_udp_ota.endPacket();
@@ -266,7 +287,8 @@ void ArduinoOTAClass::_runUpdate() {
266287
_state = OTA_IDLE;
267288
return;
268289
}
269-
Update.setMD5(_md5.c_str());
290+
291+
Update.setMD5(_md5.c_str()); // Note: Update library still uses MD5 for firmware integrity, this is separate from authentication
270292

271293
if (_start_callback) {
272294
_start_callback();

‎libraries/ArduinoOTA/src/ArduinoOTA.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class ArduinoOTAClass {
5454
//Sets the password that will be required for OTA. Default NULL
5555
ArduinoOTAClass &setPassword(const char *password);
5656

57-
//Sets the password as above but in the form MD5(password). Default NULL
57+
//Sets the password as above but in the form SHA256(password). Default NULL
5858
ArduinoOTAClass &setPasswordHash(const char *password);
5959

6060
//Sets the partition label to write to when updating SPIFFS. Default NULL

‎libraries/WiFi/examples/WiFiUDPClient/udp_server.py

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,93 @@
22
# for messages from the ESP32 board and prints them
33
import socket
44
import sys
5+
import subprocess
6+
import platform
7+
8+
def get_interface_ips():
9+
"""Get all available interface IP addresses"""
10+
interface_ips = []
11+
12+
# Try using system commands to get interface IPs
13+
system = platform.system().lower()
14+
15+
try:
16+
if system == "darwin" or system == "linux":
17+
# Use 'ifconfig' on macOS/Linux
18+
result = subprocess.run(['ifconfig'], capture_output=True, text=True, timeout=5)
19+
if result.returncode == 0:
20+
lines = result.stdout.split('\n')
21+
for line in lines:
22+
if 'inet ' in line and '127.0.0.1' not in line:
23+
# Extract IP address from ifconfig output
24+
parts = line.strip().split()
25+
for i, part in enumerate(parts):
26+
if part == 'inet':
27+
if i + 1 < len(parts):
28+
ip = parts[i + 1]
29+
if ip not in interface_ips and ip != '127.0.0.1':
30+
interface_ips.append(ip)
31+
break
32+
elif system == "windows":
33+
# Use 'ipconfig' on Windows
34+
result = subprocess.run(['ipconfig'], capture_output=True, text=True, timeout=5)
35+
if result.returncode == 0:
36+
lines = result.stdout.split('\n')
37+
for line in lines:
38+
if 'IPv4 Address' in line and '127.0.0.1' not in line:
39+
# Extract IP address from ipconfig output
40+
if ':' in line:
41+
ip = line.split(':')[1].strip()
42+
if ip not in interface_ips and ip != '127.0.0.1':
43+
interface_ips.append(ip)
44+
except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError):
45+
print("Error: Failed to get interface IPs using system commands")
46+
print("Trying fallback methods...")
47+
48+
# Fallback: try to get IPs using socket methods
49+
if not interface_ips:
50+
try:
51+
# Get all IP addresses associated with the hostname
52+
hostname = socket.gethostname()
53+
ip_list = socket.gethostbyname_ex(hostname)[2]
54+
for ip in ip_list:
55+
if ip not in interface_ips and ip != '127.0.0.1':
56+
interface_ips.append(ip)
57+
except socket.gaierror:
58+
print("Error: Failed to get interface IPs using sockets")
59+
60+
# Fail if no interfaces found
61+
if not interface_ips:
62+
print("Error: No network interfaces found. Please check your network configuration.")
63+
sys.exit(1)
64+
65+
return interface_ips
66+
67+
def select_interface(interface_ips):
68+
"""Ask user to select which interface to bind to"""
69+
if len(interface_ips) == 1:
70+
print(f"Using interface: {interface_ips[0]}")
71+
return interface_ips[0]
72+
73+
print("Multiple network interfaces detected:")
74+
for i, ip in enumerate(interface_ips, 1):
75+
print(f" {i}. {ip}")
76+
77+
while True:
78+
try:
79+
choice = input(f"Select interface (1-{len(interface_ips)}): ").strip()
80+
choice_idx = int(choice) - 1
81+
if 0 <= choice_idx < len(interface_ips):
82+
selected_ip = interface_ips[choice_idx]
83+
print(f"Selected interface: {selected_ip}")
84+
return selected_ip
85+
else:
86+
print(f"Please enter a number between 1 and {len(interface_ips)}")
87+
except ValueError:
88+
print("Please enter a valid number")
89+
except KeyboardInterrupt:
90+
print("\nExiting...")
91+
sys.exit(1)
592

693
try:
794
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -10,15 +97,17 @@
1097
print("Failed to create socket. Error Code : " + str(msg[0]) + " Message " + msg[1])
1198
sys.exit()
1299

100+
# Get available interfaces and let user choose
101+
interface_ips = get_interface_ips()
102+
selected_ip = select_interface(interface_ips)
103+
13104
try:
14-
s.bind(("", 3333))
105+
s.bind((selected_ip, 3333))
15106
except socket.error as msg:
16107
print("Bind failed. Error: " + str(msg[0]) + ": " + msg[1])
17108
sys.exit()
18109

19-
print("Server listening")
20-
21-
print("Server listening")
110+
print(f"Server listening on {selected_ip}:3333")
22111

23112
while 1:
24113
d = s.recvfrom(1024)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /