VYPR
Critical severity9.6NVD Advisory· Published May 4, 2026· Updated May 8, 2026

CVE-2026-42087

CVE-2026-42087

Description

OpenC3 COSMOS provides the functionality needed to send commands to and receive data from one or more embedded systems. From version 6.7.0 to before version 7.0.0-rc3, a SQL injection vulnerability exists in the Time-Series Database (TSDB) component of COSMOS. The tsdb_lookup function in the cvt_model.rb file directly places user-supplied input into a SQL query without sanitizing the input. As a result, a user can break out of the initial SQL statement and execute arbitrary SQL commands, including deleting data. This issue has been patched in version 7.0.0-rc3.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openc3RubyGems
>= 6.7.0, < 7.0.0-rc37.0.0-rc3

Affected products

3
  • Openc3/Cosmos3 versions
    cpe:2.3:a:openc3:cosmos:7.0.0:rc1:*:*:open_source:*:*:*+ 2 more
    • cpe:2.3:a:openc3:cosmos:7.0.0:rc1:*:*:open_source:*:*:*
    • cpe:2.3:a:openc3:cosmos:7.0.0:rc2:*:*:open_source:*:*:*
    • cpe:2.3:a:openc3:cosmos:*:*:*:*:open_source:*:*:*range: >=6.7.0,<7.0.0

Patches

1
9ba60c09c883

Use parameterized queries to the TSDB

https://github.com/OpenC3/cosmosJason ThomasMar 2, 2026via ghsa
3 files changed · +16 8
  • openc3/lib/openc3/models/cvt_model.rb+7 3 modified
    @@ -270,10 +270,14 @@ def self.tsdb_lookup(items, start_time:, end_time: nil, scope: $openc3_scope)
               query += "ASOF JOIN #{table_name} as T#{index} "
             end
           end
    +      query_params = []
           if start_time && !end_time
    -        query += "WHERE T0.PACKET_TIMESECONDS < '#{start_time}' LIMIT -1"
    +        query += "WHERE T0.PACKET_TIMESECONDS < $1 LIMIT -1"
    +        query_params << start_time
           elsif start_time && end_time
    -        query += "WHERE T0.PACKET_TIMESECONDS >= '#{start_time}' AND T0.PACKET_TIMESECONDS < '#{end_time}'"
    +        query += "WHERE T0.PACKET_TIMESECONDS >= $1 AND T0.PACKET_TIMESECONDS < $2"
    +        query_params << start_time
    +        query_params << end_time
           end
     
           retry_count = 0
    @@ -291,7 +295,7 @@ def self.tsdb_lookup(items, start_time:, end_time: nil, scope: $openc3_scope)
                 @@conn.type_map_for_results = PG::BasicTypeMapForResults.new @@conn
               end
     
    -          result = @@conn.exec(query)
    +          result = @@conn.exec_params(query, query_params)
               if result.nil? or result.ntuples == 0
                 return {}
               else
    
  • openc3/python/openc3/models/cvt_model.py+7 3 modified
    @@ -347,10 +347,14 @@ def tsdb_lookup(
                 else:
                     query += f"ASOF JOIN {table_name} as T{index} "
     
    +        query_params = []
             if start_time and not end_time:
    -            query += f"WHERE T0.PACKET_TIMESECONDS < '{start_time}' LIMIT -1"
    +            query += "WHERE T0.PACKET_TIMESECONDS < %s LIMIT -1"
    +            query_params.append(start_time)
             elif start_time and end_time:
    -            query += f"WHERE T0.PACKET_TIMESECONDS >= '{start_time}' AND T0.PACKET_TIMESECONDS < '{end_time}'"
    +            query += "WHERE T0.PACKET_TIMESECONDS >= %s AND T0.PACKET_TIMESECONDS < %s"
    +            query_params.append(start_time)
    +            query_params.append(end_time)
     
             retry_count = 0
             while retry_count <= 4:
    @@ -366,7 +370,7 @@ def tsdb_lookup(
                             )
     
                         with cls._conn.cursor(binary=True, row_factory=dict_row) as cursor:
    -                        cursor.execute(query)
    +                        cursor.execute(query, query_params or None)
                             result = cursor.fetchall()
     
                             if not result:
    
  • openc3/python/openc3/utilities/questdb_client.py+2 2 modified
    @@ -613,13 +613,13 @@ def create_table(self, target_name, packet_name, packet, cmd_or_tlm="TLM", retai
     
                             if existing_type is None:
                                 # Column doesn't exist yet — add it
    -                            alter = f'ALTER TABLE "{table_name}" ADD COLUMN {col_name} {desired_sql_type}'
    +                            alter = f'ALTER TABLE "{table_name}" ADD COLUMN "{col_name}" {desired_sql_type}'
                                 cur.execute(alter)
                                 self._log_info(f"QuestDB: Added column: {alter}")
                                 altered = True
                             elif existing_type != desired_canonical:
                                 # Type mismatch — ALTER the column type
    -                            alter = f'ALTER TABLE "{table_name}" ALTER COLUMN {col_name} TYPE {desired_sql_type}'
    +                            alter = f'ALTER TABLE "{table_name}" ALTER COLUMN "{col_name}" TYPE {desired_sql_type}'
                                 cur.execute(alter)
                                 self._log_info(
                                     f"QuestDB: Altered column type: {alter} (was {existing_type}, now {desired_canonical})"
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.