From c9b608ca605315f161d7cd82b3a0f7930ad53916 Mon Sep 17 00:00:00 2001 From: "BULL\\jpmug" Date: Wed, 7 Oct 2026 05:35:02 -0400 Subject: [PATCH 01/11] ASN1_Time_Decode fix forfour digit years not being handled properly. Fixes https://github.com/TaurusTLS-Developers/TaurusTLS/issues/321 --- Source/TaurusTLS_Utils.pas | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Source/TaurusTLS_Utils.pas b/Source/TaurusTLS_Utils.pas index df42c1b2..0b621aeb 100644 --- a/Source/TaurusTLS_Utils.pas +++ b/Source/TaurusTLS_Utils.pas @@ -602,16 +602,28 @@ function ASN1_Time_Decode(const a: PASN1_TIME; out year, month, day, hour, min, Exit; end; // Convert time from string to number - year := IndyStrToInt(Copy(time_str, 1, 2)) + 1900; - month := IndyStrToInt(Copy(time_str, 3, 2)); - day := IndyStrToInt(Copy(time_str, 5, 2)); - hour := IndyStrToInt(Copy(time_str, 7, 2)); - min := IndyStrToInt(Copy(time_str, 9, 2)); - sec := IndyStrToInt(Copy(time_str, 11, 2)); - // Fix year. This function is Y2k but isn't compatible with Y2k5 :-( {Do not Localize} - if year < 1950 then + if Length(time+str) >= 14 then begin - Inc(year, 100); + year := IndyStrToInt(Copy(time_str, 1, 4)); + month := IndyStrToInt(Copy(time_str, 5, 2)); + day := IndyStrToInt(Copy(time_str, 7, 2)); + hour := IndyStrToInt(Copy(time_str, 9, 2)); + min := IndyStrToInt(Copy(time_str, 11, 2)); + sec := IndyStrToInt(Copy(time_str, 13, 2)); + end + else + begin + year := IndyStrToInt(Copy(time_str, 1, 2)) + 1900; + month := IndyStrToInt(Copy(time_str, 3, 2)); + day := IndyStrToInt(Copy(time_str, 5, 2)); + hour := IndyStrToInt(Copy(time_str, 7, 2)); + min := IndyStrToInt(Copy(time_str, 9, 2)); + sec := IndyStrToInt(Copy(time_str, 11, 2)); + // Fix year. This function is Y2k but isn't compatible with Y2k5 :-( {Do not Localize} + if year < 1950 then + begin + Inc(year, 100); + end; end; // Check TZ tz_hour := 0; From 2d018e7269c97f4aed1c583398bd2aa9cbdf4916 Mon Sep 17 00:00:00 2001 From: "BULL\\jpmug" Date: Wed, 7 Oct 2026 14:17:12 -0400 Subject: [PATCH 02/11] Use ASN1_Time_Decode and do not parse tz. It should be UTC in any case.. --- Source/TaurusTLS_Utils.pas | 111 ++++--------------------------------- 1 file changed, 12 insertions(+), 99 deletions(-) diff --git a/Source/TaurusTLS_Utils.pas b/Source/TaurusTLS_Utils.pas index 0b621aeb..ce0c42e0 100644 --- a/Source/TaurusTLS_Utils.pas +++ b/Source/TaurusTLS_Utils.pas @@ -145,17 +145,11 @@ function MDAsString(const AMD: TTaurusTLSLEVP_MD): String; {$IFDEF USE_INLINE} /// /// Returns the second of the minute. /// -/// -/// Returns the Time Zone offset hour -/// -/// -/// Returns the Time Zone offset minute. -/// /// /// True if parsing was successful or False if failed. /// function ASN1_Time_Decode(const a: PASN1_TIME; out year, month, day, hour, min, - sec: Word; out tz_hour, tz_min: Integer): Boolean; + sec: Word): Boolean; /// /// Converts a ASN1_TIME to a TDateTime time stamp. @@ -556,96 +550,19 @@ function ASN1_OBJECT_ToStr(a: PASN1_OBJECT): String; end; function ASN1_Time_Decode(const a: PASN1_TIME; out year, month, day, hour, min, - sec: Word; out tz_hour, tz_min: Integer): Boolean; -var - i, tz_dir: Integer; -{$IFDEF FPC} - time_str: AnsiString; -{$ELSE} - time_str: string; -{$ENDIF} -{$IFNDEF USE_MARSHALLED_PTRS} -{$IFNDEF STRING_IS_ANSI} - LTemp: AnsiString; -{$ENDIF} -{$ENDIF} + sec: Word): Boolean; +var ltm : PIdC_TM; begin - year := 0; - month := 1; - day := 1; - hour := 0; - min := 0; - sec := 0; - tz_hour := 0; - tz_min := 0; - Result := False; { default is to return with an error indication } - if ASN1_STRING_length(PASN1_STRING(a)) < 12 then //PALOFF - Possible bad typecast - begin - Exit; - end; -{$IFDEF USE_MARSHALLED_PTRS} - time_str := TMarshal.ReadStringAsAnsi(TPtrWrapper.Create(ASN1_STRING_get0_data(PASN1_STRING(a))), - ASN1_STRING_length(PASN1_STRING(a))); -{$ELSE} -{$IFDEF STRING_IS_ANSI} - SetString(time_str, PIdAnsiChar(ASN1_STRING_get0_data(PASN1_STRING(a))), ASN1_STRING_length(PASN1_STRING(a))); -{$ELSE} - SetString(LTemp, PIdAnsiChar(ASN1_STRING_get0_data(PASN1_STRING(a))), ASN1_STRING_length(PASN1_STRING(a))); //PALOFF - Possible bad pointer usage [data : PByte cast to PAnsiChar] - { Note: UTCtime is a type defined by OpenSSL and hence is ansistring and not UCS-2 } - // TODO: do we need to use SetCodePage() here? - time_str := String(LTemp); // explicit convert to Unicode -{$ENDIF} -{$ENDIF} - // Check if first 12 chars are numbers - if not IsNumeric(time_str, 12) then - begin - Exit; - end; - // Convert time from string to number - if Length(time+str) >= 14 then + Result := ASN1_TIME_to_tm(a,ltm) = 1; + if Result then begin - year := IndyStrToInt(Copy(time_str, 1, 4)); - month := IndyStrToInt(Copy(time_str, 5, 2)); - day := IndyStrToInt(Copy(time_str, 7, 2)); - hour := IndyStrToInt(Copy(time_str, 9, 2)); - min := IndyStrToInt(Copy(time_str, 11, 2)); - sec := IndyStrToInt(Copy(time_str, 13, 2)); - end - else - begin - year := IndyStrToInt(Copy(time_str, 1, 2)) + 1900; - month := IndyStrToInt(Copy(time_str, 3, 2)); - day := IndyStrToInt(Copy(time_str, 5, 2)); - hour := IndyStrToInt(Copy(time_str, 7, 2)); - min := IndyStrToInt(Copy(time_str, 9, 2)); - sec := IndyStrToInt(Copy(time_str, 11, 2)); - // Fix year. This function is Y2k but isn't compatible with Y2k5 :-( {Do not Localize} - if year < 1950 then - begin - Inc(year, 100); - end; - end; - // Check TZ - tz_hour := 0; - tz_min := 0; - if CharIsInSet(time_str, 13, '-+') then - begin { Do not Localize } - tz_dir := iif(CharEquals(time_str, 13, '-'), -1, 1); { Do not Localize } - for i := 14 to 18 do - begin // Check if numbers are numbers - if i = 16 then - begin - Continue; - end; - if not IsNumeric(time_str[i]) then - begin - Exit; - end; - end; - tz_hour := IndyStrToInt(Copy(time_str, 14, 15)) * tz_dir; - tz_min := IndyStrToInt(Copy(time_str, 17, 18)) * tz_dir; + Year := ltm.tm_year + 1900; + Month := ltm.tm_mon; + day := ltm.tm_mday; + hour := ltm.tm_hour; + min := ltm.tm_min; + sec := ltm.tm_sec; end; - Result := True; { everthing OK } end; function ASN1TimeToDateTime(a: PASN1_TIME): TDateTime; @@ -656,15 +573,11 @@ function ASN1TimeToDateTime(a: PASN1_TIME): TDateTime; hour: Word; min: Word; sec: Word; - tz_h: Integer; - tz_m: Integer; begin Result := 0; - if ASN1_Time_Decode(a, year, month, day, hour, min, sec, tz_h, tz_m) then + if ASN1_Time_Decode(a, year, month, day, hour, min, sec) then begin Result := EncodeDate(year, month, day) + EncodeTime(hour, min, sec, 0); - Result := Result + (tz_m / (60 * 24)); - Result := Result + (tz_h / 24.0); Result := UTCTimeToLocalTime(Result); end; end; From bbce4eda2f6e6bfc9dd0cd58b6b0d7d7698994ed Mon Sep 17 00:00:00 2001 From: "@AT" Date: Wed, 7 Oct 2026 11:50:58 -0700 Subject: [PATCH 03/11] Fix: Avoid laep second and invalid time failure Uses TryEncodeDateTime and clamps leap seconds to prevent exception crashes. --- Source/TaurusTLS_Utils.pas | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/Source/TaurusTLS_Utils.pas b/Source/TaurusTLS_Utils.pas index ce0c42e0..0c9a687d 100644 --- a/Source/TaurusTLS_Utils.pas +++ b/Source/TaurusTLS_Utils.pas @@ -254,6 +254,7 @@ implementation {$IFDEF WINDOWS} IdIDN, {$ENDIF} + DateUtils, TaurusTLS_ResourceStrings, TaurusTLSHeaders_bio, TaurusTLSHeaders_objects, TaurusTLSHeaders_x509, TaurusTLSHeaders_x509_vfy, SysUtils; @@ -567,19 +568,31 @@ function ASN1_Time_Decode(const a: PASN1_TIME; out year, month, day, hour, min, function ASN1TimeToDateTime(a: PASN1_TIME): TDateTime; var - year: Word; - month: Word; - day: Word; - hour: Word; - min: Word; - sec: Word; + lTm: TIdC_TM; + lUtcDT: TDateTime; + lSec: Word; + begin Result := 0; - if ASN1_Time_Decode(a, year, month, day, hour, min, sec) then + if not Assigned(a) then + Exit; + + {$IFDEF DCC}{$WARN UNSAFE_CODE OFF}{$ENDIF} + if ASN1_TIME_to_tm(a, @lTm) = 1 then begin - Result := EncodeDate(year, month, day) + EncodeTime(hour, min, sec, 0); - Result := UTCTimeToLocalTime(Result); + // Handle leap seconds safely (clamp sec = 60 to 59) + lSec := lTm.tm_sec; + if lSec > 59 then + lSec := 59; + + // Safely encode UTC DateTime without throwing exceptions on edge cases + if TryEncodeDateTime(lTm.tm_year + 1900, lTm.tm_mon + 1, lTm.tm_mday, + lTm.tm_hour, lTm.tm_min, lSec, 0, lUtcDT) then + begin + Result := UTCTimeToLocalTime(lUtcDT); + end; end; + {$IFDEF DCC}{$WARN UNSAFE_CODE DEFAULT}{$ENDIF} end; function DirName(const ADirName: PX509_NAME): String; From 06c4c893f0db9e3e4a8f31c8e6ca018fe88c4e16 Mon Sep 17 00:00:00 2001 From: "@AT" Date: Wed, 7 Oct 2026 11:56:12 -0700 Subject: [PATCH 04/11] Restore function `ASN1_Time_Decode` API contract The function `ASN1_Time_Decode` API contract was changed accedentaly. Even the function is used by one consumer in the `TaurusTLS`, it defined in the interface section and can be used by customer code. We MUST keep its declaration as is. The `ASN1_Time_Decode` is reimplemented as a zero-allocation raw buffer parser. Correctly handles UTCTime, GeneralizedTime, fractional seconds, and signed timezone offsets while preserving local wall-clock values for public API users. --- Source/TaurusTLS_Utils.pas | 163 ++++++++++++++++++++++++++++++++++--- 1 file changed, 152 insertions(+), 11 deletions(-) diff --git a/Source/TaurusTLS_Utils.pas b/Source/TaurusTLS_Utils.pas index 0c9a687d..503f7279 100644 --- a/Source/TaurusTLS_Utils.pas +++ b/Source/TaurusTLS_Utils.pas @@ -149,7 +149,7 @@ function MDAsString(const AMD: TTaurusTLSLEVP_MD): String; {$IFDEF USE_INLINE} /// True if parsing was successful or False if failed. /// function ASN1_Time_Decode(const a: PASN1_TIME; out year, month, day, hour, min, - sec: Word): Boolean; + sec: Word; out tz_hour, tz_min: Integer): Boolean; /// /// Converts a ASN1_TIME to a TDateTime time stamp. @@ -550,20 +550,161 @@ function ASN1_OBJECT_ToStr(a: PASN1_OBJECT): String; end; end; -function ASN1_Time_Decode(const a: PASN1_TIME; out year, month, day, hour, min, - sec: Word): Boolean; -var ltm : PIdC_TM; +// Inline helper function for `ASN1_Time_Decode` +function CharArrayToWord(AChars: PIdAnsiChar; AStart, AEnd: TIdC_INT; + out AResult: Word): Boolean; {$IFDEF USE_INLINE}inline; {$ENDIF} +var + i: TIdC_INT; + lChar: AnsiChar; + lResult: Word; begin - Result := ASN1_TIME_to_tm(a,ltm) = 1; + Result := Assigned(AChars) and (AStart >= 0) and (AEnd >= AStart); if Result then begin - Year := ltm.tm_year + 1900; - Month := ltm.tm_mon; - day := ltm.tm_mday; - hour := ltm.tm_hour; - min := ltm.tm_min; - sec := ltm.tm_sec; + lResult := 0; + for i := AStart to AEnd do + begin + lChar := AChars[i]; + if lChar in ['0'..'9'] then + lResult := (lResult * 10) + (Byte(lChar) - Byte('0')) + else + Exit(False); + end; + AResult := lResult; + end; +end; + +// Note: ASN1TimeToDateTime was previously the sole consumer of this function. +// While ASN1TimeToDateTime has been refactored to use native OpenSSL routines +// and ASN1_Time_Decode is no longer used internally within TaurusTLS, this function +// remains part of the public API contract and must be preserved for compatibility. +// +// The legacy implementation relied on multiple temporary string allocations and +// lacked adequate input validation for malformed time strings, making it inefficient +// and prone to unexpected failures. +// +// The refactored version below provides a highly optimized, low-footprint parser +// that extracts raw ASN.1 time components into local time values safely. +function ASN1_Time_Decode(const a: PASN1_TIME; out year, month, day, hour, min, + sec: Word; out tz_hour, tz_min: Integer): Boolean; +var + lStr: PASN1_STRING; + lTime: PIdAnsiChar; + lLen: TIdC_INT; + lIdx: TIdC_INT; + lIsGeneralized: Boolean; + lRawYear: Word; + lSignIdx: TIdC_INT; + lPositiveTZ: Boolean; + lTZH, lTZM: Word; + +begin + year := 0; month := 0; day := 0; + hour := 0; min := 0; sec := 0; + tz_hour := 0; tz_min := 0; + + lStr := PASN1_STRING(a); + if not Assigned(lStr) then + Exit(False); + + lLen := ASN1_STRING_length(lStr); + lTime := PIdAnsiChar(ASN1_STRING_get0_data(lStr)); + + if not Assigned(lTime) or (lLen < 11) then + Exit(False); + + // Determine if UTCTime (2-digit year) or GeneralizedTime (4-digit year) + // ASN1_STRING_type(lStr) can be checked, or infer from string length/format. + lIsGeneralized := (ASN1_STRING_type(lStr) = V_ASN1_GENERALIZEDTIME) or + ((lLen >= 15) and (lTime[14] in ['Z', 'z', '+', '-', '.', ','])); + + lIdx := 0; + + // 1. Parse Year + if lIsGeneralized then + begin + if not CharArrayToWord(lTime, lIdx, lIdx + 3, year) then Exit(False); + Inc(lIdx, 4); + end + else + begin + if not CharArrayToWord(lTime, lIdx, lIdx + 1, lRawYear) then Exit(False); + // UTCTime sliding window rule (RFC 5280): YY >= 50 -> 19YY, YY < 50 -> 20YY + if lRawYear >= 50 then + year := 1900 + lRawYear + else + year := 2000 + lRawYear; + Inc(lIdx, 2); end; + + // 2. Parse Month, Day, Hour, Minute + if not CharArrayToWord(lTime, lIdx, lIdx + 1, month) then Exit(False); + if not CharArrayToWord(lTime, lIdx + 2, lIdx + 3, day) then Exit(False); + if not CharArrayToWord(lTime, lIdx + 4, lIdx + 5, hour) then Exit(False); + if not CharArrayToWord(lTime, lIdx + 6, lIdx + 7, min) then Exit(False); + Inc(lIdx, 8); + + // 3. Parse Seconds (optional in some BER structures, standard in DER) + if (lIdx + 1 < lLen) and (lTime[lIdx] in ['0'..'9']) then + begin + if not CharArrayToWord(lTime, lIdx, lIdx + 1, sec) then Exit(False); + Inc(lIdx, 2); + end + else + sec := 0; + + // 4. Skip Fractional Seconds (.sss or ,sss if present in GeneralizedTime) + if (lIdx < lLen) and (lTime[lIdx] in ['.', ',']) then + begin + Inc(lIdx); + while (lIdx < lLen) and (lTime[lIdx] in ['0'..'9']) do + Inc(lIdx); + end; + + // 5. Parse Timezone Portion + if (lIdx >= lLen) or (lTime[lIdx] in ['Z', 'z']) then + begin + // UTC / Zulu time + tz_hour := 0; + tz_min := 0; + Exit(True); + end; + + // Scan for offset sign '+' or '-' + lSignIdx := lIdx; + while (lSignIdx < lLen) and not (lTime[lSignIdx] in ['+', '-']) do + Inc(lSignIdx); + + if lSignIdx >= lLen then + Exit(True); // Implicit local time or no explicit offset provided + + lPositiveTZ := (lTime[lSignIdx] = '+'); + + // Parse TZ Hours (2 digits) + if not CharArrayToWord(lTime, lSignIdx + 1, lSignIdx + 2, lTZH) then + Exit(False); + + // Parse TZ Minutes (2 digits, if available) + if (lSignIdx + 4 < lLen) and (lTime[lSignIdx + 3] in ['0'..'9']) then + begin + if not CharArrayToWord(lTime, lSignIdx + 3, lSignIdx + 4, lTZM) then + Exit(False); + end + else + lTZM := 0; + + if lPositiveTZ then + begin + tz_hour := lTZH; + tz_min := lTZM; + end + else + begin + tz_hour := -Integer(lTZH); + tz_min := -Integer(lTZM); + end; + + Result := True; end; function ASN1TimeToDateTime(a: PASN1_TIME): TDateTime; From b3e4e1e07dbd8214a188812674ca8babb3fbaa9e Mon Sep 17 00:00:00 2001 From: "@AT" <83779241+tregubovav-dev@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:20:14 -0700 Subject: [PATCH 05/11] Add GitHub Actions workflow for Telegram alerts --- .github/workflows/main.yml | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..c5f9ee71 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,52 @@ +name: Telegram Internal Workload Alerts + +on: + pull_request: + types: [opened, closed, review_requested] +# workflow_run: +# workflows: ["CI"] # Adjust to match your main CI workflow name +# types: [completed] + security_advisory: + types: [published] + +jobs: + notify-telegram: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Determine event context for dynamic messaging + - name: Set Event Details + id: event-info + run: | + if [ "${{ github.event_name }}" == "pull_request" ]; then + echo "TITLE=Pull Request ${{ github.event.action }}: ${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT + echo "URL=${{ github.event.pull_request.html_url }}" >> $GITHUB_OUTPUT + echo "AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" == "workflow_run" ]; then + echo "TITLE=CI Build Failed for ${{ github.repository }}" >> $GITHUB_OUTPUT + echo "URL=${{ github.event.workflow_run.html_url }}" >> $GITHUB_OUTPUT + echo "AUTHOR=GitHub Actions" >> $GITHUB_OUTPUT + else + echo "TITLE=Security Advisory Published in ${{ github.repository }}" >> $GITHUB_OUTPUT + echo "URL=https://github.com/${{ github.repository }}/security/advisories" >> $GITHUB_OUTPUT + echo "AUTHOR=GitHub Security" >> $GITHUB_OUTPUT + fi + + - name: Send Telegram Notification + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TLG_CHAT_ID_TAURUSTLS_ALERTS }} + token: ${{ secrets.TLG_BOT_TKN_TAURUSTLSDEV_BOT }} + message_thread_id: ${{ vars.TLG_THRD_ID_TAURUSTLS_ALERTS }} + disable_web_page_preview: true + format: markdown + message: | + 🛠️ *TaurusTLS Internal Workload Alert* + + *Event:* ${{ steps.event-info.outputs.TITLE }} + *Repository:* `TaurusTLS/${{ github.event.repository.name }}` + *Triggered by:* @${{ steps.event-info.outputs.AUTHOR }} + + [🔗 View on GitHub](${{ steps.event-info.outputs.URL }}) From 25e188b068756bd43f191b1be7853d9c326bd383 Mon Sep 17 00:00:00 2001 From: "@AT" <83779241+tregubovav-dev@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:28:35 -0700 Subject: [PATCH 06/11] Update GitHub Actions workflow for notifications Remove security advisory notification and fix conditional syntax. --- .github/workflows/main.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c5f9ee71..eceea016 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,8 +6,6 @@ on: # workflow_run: # workflows: ["CI"] # Adjust to match your main CI workflow name # types: [completed] - security_advisory: - types: [published] jobs: notify-telegram: @@ -24,14 +22,10 @@ jobs: echo "TITLE=Pull Request ${{ github.event.action }}: ${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT echo "URL=${{ github.event.pull_request.html_url }}" >> $GITHUB_OUTPUT echo "AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_OUTPUT - elif [ "${{ github.event_name }}" == "workflow_run" ]; then + else [ "${{ github.event_name }}" == "workflow_run" ]; then echo "TITLE=CI Build Failed for ${{ github.repository }}" >> $GITHUB_OUTPUT echo "URL=${{ github.event.workflow_run.html_url }}" >> $GITHUB_OUTPUT echo "AUTHOR=GitHub Actions" >> $GITHUB_OUTPUT - else - echo "TITLE=Security Advisory Published in ${{ github.repository }}" >> $GITHUB_OUTPUT - echo "URL=https://github.com/${{ github.repository }}/security/advisories" >> $GITHUB_OUTPUT - echo "AUTHOR=GitHub Security" >> $GITHUB_OUTPUT fi - name: Send Telegram Notification From 6e5f73452c04ca3733eb53d069609226d53dcec1 Mon Sep 17 00:00:00 2001 From: "@AT" <83779241+tregubovav-dev@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:36:36 -0700 Subject: [PATCH 07/11] Improve event handling in GitHub Actions workflow Refactor conditional statements to handle different GitHub event types and update output variables accordingly. --- .github/workflows/main.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eceea016..dffc8156 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,12 +22,16 @@ jobs: echo "TITLE=Pull Request ${{ github.event.action }}: ${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT echo "URL=${{ github.event.pull_request.html_url }}" >> $GITHUB_OUTPUT echo "AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_OUTPUT - else [ "${{ github.event_name }}" == "workflow_run" ]; then + elif [ "${{ github.event_name }}" == "workflow_run" ]; then echo "TITLE=CI Build Failed for ${{ github.repository }}" >> $GITHUB_OUTPUT echo "URL=${{ github.event.workflow_run.html_url }}" >> $GITHUB_OUTPUT echo "AUTHOR=GitHub Actions" >> $GITHUB_OUTPUT + else + echo "TITLE=Event Triggered: ${{ github.event_name }}" >> $GITHUB_OUTPUT + echo "URL=https://github.com/${{ github.repository }}" >> $GITHUB_OUTPUT + echo "AUTHOR=${{ github.actor }}" >> $GITHUB_OUTPUT fi - + - name: Send Telegram Notification uses: appleboy/telegram-action@master with: From 89d5e0ff52cfa2b66c58d77527065a5f31bbcd0d Mon Sep 17 00:00:00 2001 From: "@AT" <83779241+tregubovav-dev@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:05:34 -0700 Subject: [PATCH 08/11] Update GitHub Actions workflow for notifications Improve events handling and Telegram message formatting. --- .github/workflows/main.yml | 40 +++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dffc8156..c712a65c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,33 +1,50 @@ -name: Telegram Internal Workload Alerts +name: TaurusTLS GitHub Notifications on: pull_request: types: [opened, closed, review_requested] -# workflow_run: -# workflows: ["CI"] # Adjust to match your main CI workflow name -# types: [completed] + issues: + types: [opened, reopened, closed] + issue_comment: + types: [created] jobs: notify-telegram: runs-on: ubuntu-latest + # Skip job if it's a comment on a PR (GitHub treats PRs as issues for comments) + if: ${{ github.event_name != 'issue_comment' || !github.event.issue.pull_request }} + steps: - - name: Checkout code - uses: actions/checkout@v4 - - # Determine event context for dynamic messaging - name: Set Event Details id: event-info run: | if [ "${{ github.event_name }}" == "pull_request" ]; then - echo "TITLE=Pull Request ${{ github.event.action }}: ${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT + echo "TITLE=Pull Request ${{ github.event.action }}" >> $GITHUB_OUTPUT + echo "SUBJECT=${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT echo "URL=${{ github.event.pull_request.html_url }}" >> $GITHUB_OUTPUT echo "AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_OUTPUT + + elif [ "${{ github.event_name }}" == "issues" ]; then + echo "TITLE=Issue ${{ github.event.action }}" >> $GITHUB_OUTPUT + echo "SUBJECT=${{ github.event.issue.title }}" >> $GITHUB_OUTPUT + echo "URL=${{ github.event.issue.html_url }}" >> $GITHUB_OUTPUT + echo "AUTHOR=${{ github.event.issue.user.login }}" >> $GITHUB_OUTPUT + + elif [ "${{ github.event_name }}" == "issue_comment" ]; then + echo "TITLE=New Comment on Issue #${{ github.event.issue.number }}" >> $GITHUB_OUTPUT + echo "SUBJECT=${{ github.event.issue.title }}" >> $GITHUB_OUTPUT + echo "URL=${{ github.event.comment.html_url }}" >> $GITHUB_OUTPUT + echo "AUTHOR=${{ github.event.comment.user.login }}" >> $GITHUB_OUTPUT + elif [ "${{ github.event_name }}" == "workflow_run" ]; then - echo "TITLE=CI Build Failed for ${{ github.repository }}" >> $GITHUB_OUTPUT + echo "TITLE=CI Build Failed" >> $GITHUB_OUTPUT + echo "SUBJECT=${{ github.repository }}" >> $GITHUB_OUTPUT echo "URL=${{ github.event.workflow_run.html_url }}" >> $GITHUB_OUTPUT echo "AUTHOR=GitHub Actions" >> $GITHUB_OUTPUT + else echo "TITLE=Event Triggered: ${{ github.event_name }}" >> $GITHUB_OUTPUT + echo "SUBJECT=${{ github.repository }}" >> $GITHUB_OUTPUT echo "URL=https://github.com/${{ github.repository }}" >> $GITHUB_OUTPUT echo "AUTHOR=${{ github.actor }}" >> $GITHUB_OUTPUT fi @@ -41,9 +58,10 @@ jobs: disable_web_page_preview: true format: markdown message: | - 🛠️ *TaurusTLS Internal Workload Alert* + 🛠️ *TaurusTLS GitHub Notification* *Event:* ${{ steps.event-info.outputs.TITLE }} + *Subject:* ${{ steps.event-info.outputs.SUBJECT }} *Repository:* `TaurusTLS/${{ github.event.repository.name }}` *Triggered by:* @${{ steps.event-info.outputs.AUTHOR }} From 0cee2ee727761616ff03b28c6ef6532de7d85c17 Mon Sep 17 00:00:00 2001 From: "@AT" <83779241+tregubovav-dev@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:06:18 -0700 Subject: [PATCH 09/11] Update pull_request types to include 'reopened' --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c712a65c..13a0177e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: TaurusTLS GitHub Notifications on: pull_request: - types: [opened, closed, review_requested] + types: [opened, reopened, closed, review_requested] issues: types: [opened, reopened, closed] issue_comment: From 193332329e16589e4d13bb7e8c8b7c1ba0ea3b59 Mon Sep 17 00:00:00 2001 From: "@AT" <83779241+tregubovav-dev@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:12:05 -0700 Subject: [PATCH 10/11] Rename main.yml workflow file to Notifications.yml --- .github/workflows/{main.yml => Notifications.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{main.yml => Notifications.yml} (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/Notifications.yml similarity index 100% rename from .github/workflows/main.yml rename to .github/workflows/Notifications.yml From 981147eb5a4cb2f0267eb7260b76f42711f40615 Mon Sep 17 00:00:00 2001 From: "@AT" <83779241+tregubovav-dev@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:03:32 -0700 Subject: [PATCH 11/11] Restricts PR notifications only on main branch --- .github/workflows/Notifications.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/Notifications.yml b/.github/workflows/Notifications.yml index 13a0177e..bd385304 100644 --- a/.github/workflows/Notifications.yml +++ b/.github/workflows/Notifications.yml @@ -3,6 +3,8 @@ name: TaurusTLS GitHub Notifications on: pull_request: types: [opened, reopened, closed, review_requested] + branches: + - main issues: types: [opened, reopened, closed] issue_comment: