Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .buildpacks
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
https://github.com/heroku/heroku-buildpack-nodejs.git#v183
https://github.com/mars/create-react-app-inner-buildpack.git#v9.0.0
https://github.com/heroku/heroku-buildpack-static.git#v5
https://github.com/heroku/heroku-buildpack-nginx.git
36 changes: 36 additions & 0 deletions .github/workflows/staging-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Staging Integration Testing

on:
pull_request:
branches: [rvignesh/fix-nginx-conf]

jobs:
cypress:
if: github.event.label.name == 'test-staging'
runs-on: ubuntu-latest
steps:
- name: Checkout frontend
uses: actions/checkout@v3

- name: Use latest Node.js
uses: actions/setup-node@v3

- name: Install dependencies
run: yarn setup

- name: Setup Cypress
run: |
git clone https://github.com/glific/cypress-testing.git
cd cypress-testing
git checkout test_staging
cd ..
cp -r cypress-testing/cypress cypress
yarn add [email protected]
cp cypress-testing/cypress.config.ts.example cypress.config.ts

- name: Cypress run
env:
CYPRESS_BASE_URL: https://staging.glific.com
CYPRESS_BACKEND_URL: https://api.staging.glific.com/api
run: |
yarn run cypress run --record --key ${{ secrets.CYPRESS_DASHBOARD_KEY }}

Check warning

Code scanning / CodeQL

Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

Copilot Autofix

AI about 1 month ago

In general, the problem is fixed by explicitly defining a permissions: block either at the workflow root (applies to all jobs) or within the individual job to restrict the GITHUB_TOKEN to the minimal scopes required, typically contents: read for workflows that only need to read the repository. This documents the intended access and ensures the workflow remains least‑privilege even if repo/org defaults change later.

For this specific workflow, the cypress job only needs to check out code and run tests. It does not create or modify GitHub resources (no pushes, no issue or PR updates, no releases), so it only requires contents: read. The most targeted fix is to add a permissions: block directly under the cypress job definition, at the same indentation level as if: and runs-on:, with contents: read. This avoids affecting any other jobs that might exist in the file (none are shown, but scoping at job level is still precise) and does not change runtime behavior except for reducing available privileges on the GITHUB_TOKEN.

Concretely, in .github/workflows/staging-testing.yml, insert:

    permissions:
      contents: read

between the if: github.event.label.name == 'test-staging' line and the runs-on: ubuntu-latest line for the cypress job. No imports or additional definitions are needed, as permissions is a native GitHub Actions workflow key.

Suggested changeset 1
.github/workflows/staging-testing.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/staging-testing.yml b/.github/workflows/staging-testing.yml
--- a/.github/workflows/staging-testing.yml
+++ b/.github/workflows/staging-testing.yml
@@ -7,6 +7,8 @@
 jobs:
   cypress:
     if: github.event.label.name == 'test-staging'
+    permissions:
+      contents: read
     runs-on: ubuntu-latest
     steps:
       - name: Checkout frontend
EOF
@@ -7,6 +7,8 @@
jobs:
cypress:
if: github.event.label.name == 'test-staging'
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- name: Checkout frontend
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: bin/start-nginx-static
43 changes: 43 additions & 0 deletions config/nginx.conf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
daemon off;

worker_processes <%= ENV['NGINX_WORKERS'] || 4 %>;

pid /app/nginx.pid;
error_log stderr error;

events {
use epoll;
accept_mutex on;
worker_connections 1024;
}

