Skip to content

Commit

Permalink
Merge branch 'master' into 2115-investigate-potential-ways-of-prevent…
Browse files Browse the repository at this point in the history
…ing-malicious-contracts-from-being-loaded
  • Loading branch information
corrideat committed Jan 18, 2025
2 parents e7eb314 + f981369 commit fa8e3e3
Show file tree
Hide file tree
Showing 21 changed files with 621 additions and 167 deletions.
6 changes: 6 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
blank_issues_enabled: false
contact_links:
- name: Discuss Group Income
url: https://github.com/okTurtles/group-income/discussions
about: Use issues to report bugs or request features. Everything else goes here.

16 changes: 16 additions & 0 deletions .github/ISSUE_TEMPLATE/create-issue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: Create Issue
about: Template for creating an issue, whether a bug report or a feature request.
title: ''
labels: ''
assignees: ''

---

### Problem

_Describe a clear problem here._

### Solution

_Do your best to describe what the solution should look like._
20 changes: 0 additions & 20 deletions ISSUE_TEMPLATE.md

This file was deleted.

2 changes: 1 addition & 1 deletion backend/zkppSalt.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const getZkppSaltRecord = async (contractID: string) => {
!Array.isArray(recordObj) ||
(recordObj.length !== 3 && recordObj.length !== 4) ||
recordObj.slice(0, 3).some((r) => !r || typeof r !== 'string') ||
(recordObj[3] !== null && typeof recordObj[3] !== 'string')
(recordObj[3] != null && typeof recordObj[3] !== 'string')
) {
console.error('Error validating encrypted JSON object ' + recordId)
return null
Expand Down
18 changes: 17 additions & 1 deletion frontend/controller/serviceworkers/sw-primary.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,23 @@ sbp('sbp/selectors/register', {

return computedGetters
}
})()
})(),
// For compatibility for old contracts.
// TODO: This is to be deleted in a later release, once contracts are recreated.
'state/vuex/commit': (key, data) => {
switch (key) {
case 'deleteChatRoomScrollPosition':
case 'setChatRoomScrollPosition':
return sbp('okTurtles.events/emit', NEW_CHATROOM_UNREAD_POSITION, data)
// These are handled by events
case 'removeNotification':
return
case 'setCurrentChatRoomId':
return
}

throw new Error(`Invalid selector 'state/vuex/commit': Not allowed in SW. (key: ${key})`)
}
})

const ourLocation = new URL(self.location)
Expand Down
2 changes: 1 addition & 1 deletion frontend/model/contracts/shared/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const PROFILE_STATUS = {
}
export const GROUP_NAME_MAX_CHAR = 50 // https://github.com/okTurtles/group-income/issues/2196
export const GROUP_DESCRIPTION_MAX_CHAR = 500
export const GROUP_PAYMENT_METHOD_MAX_CHAR = 250
export const GROUP_PAYMENT_METHOD_MAX_CHAR = 1024
export const GROUP_NON_MONETARY_CONTRIBUTION_MAX_CHAR = 150
export const GROUP_CURRENCY_MAX_CHAR = 10
export const GROUP_MAX_PLEDGE_AMOUNT = 1000000000
Expand Down
51 changes: 34 additions & 17 deletions frontend/model/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,43 @@ const localforage = {
reject(new Error('Unsupported characters in name: -'))
return
}
const request = self.indexedDB.open(name + '--' + storeName)

// Create the object store if it doesn't exist
request.onupgradeneeded = (event) => {
const db = event.target.result
db.createObjectStore(storeName)
const openDB = (version?: number) => {
// By default `version` is the latest DB version. Initially, we
// try to open that, but in some cases (e.g., when manually
// deleting the DBs), the schema will be wrong and miss the object
// store. In these cases, we need to upgrade the DB by
// incrementing the version number to re-create the schema, which
// can only be done when the DB is being 'upgraded'.
const request = self.indexedDB.open(name + '--' + storeName, version)

// Create the object store if it doesn't exist
request.onupgradeneeded = (event) => {
const db = event.target.result
db.createObjectStore(storeName)
}

request.onsuccess = (event) => {
const db = event.target.result
if (!db.objectStoreNames.contains(storeName)) {
return openDB(db.version + 1)
}

resolve(db)
}

request.onerror = (error) => {
reject(error)
}

// If this happens, closing all tabs and stopping the SW could
// help.
request.onblocked = (event) => {
reject(new Error('DB is blocked'))
}
}

request.onsuccess = (event) => {
const db = event.target.result
resolve(db)
}

request.onerror = (error) => {
reject(error)
}

request.onblocked = (event) => {
reject(new Error('DB is blocked'))
}
openDB()
})
}
return promise
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const periodicNotificationEntries: {
const comparison = comparePeriodStamps(nextPeriod, now)

return (
rootGetters.ourGroupProfileForGroup(rootState[gId]).incomeDetailsType === 'pledgeAmount' &&
rootGetters.ourGroupProfileForGroup(rootState[gId])?.incomeDetailsType === 'pledgeAmount' &&
(comparison > 0 && comparison < DAYS_MILLIS * 7) &&
(rootGetters.ourPaymentsForGroup(rootState[gId])?.todo.length > 0) &&
!myNotificationHas(item => item.type === 'NEAR_DISTRIBUTION_END' && item.data.period === currentPeriod, gId)
Expand Down Expand Up @@ -84,7 +84,7 @@ const periodicNotificationEntries: {

this.nextDistributionPeriod = groupIds.map((gId) => {
const profile = rootGetters.ourGroupProfileForGroup(rootState[gId])
if (!profile.incomeDetailsType) {
if (!profile?.incomeDetailsType) {
// if income-details are not set yet, ignore.
return null
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/views/components/LinkToCopy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ component.c-wrapper(
tooltip.c-feedback(
v-if='ephemeral.isTooltipActive'
:isVisible='true'
:anchorToElement='true'
direction='top'
:text='L("Copied to clipboard!")'
)
Expand Down Expand Up @@ -100,7 +101,7 @@ export default ({
}
.c-feedback {
position: absolute;
position: absolute !important;
left: 50%;
transform: translateX(-50%);
}
Expand Down
7 changes: 5 additions & 2 deletions frontend/views/components/ProfileCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,15 @@ export default ({
)
},
toggleTooltip () {
this.$refs.tooltip.toggle()
this.$refs.tooltip?.toggle()
},
async sendMessage () {
const chatRoomID = this.ourGroupDirectMessageFromUserIds(this.contractID)
if (!chatRoomID) {
await this.createDirectMessage(this.contractID)
const freshChatRoomID = await this.createDirectMessage(this.contractID)
if (freshChatRoomID) {
this.redirect(freshChatRoomID)
}
} else {
if (!this.ourGroupDirectMessages[chatRoomID].visible) {
this.setDMVisibility(chatRoomID, true)
Expand Down
13 changes: 9 additions & 4 deletions frontend/views/components/Tooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,9 @@ export default ({
switch (this.direction) {
case 'right':
x = '100%'
x = `${spacing}px`
y = '-50%'
absPosition = { top: '50%', right: `-${spacing}px` }
absPosition = { top: '50%', left: '100%' }
break
case 'left':
x = '-100%'
Expand All @@ -177,9 +177,9 @@ export default ({
absPosition = { bottom: `-${spacing}px` }
break
case 'bottom-right':
x = 0
x = '-100%'
y = '100%'
absPosition = { bottom: `-${spacing}px`, right: 0 }
absPosition = { bottom: `-${spacing}px`, left: '100%' }
break
case 'top':
x = '-50%'
Expand Down Expand Up @@ -324,6 +324,7 @@ export default ({
if (this.triggerElementSelector) {
this.triggerDOM.style.cursor = 'pointer'
this.triggerDOM.style.position = 'relative'
}
},
beforeDestory () {
Expand Down Expand Up @@ -388,6 +389,10 @@ export default ({
}
}
.c-anchored-tooltip {
width: max-content;
}
.c-background {
position: absolute;
z-index: $zindex-tooltip - 1;
Expand Down
21 changes: 16 additions & 5 deletions frontend/views/containers/chatroom/ChatMain.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ import Vue from 'vue'
import Avatar from '@components/Avatar.vue'
import InfiniteLoading from 'vue-infinite-loading'
import Message from './Message.vue'
import MessageInteractive from './MessageInteractive.vue'
import MessageInteractive, { interactiveMessage } from './MessageInteractive.vue'
import MessageNotification from './MessageNotification.vue'
import MessagePoll from './MessagePoll.vue'
import ConversationGreetings from '@containers/chatroom/ConversationGreetings.vue'
Expand All @@ -137,7 +137,7 @@ import ViewArea from './ViewArea.vue'
import Emoticons from './Emoticons.vue'
import TouchLinkHelper from './TouchLinkHelper.vue'
import DragActiveOverlay from './file-attachment/DragActiveOverlay.vue'
import { MESSAGE_TYPES, MESSAGE_VARIANTS, CHATROOM_ACTIONS_PER_PAGE } from '@model/contracts/shared/constants.js'
import { MESSAGE_TYPES, MESSAGE_VARIANTS, CHATROOM_ACTIONS_PER_PAGE, CHATROOM_MEMBER_MENTION_SPECIAL_CHAR } from '@model/contracts/shared/constants.js'
import { CHATROOM_EVENTS, NEW_CHATROOM_UNREAD_POSITION, DELETE_ATTACHMENT_FEEDBACK } from '@utils/events.js'
import { findMessageIdx } from '@model/contracts/shared/functions.js'
import { proximityDate, MINS_MILLIS } from '@model/contracts/shared/time.js'
Expand Down Expand Up @@ -616,9 +616,20 @@ export default ({
this.handleSendMessage(message.text, message.attachments, message.replyingMessage)
},
replyMessage (message) {
const { text, hash } = message
this.ephemeral.replyingMessage = { text, hash }
this.ephemeral.replyingTo = this.who(message)
const { text, hash, type } = message
if (type === MESSAGE_TYPES.INTERACTIVE) {
const proposal = message.proposal
this.ephemeral.replyingMessage = {
text: interactiveMessage(proposal, { from: `${CHATROOM_MEMBER_MENTION_SPECIAL_CHAR}${proposal.creatorID}` }),
hash
}
this.ephemeral.replyingTo = L('Proposal notification')
} else {
this.ephemeral.replyingMessage = { text, hash }
this.ephemeral.replyingTo = this.who(message)
}
},
editMessage (message, newMessage) {
message.text = newMessage
Expand Down
7 changes: 6 additions & 1 deletion frontend/views/containers/chatroom/DMMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const DMMixin: Object = {
try {
const identityContractID = this.ourIdentityContractId
const currentGroupId = this.currentGroupId
let dmChatRoomId

await sbp('gi.actions/identity/createDirectMessage', {
contractID: identityContractID,
data: { currentGroupId, memberIDs },
Expand All @@ -33,10 +35,13 @@ const DMMixin: Object = {
// state ('pending') and we'll set the chatroom ID when the
// contract is loaded.
// This is done in the JOINED_CHATROOM event.
sbp('state/vuex/commit', 'setPendingChatRoomId', { chatRoomID: message.contractID(), groupID: currentGroupId })
dmChatRoomId = message.contractID()
sbp('state/vuex/commit', 'setPendingChatRoomId', { chatRoomID: dmChatRoomId, groupID: currentGroupId })
}
}
})

return dmChatRoomId
} catch (err) {
console.error('[DMMixin.js] Failed to create a new chatroom', err)
await sbp('gi.ui/prompt', {
Expand Down
9 changes: 6 additions & 3 deletions frontend/views/containers/chatroom/MessageActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ menu-parent.c-message-menu(ref='menu')
i.icon-pencil-alt

tooltip(
v-if='isText'
v-if='isReplyable'
direction='top'
:text='L("Reply")'
)
Expand Down Expand Up @@ -107,6 +107,9 @@ export default ({
isPoll () {
return this.type === MESSAGE_TYPES.POLL
},
isReplyable () {
return [MESSAGE_TYPES.TEXT, MESSAGE_TYPES.INTERACTIVE].includes(this.type)
},
isPinnable () {
return this.isText || this.isPoll
},
Expand All @@ -130,9 +133,9 @@ export default ({
conditionToShow: !this.isDesktopScreen && this.isEditable
}, {
name: L('Reply'),
action: 'Reply',
action: 'reply',
icon: 'reply',
conditionToShow: !this.isDesktopScreen && this.isText
conditionToShow: !this.isDesktopScreen && this.isReplyable
}, {
name: L('Retry'),
action: 'retry',
Expand Down
7 changes: 5 additions & 2 deletions frontend/views/containers/chatroom/MessageInteractive.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template lang='pug'>
message-base(v-bind='$props' @wrapperAction='action')
message-base(v-bind='$props'
@wrapperAction='action'
@reply='$emit("reply")'
)
template(#image='')
.c-icon(:class='{"is-warning": isYellowHorn}')
svg-yellow-horn(v-if='isYellowHorn')
Expand Down Expand Up @@ -41,7 +44,7 @@ import SvgYellowHorn from '@svgs/yellow-horn.svg'
import { humanDate } from '@model/contracts/shared/time.js'
import { get } from '@model/contracts/shared/giLodash.js'
const interactiveMessage = (proposal, baseOptions = {}) => {
export const interactiveMessage = (proposal, baseOptions = {}) => {
const { status, creatorID, proposalType, proposalData } = proposal
const { options: proposalDetails } = getProposalDetails({
status,
Expand Down
7 changes: 4 additions & 3 deletions frontend/views/containers/chatroom/SendArea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

.c-reply-wrapper
.c-reply(v-if='replyingMessage')
i18n(:args='{ replyingTo, text: replyingMessage.text }') Replying to {replyingTo}: "{text}"
i18n(:args='{ replyingTo, text: swapMentionIDForDisplayname(replyingMessage.text) }') Replying to {replyingTo}: "{text}"
button.c-clear.is-icon-small(
:aria-label='L("Stop replying")'
@click='stopReplying'
Expand Down Expand Up @@ -272,7 +272,7 @@ import CreatePoll from './CreatePoll.vue'
import Avatar from '@components/Avatar.vue'
import Tooltip from '@components/Tooltip.vue'
import ChatAttachmentPreview from './file-attachment/ChatAttachmentPreview.vue'
import { makeMentionFromUsername, makeChannelMention } from '@model/chatroom/utils.js'
import { makeMentionFromUsername, makeChannelMention, swapMentionIDForDisplayname } from '@model/chatroom/utils.js'
import {
CHATROOM_PRIVACY_LEVEL,
CHATROOM_MEMBER_MENTION_SPECIAL_CHAR,
Expand Down Expand Up @@ -927,7 +927,8 @@ export default ({
}).catch(e => {
console.error('Error emitting user stopped typing event', e)
})
}
},
swapMentionIDForDisplayname
}
}: Object)
</script>
Expand Down
Loading

0 comments on commit fa8e3e3

Please sign in to comment.