http {
include mime.types;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $request_time $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /dev/stdout detailed;

server {
listen <%= ENV['PORT'] %>;
root /app/build;
index index.html;

add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Frame-Options "deny" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always;
add_header Content-Security-Policy "default-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; script-src-elem 'self' 'unsafe-inline' https://www.google.com https://www.gstatic.com https://js.stripe.com; frame-src 'self' https://js.stripe.com https://www.canva.com https://www.google.com https://www.gstatic.com data:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; connect-src *;" always;

if ($http_x_forwarded_proto != "https") {
return 301 https://$host$request_uri;
}

location / {
try_files $uri $uri/ /index.html;
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "glific-frontend",
"version": "6.9.0",
"version": "6.9.1",
"private": true,
"type": "module",
"license": {
Expand Down
4 changes: 0 additions & 4 deletions src/containers/Auth/Auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import setLogs from 'config/logs';
import { checkOrgStatus } from 'services/AuthService';
import { TERMS_OF_USE_LINK } from 'common/constants';

import { Promotion } from './Promotion/Promotion';

export interface AuthProps {
pageTitle: string;
buttonText: string;
Expand Down Expand Up @@ -302,8 +300,6 @@ export const Auth = ({
</>
) : null}
</div>

{mode === 'login' && <Promotion />}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { MockedProvider } from '@apollo/client/testing';
import { fireEvent, render } from '@testing-library/react';
import dayjs from 'dayjs';
import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter } from 'react-router';

import { MARK_AS_READ } from 'graphql/mutations/Chat';
import { SHORT_DATE_FORMAT } from 'common/constants';
import { MARK_AS_READ } from 'graphql/mutations/Chat';
import ChatConversation from './ChatConversation';

const mockCallback = vi.fn();
Expand Down Expand Up @@ -79,3 +79,50 @@ test('it should call the callback function on click action', () => {
fireEvent.click(getAllByTestId('list')[0]);
expect(mockCallback).toHaveBeenCalled();
});

test('it should not throw when lastMessage body is null', () => {
const props = {
...defaultProps,
highlightSearch: 'test',
lastMessage: { body: null, insertedAt, type: 'TEXT' },
};
expect(() => render(wrapperContainer(props))).not.toThrow();
});

test('it should not throw when lastMessage body is undefined', () => {
const props = {
...defaultProps,
highlightSearch: 'test',
lastMessage: { body: undefined, insertedAt, type: 'TEXT' },
};
expect(() => render(wrapperContainer(props))).not.toThrow();
});

test('it should truncate message body longer than 35 characters', () => {
const longBody = 'This is a very long message that exceeds the limit';
const props = {
...defaultProps,
lastMessage: { body: longBody, insertedAt, type: 'TEXT' },
};
const { getByTestId } = render(wrapperContainer(props));
expect(getByTestId('content').textContent).toContain('...');
});

test('it should not truncate message body within 35 characters', () => {
const shortBody = 'Short message';
const props = {
...defaultProps,
lastMessage: { body: shortBody, insertedAt, type: 'TEXT' },
};
const { getByTestId } = render(wrapperContainer(props));
expect(getByTestId('content').textContent).not.toContain('...');
});

test('it should replace newlines with spaces in TEXT messages', () => {
const props = {
...defaultProps,
lastMessage: { body: 'Hello\nWorld', insertedAt, type: 'TEXT' },
};
const { getByTestId } = render(wrapperContainer(props));
expect(getByTestId('content').textContent).not.toContain('\n');
});
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { useApolloClient, useMutation } from '@apollo/client';
import { ListItemButton } from '@mui/material';
import { Link, useLocation } from 'react-router';
import dayjs from 'dayjs';
import { useApolloClient, useMutation } from '@apollo/client';
import { Link, useLocation } from 'react-router';

import { COMPACT_MESSAGE_LENGTH, SHORT_DATE_FORMAT } from 'common/constants';
import { MARK_AS_READ } from 'graphql/mutations/Chat';
import { SEARCH_OFFSET } from 'graphql/queries/Search';
import { WhatsAppToJsx } from 'common/RichEditor';
import { MessageType } from '../MessageType/MessageType';
import styles from './ChatConversation.module.css';
import Track from 'services/TrackService';
import { slicedString, updateContactCache } from 'common/utils';
import { AvatarDisplay } from 'components/UI/AvatarDisplay/AvatarDisplay';
import { Timer } from 'components/UI/Timer/Timer';
import { MARK_AS_READ } from 'graphql/mutations/Chat';
import { SEARCH_OFFSET } from 'graphql/queries/Search';
import Track from 'services/TrackService';
import { MessageType } from '../MessageType/MessageType';
import styles from './ChatConversation.module.css';

export interface ChatConversationProps {
entityId: number;
Expand All @@ -24,7 +24,7 @@ export interface ChatConversationProps {
index: number;
lastMessage: {
id: number;
body: string;
body: string | null | undefined;
insertedAt: string;
type: string;
media: any;
Expand Down Expand Up @@ -130,13 +130,11 @@ const ChatConversation = ({
}

const name = slicedString(contactName, 20);

const { type, body } = lastMessage;
const isTextType = type === 'TEXT';
let originalText = body ?? '';
let displayMSG: any = <MessageType type={type} body={originalText} />;

let displayMSG: any = <MessageType type={type} body={body} />;

let originalText = body;
if (isTextType) {
// let's shorten the text message to display correctly
if (originalText.length > COMPACT_MESSAGE_LENGTH) {
Expand Down Expand Up @@ -203,7 +201,7 @@ const ChatConversation = ({
{name}
</div>
<div className={styles.MessageContent} data-testid="content">
{isTextType && highlightSearch ? BoldedText(body, highlightSearch) : displayMSG}
{isTextType && highlightSearch ? BoldedText(originalText, highlightSearch) : displayMSG}
</div>
</div>
<div>
Expand Down
16 changes: 0 additions & 16 deletions static.json

This file was deleted.